peer_digest.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 /* DEBUG: section 72 Peer Digest Routines */
10 
11 #include "squid.h"
12 #if USE_CACHE_DIGESTS
13 #include "CacheDigest.h"
14 #include "CachePeer.h"
15 #include "event.h"
16 #include "FwdState.h"
17 #include "globals.h"
18 #include "HttpReply.h"
19 #include "HttpRequest.h"
20 #include "internal.h"
21 #include "MemObject.h"
22 #include "mime_header.h"
23 #include "neighbors.h"
24 #include "PeerDigest.h"
25 #include "SquidTime.h"
26 #include "Store.h"
27 #include "store_key_md5.h"
28 #include "StoreClient.h"
29 #include "tools.h"
30 #include "util.h"
31 
32 /* local types */
33 
34 /* local prototypes */
35 static time_t peerDigestIncDelay(const PeerDigest * pd);
36 static time_t peerDigestNewDelay(const StoreEntry * e);
37 static void peerDigestSetCheck(PeerDigest * pd, time_t delay);
39 static void peerDigestRequest(PeerDigest * pd);
41 static int peerDigestFetchReply(void *, char *, ssize_t);
42 int peerDigestSwapInHeaders(void *, char *, ssize_t);
43 int peerDigestSwapInCBlock(void *, char *, ssize_t);
44 int peerDigestSwapInMask(void *, char *, ssize_t);
45 static int peerDigestFetchedEnough(DigestFetchState * fetch, char *buf, ssize_t size, const char *step_name);
46 static void peerDigestFetchStop(DigestFetchState * fetch, char *buf, const char *reason);
47 static void peerDigestFetchAbort(DigestFetchState * fetch, char *buf, const char *reason);
48 static void peerDigestReqFinish(DigestFetchState * fetch, char *buf, int, int, int, const char *reason, int err);
49 static void peerDigestPDFinish(DigestFetchState * fetch, int pcb_valid, int err);
50 static void peerDigestFetchFinish(DigestFetchState * fetch, int err);
51 static void peerDigestFetchSetStats(DigestFetchState * fetch);
52 static int peerDigestSetCBlock(PeerDigest * pd, const char *buf);
53 static int peerDigestUseful(const PeerDigest * pd);
54 
55 /* local constants */
56 Version const CacheDigestVer = { 5, 3 };
57 
58 #define StoreDigestCBlockSize sizeof(StoreDigestCBlock)
59 
60 /* min interval for requesting digests from a given peer */
61 static const time_t PeerDigestReqMinGap = 5 * 60; /* seconds */
62 /* min interval for requesting digests (cumulative request stream) */
63 static const time_t GlobDigestReqMinGap = 1 * 60; /* seconds */
64 
65 /* local vars */
66 
67 static time_t pd_last_req_time = 0; /* last call to Check */
68 
70 {
71  assert(p);
72 
73  /*
74  * DPW 2007-04-12
75  * Lock on to the peer here. The corresponding cbdataReferenceDone()
76  * is in peerDigestDestroy().
77  */
78  peer = cbdataReference(p);
79  /* if peer disappears, we will know it's name */
80  host = p->host;
81 
82  times.initialized = squid_curtime;
83 }
84 
86 
88 
90  pd(cbdataReference(aPd)),
91  entry(NULL),
92  old_entry(NULL),
93  sc(NULL),
94  old_sc(NULL),
95  request(req),
96  offset(0),
97  mask_offset(0),
98  start_time(squid_curtime),
99  resp_time(0),
100  expires(0),
101  bufofs(0),
102  state(DIGEST_READ_REPLY)
103 {
105 
106  sent.msg = 0;
107  sent.bytes = 0;
108 
109  recv.msg = 0;
110  recv.bytes = 0;
111 
112  *buf = 0;
113 }
114 
116 {
117  /* unlock everything */
118  storeUnregister(sc, entry, this);
119 
120  entry->unlock("DigestFetchState destructed");
121  entry = NULL;
122 
124 
125  assert(pd == NULL);
126 }
127 
128 /* allocate new peer digest, call Init, and lock everything */
129 void
131 {
132  assert(p);
133 
134  PeerDigest *pd = new PeerDigest(p);
135 
136  // TODO: make CachePeer member a CbcPointer
137  p->digest = cbdataReference(pd);
138 
139  // lock a reference to pd again to prevent the PeerDigest
140  // disappearing during peerDigestDestroy() when
141  // cbdataReferenceValidDone is called.
142  // TODO test if it can be moved into peerDigestDestroy() or
143  // if things can break earlier (eg CachePeer death).
144  (void)cbdataReference(pd);
145 }
146 
147 /* call Clean and free/unlock everything */
148 static void
150 {
151  void *p;
152  assert(pd);
153  void * peerTmp = pd->peer;
154 
155  /*
156  * DPW 2007-04-12
157  * We locked the peer in PeerDigest constructor, this is
158  * where we unlock it.
159  */
160  if (cbdataReferenceValidDone(peerTmp, &p)) {
161  // we locked the p->digest in peerDigestCreate()
162  // this is where we unlock that
163  cbdataReferenceDone(static_cast<CachePeer *>(p)->digest);
164  }
165 
166  delete pd;
167 }
168 
170 {
171  delete cd;
172  // req_result pointer is not owned by us
173 }
174 
175 /* called by peer to indicate that somebody actually needs this digest */
176 void
178 {
179  assert(pd);
180  assert(!pd->flags.needed);
181  assert(!pd->cd);
182 
183  pd->flags.needed = true;
184  pd->times.needed = squid_curtime;
185  peerDigestSetCheck(pd, 0); /* check asap */
186 }
187 
188 /* currently we do not have a reason to disable without destroying */
189 #if FUTURE_CODE
190 /* disables peer for good */
191 static void
192 peerDigestDisable(PeerDigest * pd)
193 {
194  debugs(72, 2, "peerDigestDisable: peer " << pd->host.buf() << " disabled for good");
196  pd->times.next_check = -1; /* never */
197  pd->flags.usable = 0;
198 
199  delete pd->cd
200  pd->cd = nullptr;
201 
202  /* we do not destroy the pd itself to preserve its "history" and stats */
203 }
204 
205 #endif
206 
207 /* increment retry delay [after an unsuccessful attempt] */
208 static time_t
210 {
211  assert(pd);
212  return pd->times.retry_delay > 0 ?
213  2 * pd->times.retry_delay : /* exponential backoff */
214  PeerDigestReqMinGap; /* minimal delay */
215 }
216 
217 /* artificially increases Expires: setting to avoid race conditions
218  * returns the delay till that [increased] expiration time */
219 static time_t
221 {
222  assert(e);
223 
224  if (e->expires > 0)
226 
227  return PeerDigestReqMinGap;
228 }
229 
230 /* registers next digest verification */
231 static void
232 peerDigestSetCheck(PeerDigest * pd, time_t delay)
233 {
234  eventAdd("peerDigestCheck", peerDigestCheck, pd, (double) delay, 1);
235  pd->times.next_check = squid_curtime + delay;
236  debugs(72, 3, "peerDigestSetCheck: will check peer " << pd->host << " in " << delay << " secs");
237 }
238 
239 /*
240  * called when peer is about to disappear or have already disappeared
241  */
242 void
244 {
245  if (pd->flags.requested) {
246  debugs(72, 2, "peerDigest: peer " << pd->host << " gone, will destroy after fetch.");
247  /* do nothing now, the fetching chain will notice and take action */
248  } else {
249  debugs(72, 2, "peerDigest: peer " << pd->host << " is gone, destroying now.");
250  peerDigestDestroy(pd);
251  }
252 }
253 
254 /* callback for eventAdd() (with peer digest locked)
255  * request new digest if our copy is too old or if we lack one;
256  * schedule next check otherwise */
257 static void
259 {
260  PeerDigest *pd = (PeerDigest *)data;
261  time_t req_time;
262 
263  assert(!pd->flags.requested);
264 
265  pd->times.next_check = 0; /* unknown */
266 
267  if (!cbdataReferenceValid(pd->peer)) {
269  return;
270  }
271 
272  debugs(72, 3, "peerDigestCheck: peer " << pd->peer->host << ":" << pd->peer->http_port);
273  debugs(72, 3, "peerDigestCheck: time: " << squid_curtime <<
274  ", last received: " << (long int) pd->times.received << " (" <<
275  std::showpos << (int) (squid_curtime - pd->times.received) << ")");
276 
277  /* decide when we should send the request:
278  * request now unless too close to other requests */
279  req_time = squid_curtime;
280 
281  /* per-peer limit */
282 
283  if (req_time - pd->times.received < PeerDigestReqMinGap) {
284  debugs(72, 2, "peerDigestCheck: " << pd->host <<
285  ", avoiding close peer requests (" <<
286  (int) (req_time - pd->times.received) << " < " <<
287  (int) PeerDigestReqMinGap << " secs).");
288 
289  req_time = pd->times.received + PeerDigestReqMinGap;
290  }
291 
292  /* global limit */
293  if (req_time - pd_last_req_time < GlobDigestReqMinGap) {
294  debugs(72, 2, "peerDigestCheck: " << pd->host <<
295  ", avoiding close requests (" <<
296  (int) (req_time - pd_last_req_time) << " < " <<
297  (int) GlobDigestReqMinGap << " secs).");
298 
300  }
301 
302  if (req_time <= squid_curtime)
303  peerDigestRequest(pd); /* will set pd->flags.requested */
304  else
305  peerDigestSetCheck(pd, req_time - squid_curtime);
306 }
307 
308 /* ask store for a digest */
309 static void
311 {
312  CachePeer *p = pd->peer;
313  StoreEntry *e, *old_e;
314  char *url = NULL;
315  HttpRequest *req;
316  StoreIOBuffer tempBuffer;
317 
318  pd->req_result = NULL;
319  pd->flags.requested = true;
320 
321  /* compute future request components */
322 
323  if (p->digest_url)
324  url = xstrdup(p->digest_url);
325  else
326  url = xstrdup(internalRemoteUri(p->secure.encryptTransport, p->host, p->http_port, "/squid-internal-periodic/", SBuf(StoreDigestFileName)));
327  debugs(72, 2, url);
328 
330  req = HttpRequest::FromUrlXXX(url, mx);
331 
332  assert(req);
333 
334  /* add custom headers */
335  assert(!req->header.len);
336 
338 
339  req->header.putStr(Http::HdrType::ACCEPT, "text/html");
340 
341  if (p->login &&
342  p->login[0] != '*' &&
343  strcmp(p->login, "PASS") != 0 &&
344  strcmp(p->login, "PASSTHRU") != 0 &&
345  strncmp(p->login, "NEGOTIATE",9) != 0 &&
346  strcmp(p->login, "PROXYPASS") != 0) {
347  req->url.userInfo(SBuf(p->login)); // XXX: performance regression make peer login SBuf as well.
348  }
349  /* create fetch state structure */
350  DigestFetchState *fetch = new DigestFetchState(pd, req);
351 
352  /* update timestamps */
355  req->flags.cachable = true;
356 
357  /* the rest is based on clientReplyContext::processExpired() */
358  req->flags.refresh = true;
359 
360  old_e = fetch->old_entry = storeGetPublicByRequest(req);
361 
362  // XXX: Missing a hittingRequiresCollapsing() && startCollapsingOn() check.
363  if (old_e) {
364  debugs(72, 5, "found old " << *old_e);
365 
366  old_e->lock("peerDigestRequest");
367  old_e->ensureMemObject(url, url, req->method);
368 
369  fetch->old_sc = storeClientListAdd(old_e, fetch);
370  }
371 
372  e = fetch->entry = storeCreateEntry(url, url, req->flags, req->method);
373  debugs(72, 5, "created " << *e);
375  fetch->sc = storeClientListAdd(e, fetch);
376  /* set lastmod to trigger IMS request if possible */
377 
378  if (old_e)
379  e->lastModified(old_e->lastModified());
380 
381  /* push towards peer cache */
383 
384  tempBuffer.offset = 0;
385 
386  tempBuffer.length = SM_PAGE_SIZE;
387 
388  tempBuffer.data = fetch->buf;
389 
390  storeClientCopy(fetch->sc, e, tempBuffer,
391  peerDigestHandleReply, fetch);
392 
393  safe_free(url);
394 }
395 
396 /* Handle the data copying .. */
397 
398 /*
399  * This routine handles the copy data and then redirects the
400  * copy to a bunch of subfunctions depending upon the copy state.
401  * It also tracks the buffer offset and "seen", since I'm actually
402  * not interested in rewriting everything to suit my little idea.
403  */
404 static void
406 {
408  int retsize = -1;
409  digest_read_state_t prevstate;
410  int newsize;
411 
412  assert(fetch->pd && receivedData.data);
413  /* The existing code assumes that the received pointer is
414  * where we asked the data to be put
415  */
416  assert(fetch->buf + fetch->bufofs == receivedData.data);
417 
418  /* Update the buffer size */
419  fetch->bufofs += receivedData.length;
420 
421  assert(fetch->bufofs <= SM_PAGE_SIZE);
422 
423  /* If we've fetched enough, return */
424 
425  if (peerDigestFetchedEnough(fetch, fetch->buf, fetch->bufofs, "peerDigestHandleReply"))
426  return;
427 
428  /* Call the right function based on the state */
429  /* (Those functions will update the state if needed) */
430 
431  /* Give us a temporary reference. Some of the calls we make may
432  * try to destroy the fetch structure, and we like to know if they
433  * do
434  */
435  CbcPointer<DigestFetchState> tmpLock = fetch;
436 
437  /* Repeat this loop until we're out of data OR the state changes */
438  /* (So keep going if the state has changed and we still have data */
439  do {
440  prevstate = fetch->state;
441 
442  switch (fetch->state) {
443 
444  case DIGEST_READ_REPLY:
445  retsize = peerDigestFetchReply(fetch, fetch->buf, fetch->bufofs);
446  break;
447 
448  case DIGEST_READ_HEADERS:
449  retsize = peerDigestSwapInHeaders(fetch, fetch->buf, fetch->bufofs);
450  break;
451 
452  case DIGEST_READ_CBLOCK:
453  retsize = peerDigestSwapInCBlock(fetch, fetch->buf, fetch->bufofs);
454  break;
455 
456  case DIGEST_READ_MASK:
457  retsize = peerDigestSwapInMask(fetch, fetch->buf, fetch->bufofs);
458  break;
459 
460  case DIGEST_READ_NONE:
461  break;
462 
463  case DIGEST_READ_DONE:
464  return;
465  break;
466 
467  default:
468  fatal("Bad digest transfer mode!\n");
469  }
470 
471  if (retsize < 0)
472  return;
473 
474  /*
475  * The returned size indicates how much of the buffer was read -
476  * so move the remainder of the buffer to the beginning
477  * and update the bufofs / bufsize
478  */
479  newsize = fetch->bufofs - retsize;
480 
481  memmove(fetch->buf, fetch->buf + retsize, fetch->bufofs - newsize);
482 
483  fetch->bufofs = newsize;
484 
485  } while (cbdataReferenceValid(fetch) && prevstate != fetch->state && fetch->bufofs > 0);
486 
487  // Check for EOF here, thus giving the parser one extra run. We could avoid this overhead by
488  // checking at the beginning of this function. However, in this case, we would have to require
489  // that the parser does not regard EOF as a special condition (it is true now but may change
490  // in the future).
491  if (!receivedData.length) { // EOF
492  peerDigestFetchAbort(fetch, fetch->buf, "premature end of digest reply");
493  return;
494  }
495 
496  /* Update the copy offset */
497  fetch->offset += receivedData.length;
498 
499  /* Schedule another copy */
500  if (cbdataReferenceValid(fetch)) {
501  StoreIOBuffer tempBuffer;
502  tempBuffer.offset = fetch->offset;
503  tempBuffer.length = SM_PAGE_SIZE - fetch->bufofs;
504  tempBuffer.data = fetch->buf + fetch->bufofs;
505  storeClientCopy(fetch->sc, fetch->entry, tempBuffer,
506  peerDigestHandleReply, fetch);
507  }
508 }
509 
510 /* wait for full http headers to be received then parse them */
511 /*
512  * This routine handles parsing the reply line.
513  * If the reply line indicates an OK, the same data is thrown
514  * to SwapInHeaders(). If the reply line is a NOT_MODIFIED,
515  * we simply stop parsing.
516  */
517 static int
518 peerDigestFetchReply(void *data, char *buf, ssize_t size)
519 {
521  PeerDigest *pd = fetch->pd;
522  size_t hdr_size;
523  assert(pd && buf);
524  assert(!fetch->offset);
525 
526  assert(fetch->state == DIGEST_READ_REPLY);
527 
528  if (peerDigestFetchedEnough(fetch, buf, size, "peerDigestFetchReply"))
529  return -1;
530 
531  if ((hdr_size = headersEnd(buf, size))) {
532  const auto &reply = fetch->entry->mem().freshestReply();
533  const auto status = reply.sline.status();
534  assert(status != Http::scNone);
535  debugs(72, 3, "peerDigestFetchReply: " << pd->host << " status: " << status <<
536  ", expires: " << (long int) reply.expires << " (" << std::showpos <<
537  (int) (reply.expires - squid_curtime) << ")");
538 
539  /* this "if" is based on clientHandleIMSReply() */
540 
541  if (status == Http::scNotModified) {
542  /* our old entry is fine */
543  assert(fetch->old_entry);
544 
545  if (!fetch->old_entry->mem_obj->request)
546  fetch->old_entry->mem_obj->request = fetch->entry->mem_obj->request;
547 
548  assert(fetch->old_entry->mem_obj->request);
549 
550  Store::Root().updateOnNotModified(fetch->old_entry, *fetch->entry);
551 
552  /* get rid of 304 reply */
553  storeUnregister(fetch->sc, fetch->entry, fetch);
554 
555  fetch->entry->unlock("peerDigestFetchReply 304");
556 
557  fetch->entry = fetch->old_entry;
558 
559  fetch->old_entry = NULL;
560 
561  /* preserve request -- we need its size to update counters */
562  /* requestUnlink(r); */
563  /* fetch->entry->mem_obj->request = NULL; */
564  } else if (status == Http::scOkay) {
565  /* get rid of old entry if any */
566 
567  if (fetch->old_entry) {
568  debugs(72, 3, "peerDigestFetchReply: got new digest, releasing old one");
569  storeUnregister(fetch->old_sc, fetch->old_entry, fetch);
570  fetch->old_entry->releaseRequest();
571  fetch->old_entry->unlock("peerDigestFetchReply 200");
572  fetch->old_entry = NULL;
573  }
574  } else {
575  /* some kind of a bug */
576  peerDigestFetchAbort(fetch, buf, reply.sline.reason());
577  return -1; /* XXX -1 will abort stuff in ReadReply! */
578  }
579 
580  /* must have a ready-to-use store entry if we got here */
581  /* can we stay with the old in-memory digest? */
582  if (status == Http::scNotModified && fetch->pd->cd) {
583  peerDigestFetchStop(fetch, buf, "Not modified");
584  fetch->state = DIGEST_READ_DONE;
585  } else {
586  fetch->state = DIGEST_READ_HEADERS;
587  }
588  } else {
589  /* need more data, do we have space? */
590 
591  if (size >= SM_PAGE_SIZE)
592  peerDigestFetchAbort(fetch, buf, "reply header too big");
593  }
594 
595  /* We don't want to actually ack that we've handled anything,
596  * otherwise SwapInHeaders() won't get the reply line .. */
597  return 0;
598 }
599 
600 /* fetch headers from disk, pass on to SwapInCBlock */
601 int
602 peerDigestSwapInHeaders(void *data, char *buf, ssize_t size)
603 {
605  size_t hdr_size;
606 
607  assert(fetch->state == DIGEST_READ_HEADERS);
608 
609  if (peerDigestFetchedEnough(fetch, buf, size, "peerDigestSwapInHeaders"))
610  return -1;
611 
612  assert(!fetch->offset);
613 
614  if ((hdr_size = headersEnd(buf, size))) {
615  const auto &reply = fetch->entry->mem().freshestReply();
616  const auto status = reply.sline.status();
617  assert(status != Http::scNone);
618 
619  if (status != Http::scOkay) {
620  debugs(72, DBG_IMPORTANT, "peerDigestSwapInHeaders: " << fetch->pd->host <<
621  " status " << status << " got cached!");
622 
623  peerDigestFetchAbort(fetch, buf, "internal status error");
624  return -1;
625  }
626 
627  fetch->state = DIGEST_READ_CBLOCK;
628  return hdr_size; /* Say how much data we read */
629  }
630 
631  /* need more data, do we have space? */
632  if (size >= SM_PAGE_SIZE) {
633  peerDigestFetchAbort(fetch, buf, "stored header too big");
634  return -1;
635  }
636 
637  return 0; /* We need to read more to parse .. */
638 }
639 
640 int
641 peerDigestSwapInCBlock(void *data, char *buf, ssize_t size)
642 {
644 
645  assert(fetch->state == DIGEST_READ_CBLOCK);
646 
647  if (peerDigestFetchedEnough(fetch, buf, size, "peerDigestSwapInCBlock"))
648  return -1;
649 
650  if (size >= (ssize_t)StoreDigestCBlockSize) {
651  PeerDigest *pd = fetch->pd;
652 
653  assert(pd);
654  assert(fetch->entry->mem_obj);
655 
656  if (peerDigestSetCBlock(pd, buf)) {
657  /* XXX: soon we will have variable header size */
658  /* switch to CD buffer and fetch digest guts */
659  buf = NULL;
660  assert(pd->cd->mask);
661  fetch->state = DIGEST_READ_MASK;
662  return StoreDigestCBlockSize;
663  } else {
664  peerDigestFetchAbort(fetch, buf, "invalid digest cblock");
665  return -1;
666  }
667  }
668 
669  /* need more data, do we have space? */
670  if (size >= SM_PAGE_SIZE) {
671  peerDigestFetchAbort(fetch, buf, "digest cblock too big");
672  return -1;
673  }
674 
675  return 0; /* We need more data */
676 }
677 
678 int
679 peerDigestSwapInMask(void *data, char *buf, ssize_t size)
680 {
682  PeerDigest *pd;
683 
684  pd = fetch->pd;
685  assert(pd->cd && pd->cd->mask);
686 
687  /*
688  * NOTENOTENOTENOTENOTE: buf doesn't point to pd->cd->mask anymore!
689  * we need to do the copy ourselves!
690  */
691  memcpy(pd->cd->mask + fetch->mask_offset, buf, size);
692 
693  /* NOTE! buf points to the middle of pd->cd->mask! */
694 
695  if (peerDigestFetchedEnough(fetch, NULL, size, "peerDigestSwapInMask"))
696  return -1;
697 
698  fetch->mask_offset += size;
699 
700  if (fetch->mask_offset >= pd->cd->mask_size) {
701  debugs(72, 2, "peerDigestSwapInMask: Done! Got " <<
702  fetch->mask_offset << ", expected " << pd->cd->mask_size);
703  assert(fetch->mask_offset == pd->cd->mask_size);
704  assert(peerDigestFetchedEnough(fetch, NULL, 0, "peerDigestSwapInMask"));
705  return -1; /* XXX! */
706  }
707 
708  /* We always read everything, so return size */
709  return size;
710 }
711 
712 static int
713 peerDigestFetchedEnough(DigestFetchState * fetch, char *buf, ssize_t size, const char *step_name)
714 {
715  static const SBuf hostUnknown("<unknown>"); // peer host (if any)
716  SBuf host = hostUnknown;
717 
718  PeerDigest *pd = NULL;
719  const char *reason = NULL; /* reason for completion */
720  const char *no_bug = NULL; /* successful completion if set */
721  const int pdcb_valid = cbdataReferenceValid(fetch->pd);
722  const int pcb_valid = cbdataReferenceValid(fetch->pd->peer);
723 
724  /* test possible exiting conditions (the same for most steps!)
725  * cases marked with '?!' should not happen */
726 
727  if (!reason) {
728  if (!(pd = fetch->pd))
729  reason = "peer digest disappeared?!";
730 
731 #if DONT /* WHY NOT? /HNO */
732 
733  else if (!cbdataReferenceValid(pd))
734  reason = "invalidated peer digest?!";
735 
736 #endif
737 
738  else
739  host = pd->host;
740  }
741 
742  debugs(72, 6, step_name << ": peer " << host << ", offset: " <<
743  fetch->offset << " size: " << size << ".");
744 
745  /* continue checking (with pd and host known and valid) */
746 
747  if (!reason) {
748  if (!cbdataReferenceValid(pd->peer))
749  reason = "peer disappeared";
750  else if (size < 0)
751  reason = "swap failure";
752  else if (!fetch->entry)
753  reason = "swap aborted?!";
754  else if (EBIT_TEST(fetch->entry->flags, ENTRY_ABORTED))
755  reason = "swap aborted";
756  }
757 
758  /* continue checking (maybe-successful eof case) */
759  if (!reason && !size) {
760  if (!pd->cd)
761  reason = "null digest?!";
762  else if (fetch->mask_offset != pd->cd->mask_size)
763  reason = "premature end of digest?!";
764  else if (!peerDigestUseful(pd))
765  reason = "useless digest";
766  else
767  reason = no_bug = "success";
768  }
769 
770  /* finish if we have a reason */
771  if (reason) {
772  const int level = strstr(reason, "?!") ? 1 : 3;
773  debugs(72, level, "" << step_name << ": peer " << host << ", exiting after '" << reason << "'");
774  peerDigestReqFinish(fetch, buf,
775  1, pdcb_valid, pcb_valid, reason, !no_bug);
776  } else {
777  /* paranoid check */
778  assert(pdcb_valid && pcb_valid);
779  }
780 
781  return reason != NULL;
782 }
783 
784 /* call this when all callback data is valid and fetch must be stopped but
785  * no error has occurred (e.g. we received 304 reply and reuse old digest) */
786 static void
787 peerDigestFetchStop(DigestFetchState * fetch, char *buf, const char *reason)
788 {
789  assert(reason);
790  debugs(72, 2, "peerDigestFetchStop: peer " << fetch->pd->host << ", reason: " << reason);
791  peerDigestReqFinish(fetch, buf, 1, 1, 1, reason, 0);
792 }
793 
794 /* call this when all callback data is valid but something bad happened */
795 static void
796 peerDigestFetchAbort(DigestFetchState * fetch, char *buf, const char *reason)
797 {
798  assert(reason);
799  debugs(72, 2, "peerDigestFetchAbort: peer " << fetch->pd->host << ", reason: " << reason);
800  peerDigestReqFinish(fetch, buf, 1, 1, 1, reason, 1);
801 }
802 
803 /* complete the digest transfer, update stats, unlock/release everything */
804 static void
806  int fcb_valid, int pdcb_valid, int pcb_valid,
807  const char *reason, int err)
808 {
809  assert(reason);
810 
811  /* must go before peerDigestPDFinish */
812 
813  if (pdcb_valid) {
814  fetch->pd->flags.requested = false;
815  fetch->pd->req_result = reason;
816  }
817 
818  /* schedule next check if peer is still out there */
819  if (pcb_valid) {
820  PeerDigest *pd = fetch->pd;
821 
822  if (err) {
825  } else {
826  pd->times.retry_delay = 0;
828  }
829  }
830 
831  /* note: order is significant */
832  if (fcb_valid)
834 
835  if (pdcb_valid)
836  peerDigestPDFinish(fetch, pcb_valid, err);
837 
838  if (fcb_valid)
839  peerDigestFetchFinish(fetch, err);
840 }
841 
842 /* destroys digest if peer disappeared
843  * must be called only when fetch and pd cbdata are valid */
844 static void
845 peerDigestPDFinish(DigestFetchState * fetch, int pcb_valid, int err)
846 {
847  PeerDigest *pd = fetch->pd;
848  const auto host = pd->host;
850  pd->times.req_delay = fetch->resp_time;
851  pd->stats.sent.kbytes += fetch->sent.bytes;
852  pd->stats.recv.kbytes += fetch->recv.bytes;
853  pd->stats.sent.msgs += fetch->sent.msg;
854  pd->stats.recv.msgs += fetch->recv.msg;
855 
856  if (err) {
857  debugs(72, DBG_IMPORTANT, "" << (pcb_valid ? "temporary " : "" ) << "disabling (" << pd->req_result << ") digest from " << host);
858 
859  delete pd->cd;
860  pd->cd = nullptr;
861 
862  pd->flags.usable = false;
863 
864  if (!pcb_valid)
866  } else {
867  assert(pcb_valid);
868 
869  pd->flags.usable = true;
870 
871  /* XXX: ugly condition, but how? */
872 
873  if (fetch->entry->store_status == STORE_OK)
874  debugs(72, 2, "re-used old digest from " << host);
875  else
876  debugs(72, 2, "received valid digest from " << host);
877  }
878 
879  cbdataReferenceDone(fetch->pd);
880 }
881 
882 /* free fetch state structures
883  * must be called only when fetch cbdata is valid */
884 static void
886 {
887  assert(fetch->entry && fetch->request);
888 
889  if (fetch->old_entry) {
890  debugs(72, 3, "peerDigestFetchFinish: deleting old entry");
891  storeUnregister(fetch->old_sc, fetch->old_entry, fetch);
892  fetch->old_entry->releaseRequest();
893  fetch->old_entry->unlock("peerDigestFetchFinish old");
894  fetch->old_entry = NULL;
895  }
896 
897  /* update global stats */
898  statCounter.cd.kbytes_sent += fetch->sent.bytes;
899  statCounter.cd.kbytes_recv += fetch->recv.bytes;
900  statCounter.cd.msgs_sent += fetch->sent.msg;
901  statCounter.cd.msgs_recv += fetch->recv.msg;
902 
903  delete fetch;
904 }
905 
906 /* calculate fetch stats after completion */
907 static void
909 {
910  MemObject *mem;
911  assert(fetch->entry && fetch->request);
912 
913  mem = fetch->entry->mem_obj;
914  assert(mem);
915 
916  /* XXX: outgoing numbers are not precise */
917  /* XXX: we must distinguish between 304 hits and misses here */
918  fetch->sent.bytes = fetch->request->prefixLen();
919  /* XXX: this is slightly wrong: we don't KNOW that the entire memobject
920  * was fetched. We only know how big it is
921  */
922  fetch->recv.bytes = mem->size();
923  fetch->sent.msg = fetch->recv.msg = 1;
924  fetch->expires = fetch->entry->expires;
925  fetch->resp_time = squid_curtime - fetch->start_time;
926 
927  debugs(72, 3, "peerDigestFetchFinish: recv " << fetch->recv.bytes <<
928  " bytes in " << (int) fetch->resp_time << " secs");
929 
930  debugs(72, 3, "peerDigestFetchFinish: expires: " <<
931  (long int) fetch->expires << " (" << std::showpos <<
932  (int) (fetch->expires - squid_curtime) << "), lmt: " <<
933  std::noshowpos << (long int) fetch->entry->lastModified() << " (" <<
934  std::showpos << (int) (fetch->entry->lastModified() - squid_curtime) <<
935  ")");
936 
937 }
938 
939 static int
941 {
942  StoreDigestCBlock cblock;
943  int freed_size = 0;
944  const auto host = pd->host;
945 
946  memcpy(&cblock, buf, sizeof(cblock));
947  /* network -> host conversions */
948  cblock.ver.current = ntohs(cblock.ver.current);
949  cblock.ver.required = ntohs(cblock.ver.required);
950  cblock.capacity = ntohl(cblock.capacity);
951  cblock.count = ntohl(cblock.count);
952  cblock.del_count = ntohl(cblock.del_count);
953  cblock.mask_size = ntohl(cblock.mask_size);
954  debugs(72, 2, "got digest cblock from " << host << "; ver: " <<
955  (int) cblock.ver.current << " (req: " << (int) cblock.ver.required <<
956  ")");
957 
958  debugs(72, 2, "\t size: " <<
959  cblock.mask_size << " bytes, e-cnt: " <<
960  cblock.count << ", e-util: " <<
961  xpercentInt(cblock.count, cblock.capacity) << "%" );
962  /* check version requirements (both ways) */
963 
964  if (cblock.ver.required > CacheDigestVer.current) {
965  debugs(72, DBG_IMPORTANT, "" << host << " digest requires version " <<
966  cblock.ver.required << "; have: " << CacheDigestVer.current);
967 
968  return 0;
969  }
970 
971  if (cblock.ver.current < CacheDigestVer.required) {
972  debugs(72, DBG_IMPORTANT, "" << host << " digest is version " <<
973  cblock.ver.current << "; we require: " <<
975 
976  return 0;
977  }
978 
979  /* check consistency */
980  if (cblock.ver.required > cblock.ver.current ||
981  cblock.mask_size <= 0 || cblock.capacity <= 0 ||
982  cblock.bits_per_entry <= 0 || cblock.hash_func_count <= 0) {
983  debugs(72, DBG_CRITICAL, "" << host << " digest cblock is corrupted.");
984  return 0;
985  }
986 
987  /* check consistency further */
988  if ((size_t)cblock.mask_size != CacheDigest::CalcMaskSize(cblock.capacity, cblock.bits_per_entry)) {
989  debugs(72, DBG_CRITICAL, host << " digest cblock is corrupted " <<
990  "(mask size mismatch: " << cblock.mask_size << " ? " <<
992  << ").");
993  return 0;
994  }
995 
996  /* there are some things we cannot do yet */
998  debugs(72, DBG_CRITICAL, "" << host << " digest: unsupported #hash functions: " <<
999  cblock.hash_func_count << " ? " << CacheDigestHashFuncCount << ".");
1000  return 0;
1001  }
1002 
1003  /*
1004  * no cblock bugs below this point
1005  */
1006  /* check size changes */
1007  if (pd->cd && cblock.mask_size != (ssize_t)pd->cd->mask_size) {
1008  debugs(72, 2, host << " digest changed size: " << cblock.mask_size <<
1009  " -> " << pd->cd->mask_size);
1010  freed_size = pd->cd->mask_size;
1011  delete pd->cd;
1012  pd->cd = nullptr;
1013  }
1014 
1015  if (!pd->cd) {
1016  debugs(72, 2, "creating " << host << " digest; size: " << cblock.mask_size << " (" <<
1017  std::showpos << (int) (cblock.mask_size - freed_size) << ") bytes");
1018  pd->cd = new CacheDigest(cblock.capacity, cblock.bits_per_entry);
1019 
1020  if (cblock.mask_size >= freed_size)
1021  statCounter.cd.memory += (cblock.mask_size - freed_size);
1022  }
1023 
1024  assert(pd->cd);
1025  /* these assignments leave us in an inconsistent state until we finish reading the digest */
1026  pd->cd->count = cblock.count;
1027  pd->cd->del_count = cblock.del_count;
1028  return 1;
1029 }
1030 
1031 static int
1033 {
1034  /* TODO: we should calculate the prob of a false hit instead of bit util */
1035  const auto bit_util = pd->cd->usedMaskPercent();
1036 
1037  if (bit_util > 65.0) {
1038  debugs(72, DBG_CRITICAL, "Warning: " << pd->host <<
1039  " peer digest has too many bits on (" << bit_util << "%).");
1040  return 0;
1041  }
1042 
1043  return 1;
1044 }
1045 
1046 static int
1047 saneDiff(time_t diff)
1048 {
1049  return abs((int) diff) > squid_curtime / 2 ? 0 : diff;
1050 }
1051 
1052 void
1054 {
1055 #define f2s(flag) (pd->flags.flag ? "yes" : "no")
1056 #define appendTime(tm) storeAppendPrintf(e, "%s\t %10ld\t %+d\t %+d\n", \
1057  ""#tm, (long int)pd->times.tm, \
1058  saneDiff(pd->times.tm - squid_curtime), \
1059  saneDiff(pd->times.tm - pd->times.initialized))
1060 
1061  assert(pd);
1062 
1063  auto host = pd->host;
1064  storeAppendPrintf(e, "\npeer digest from " SQUIDSBUFPH "\n", SQUIDSBUFPRINT(host));
1065 
1066  cacheDigestGuessStatsReport(&pd->stats.guess, e, host);
1067 
1068  storeAppendPrintf(e, "\nevent\t timestamp\t secs from now\t secs from init\n");
1069  appendTime(initialized);
1070  appendTime(needed);
1071  appendTime(requested);
1072  appendTime(received);
1073  appendTime(next_check);
1074 
1075  storeAppendPrintf(e, "peer digest state:\n");
1076  storeAppendPrintf(e, "\tneeded: %3s, usable: %3s, requested: %3s\n",
1077  f2s(needed), f2s(usable), f2s(requested));
1078  storeAppendPrintf(e, "\n\tlast retry delay: %d secs\n",
1079  (int) pd->times.retry_delay);
1080  storeAppendPrintf(e, "\tlast request response time: %d secs\n",
1081  (int) pd->times.req_delay);
1082  storeAppendPrintf(e, "\tlast request result: %s\n",
1083  pd->req_result ? pd->req_result : "(none)");
1084 
1085  storeAppendPrintf(e, "\npeer digest traffic:\n");
1086  storeAppendPrintf(e, "\trequests sent: %d, volume: %d KB\n",
1087  pd->stats.sent.msgs, (int) pd->stats.sent.kbytes.kb);
1088  storeAppendPrintf(e, "\treplies recv: %d, volume: %d KB\n",
1089  pd->stats.recv.msgs, (int) pd->stats.recv.kbytes.kb);
1090 
1091  storeAppendPrintf(e, "\npeer digest structure:\n");
1092 
1093  if (pd->cd)
1094  cacheDigestReport(pd->cd, host, e);
1095  else
1096  storeAppendPrintf(e, "\tno in-memory copy\n");
1097 }
1098 
1099 #endif
1100 
bool usable
Definition: PeerDigest.h:89
unsigned char bits_per_entry
Definition: PeerDigest.h:34
void fatal(const char *message)
Definition: fatal.cc:28
struct PeerDigest::@92::@93 sent
double usedMaskPercent() const
percentage of mask bits which are used
Definition: CacheDigest.cc:194
static EVH peerDigestCheck
Definition: peer_digest.cc:38
static void peerDigestFetchAbort(DigestFetchState *fetch, char *buf, const char *reason)
Definition: peer_digest.cc:796
#define SM_PAGE_SIZE
Definition: defines.h:102
PeerDigest * digest
Definition: CachePeer.h:142
static int peerDigestFetchReply(void *, char *, ssize_t)
Definition: peer_digest.cc:518
void userInfo(const SBuf &s)
Definition: Uri.h:79
int peerDigestSwapInHeaders(void *, char *, ssize_t)
Definition: peer_digest.cc:602
#define StoreDigestCBlockSize
Definition: peer_digest.cc:58
AnyP::Uri url
the request URI
Definition: HttpRequest.h:115
bool needed
Definition: PeerDigest.h:88
void ensureMemObject(const char *storeId, const char *logUri, const HttpRequestMethod &aMethod)
initialize mem_obj (if needed) and set URIs/method (if missing)
Definition: store.cc:1624
@ DIGEST_READ_NONE
Definition: enums.h:203
void peerDigestCreate(CachePeer *p)
Definition: peer_digest.cc:130
#define appendTime(tm)
void releaseRequest(const bool shareable=false)
Definition: store.cc:472
time_t req_delay
Definition: PeerDigest.h:100
digest_read_state_t state
Definition: PeerDigest.h:71
digest_read_state_t
Definition: enums.h:202
#define cbdataReferenceValidDone(var, ptr)
Definition: cbdata.h:256
@ scNone
Definition: StatusCode.h:21
struct DigestFetchState::@89 sent
HttpHeader header
Definition: Message.h:75
StoreEntry * entry
Definition: PeerDigest.h:53
MemObject * mem_obj
Definition: Store.h:213
RequestFlags flags
Definition: HttpRequest.h:141
int peerDigestSwapInMask(void *, char *, ssize_t)
Definition: peer_digest.cc:679
static int saneDiff(time_t diff)
ByteCounter memory
Definition: StatCounters.h:105
CacheDigest * cd
Definition: PeerDigest.h:83
MemObject & mem()
Definition: Store.h:52
time_t disabled
Definition: PeerDigest.h:102
time_t next_check
Definition: PeerDigest.h:97
void storeAppendPrintf(StoreEntry *e, const char *fmt,...)
Definition: store.cc:880
void lock(const char *context)
Definition: store.cc:459
char * login
Definition: CachePeer.h:174
bool requested
Definition: PeerDigest.h:90
int64_t offset
Definition: StoreIOBuffer.h:55
@ KEY_PRIVATE
Definition: enums.h:102
Definition: SBuf.h:86
#define xstrdup
store_client * old_sc
Definition: PeerDigest.h:56
const Version CacheDigestVer
Definition: peer_digest.cc:56
time_t start_time
Definition: PeerDigest.h:60
uint16_t flags
Definition: Store.h:224
int cbdataReferenceValid(const void *p)
Definition: cbdata.cc:412
void cacheDigestGuessStatsReport(const CacheDigestGuessStats *stats, StoreEntry *sentry, const SBuf &label)
Definition: CacheDigest.cc:220
static void peerDigestFetchSetStats(DigestFetchState *fetch)
Definition: peer_digest.cc:908
Http::StatusLine sline
Definition: HttpReply.h:60
time_t expires
Definition: Store.h:218
void HTTPMSGUNLOCK(M *&a)
Definition: Message.h:149
#define DBG_CRITICAL
Definition: Debug.h:45
static int peerDigestSetCBlock(PeerDigest *pd, const char *buf)
Definition: peer_digest.cc:940
SBuf host
copy of peer->host
Definition: PeerDigest.h:84
#define cbdataReference(var)
Definition: cbdata.h:341
#define DBG_IMPORTANT
Definition: Debug.h:46
uint32_t mask_offset
Definition: PeerDigest.h:59
StoreEntry * storeGetPublicByRequest(HttpRequest *req, const KeyScope keyScope)
Definition: store.cc:533
struct PeerDigest::@92 stats
static const time_t GlobDigestReqMinGap
Definition: peer_digest.cc:63
void peerDigestNeeded(PeerDigest *pd)
Definition: peer_digest.cc:177
StoreEntry * old_entry
Definition: PeerDigest.h:54
ByteCounter kbytes_sent
Definition: StatCounters.h:76
void peerDigestStatsReport(const PeerDigest *pd, StoreEntry *e)
static STCB peerDigestHandleReply
Definition: peer_digest.cc:40
store_client * sc
Definition: PeerDigest.h:55
static const time_t PeerDigestReqMinGap
Definition: peer_digest.cc:61
@ initCacheDigest
Cache Digest fetching code.
const char * StoreDigestFileName
@ DIGEST_READ_DONE
Definition: enums.h:208
char * internalRemoteUri(bool encrypt, const char *host, unsigned short port, const char *dir, const SBuf &name)
Definition: internal.cc:86
int size
Definition: ModDevPoll.cc:77
#define NULL
Definition: types.h:166
void cacheDigestReport(CacheDigest *cd, const SBuf &label, StoreEntry *e)
Definition: CacheDigest.cc:256
#define SQUIDSBUFPRINT(s)
Definition: SBuf.h:32
static int peerDigestFetchedEnough(DigestFetchState *fetch, char *buf, ssize_t size, const char *step_name)
Definition: peer_digest.cc:713
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Debug.h:128
short int required
Definition: PeerDigest.h:21
Http::StatusCode status() const
retrieve the status code for this status line
Definition: StatusLine.h:45
static void peerDigestFetchStop(DigestFetchState *fetch, char *buf, const char *reason)
Definition: peer_digest.cc:787
time_t resp_time
Definition: PeerDigest.h:61
@ DIGEST_READ_HEADERS
Definition: enums.h:205
#define EBIT_TEST(flag, bit)
Definition: defines.h:107
void const char HLPCB void * data
Definition: stub_helper.cc:16
int unlock(const char *context)
Definition: store.cc:483
struct PeerDigest::@92::@93 recv
#define safe_free(x)
Definition: xalloc.h:73
store_status_t store_status
Definition: Store.h:236
time_t retry_delay
Definition: PeerDigest.h:98
struct PeerDigest::@91 times
static void peerDigestFetchFinish(DigestFetchState *fetch, int err)
Definition: peer_digest.cc:885
#define assert(EX)
Definition: assert.h:19
static uint32_t CalcMaskSize(uint64_t cap, uint8_t bpe)
Definition: CacheDigest.cc:284
const char * StoreDigestMimeStr
static time_t peerDigestIncDelay(const PeerDigest *pd)
Definition: peer_digest.cc:209
char * mask
Definition: CacheDigest.h:58
void HTTPMSGLOCK(Http::Message *a)
Definition: Message.h:160
int64_t size() const
Definition: MemObject.cc:250
#define cbdataReferenceDone(var)
Definition: cbdata.h:350
CachePeer * peer
Definition: PeerDigest.h:82
uint64_t del_count
Definition: CacheDigest.h:56
@ DIGEST_READ_CBLOCK
Definition: enums.h:206
static int sc[16]
Definition: smbdes.c:121
#define CBDATA_CLASS_INIT(type)
Definition: cbdata.h:318
static void peerDigestReqFinish(DigestFetchState *fetch, char *buf, int, int, int, const char *reason, int err)
Definition: peer_digest.cc:805
int prefixLen() const
Definition: HttpRequest.cc:372
static void peerDigestPDFinish(DigestFetchState *fetch, int pcb_valid, int err)
Definition: peer_digest.cc:845
@ STORE_OK
Definition: enums.h:50
time_t squid_curtime
Definition: stub_time.cc:17
int peerDigestSwapInCBlock(void *, char *, ssize_t)
Definition: peer_digest.cc:641
PeerDigest(CachePeer *)
Definition: peer_digest.cc:69
StoreEntry * storeCreateEntry(const char *url, const char *logUrl, const RequestFlags &flags, const HttpRequestMethod &method)
Definition: store.cc:782
static void peerDigestDestroy(PeerDigest *pd)
Definition: peer_digest.cc:149
#define f2s(flag)
@ scNotModified
Definition: StatusCode.h:40
void EVH(void *)
Definition: event.h:19
static void peerDigestRequest(PeerDigest *pd)
Definition: peer_digest.cc:310
static void peerDigestSetCheck(PeerDigest *pd, time_t delay)
Definition: peer_digest.cc:232
HttpRequest * request
Definition: PeerDigest.h:57
struct StatCounters::@135 cd
HttpRequestMethod method
Definition: HttpRequest.h:114
char * buf() const
Definition: SBuf.h:634
@ ENTRY_ABORTED
Definition: enums.h:115
char * host
Definition: CachePeer.h:40
short int current
Definition: PeerDigest.h:20
char buf[SM_PAGE_SIZE]
Definition: PeerDigest.h:69
static time_t pd_last_req_time
Definition: peer_digest.cc:67
void updateOnNotModified(StoreEntry *old, StoreEntry &e304)
using a 304 response, update the old entry (metadata and reply headers)
Definition: Controller.cc:725
void putStr(Http::HdrType id, const char *str)
Definition: HttpHeader.cc:1068
const char * req_result
Definition: PeerDigest.h:85
static HttpRequest * FromUrlXXX(const char *url, const MasterXaction::Pointer &, const HttpRequestMethod &method=Http::METHOD_GET)
Definition: HttpRequest.cc:546
CacheDigestGuessStats guess
Definition: PeerDigest.h:106
@ DIGEST_READ_MASK
Definition: enums.h:207
const HttpReply & freshestReply() const
Definition: MemObject.h:67
unsigned char hash_func_count
Definition: PeerDigest.h:35
DigestFetchState(PeerDigest *, HttpRequest *)
Definition: peer_digest.cc:89
static time_t peerDigestNewDelay(const StoreEntry *e)
Definition: peer_digest.cc:220
time_t received
Definition: PeerDigest.h:101
@ DIGEST_READ_REPLY
Definition: enums.h:204
static void fwdStart(const Comm::ConnectionPointer &client, StoreEntry *, HttpRequest *)
Same as Start() but no master xaction info (AccessLogEntry) available.
Definition: FwdState.cc:420
bool encryptTransport
whether transport encryption (TLS/SSL) is to be used on connections to the peer
Definition: PeerOptions.h:145
void lastModified(const time_t when)
Definition: Store.h:168
uint32_t mask_size
Definition: CacheDigest.h:59
@ scOkay
Definition: StatusCode.h:26
PeerDigest * pd
Definition: PeerDigest.h:52
struct PeerDigest::@90 flags
uint64_t count
Definition: CacheDigest.h:55
void peerDigestNotePeerGone(PeerDigest *pd)
Definition: peer_digest.cc:243
int storeUnregister(store_client *sc, StoreEntry *e, void *data)
size_t headersEnd(const char *mime, size_t l, bool &containsObsFold)
Definition: mime_header.cc:16
Security::PeerOptions secure
security settings for peer connection
Definition: CachePeer.h:190
void storeClientCopy(store_client *sc, StoreEntry *e, StoreIOBuffer copyInto, STCB *callback, void *data)
static int peerDigestUseful(const PeerDigest *pd)
struct _request * request(char *urlin)
Definition: tcp-banger2.c:291
SQUIDCEXTERN int xpercentInt(double part, double whole)
Definition: util.c:60
ByteCounter kbytes_recv
Definition: StatCounters.h:79
HttpRequestPointer request
Definition: MemObject.h:188
struct DigestFetchState::@89 recv
int CacheDigestHashFuncCount
unsigned short http_port
Definition: CachePeer.h:77
void const char * buf
Definition: stub_helper.cc:16
void eventAdd(const char *name, EVH *func, void *arg, double when, int weight, bool cbdata)
Definition: event.cc:109
void STCB(void *, StoreIOBuffer)
Definition: StoreClient.h:17
#define SQUIDSBUFPH
Definition: SBuf.h:31
store_client * storeClientListAdd(StoreEntry *e, void *data)
ssize_t bufofs
Definition: PeerDigest.h:70
StatCounters statCounter
Definition: StatCounters.cc:12
char * digest_url
Definition: CachePeer.h:143
Controller & Root()
safely access controller singleton
Definition: Controller.cc:938

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors