]> git.ipfire.org Git - thirdparty/squid.git/blame - src/peer_digest.cc
Fix MemMapSlot constructor initialization of expiry TTL
[thirdparty/squid.git] / src / peer_digest.cc
CommitLineData
9b7de833 1
2/*
9b7de833 3 * DEBUG: section 72 Peer Digest Routines
4 * AUTHOR: Alex Rousskov
5 *
2b6662ba 6 * SQUID Web Proxy Cache http://www.squid-cache.org/
e25c139f 7 * ----------------------------------------------------------
9b7de833 8 *
2b6662ba 9 * Squid is the result of efforts by numerous individuals from
10 * the Internet community; see the CONTRIBUTORS file for full
11 * details. Many organizations have provided support for Squid's
12 * development; see the SPONSORS file for full details. Squid is
13 * Copyrighted (C) 2001 by the Regents of the University of
14 * California; see the COPYRIGHT file for full details. Squid
15 * incorporates software developed and/or copyrighted by other
16 * sources; see the CREDITS file for full details.
9b7de833 17 *
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 2 of the License, or
21 * (at your option) any later version.
26ac0430 22 *
9b7de833 23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
26ac0430 27 *
9b7de833 28 * You should have received a copy of the GNU General Public License
29 * along with this program; if not, write to the Free Software
cbdec147 30 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
e25c139f 31 *
9b7de833 32 */
33
582c2af2 34#include "squid.h"
6cfa8966 35#if USE_CACHE_DIGESTS
b814e8d4 36#include "CacheDigest.h"
a011edee 37#include "CachePeer.h"
a553a5a3 38#include "event.h"
eb13c21e 39#include "FwdState.h"
af69c635 40#include "globals.h"
528b2c61 41#include "HttpReply.h"
582c2af2 42#include "HttpRequest.h"
308e60be 43#include "internal.h"
528b2c61 44#include "MemObject.h"
b6149797 45#include "mime_header.h"
602d9612 46#include "neighbors.h"
aa839030 47#include "PeerDigest.h"
985c86bc 48#include "SquidTime.h"
582c2af2 49#include "Store.h"
fb548aaf 50#include "store_key_md5.h"
598465f1 51#include "StoreClient.h"
4e540555 52#include "tools.h"
598465f1 53
9b7de833 54/* local types */
55
56/* local prototypes */
e13ee7ad 57static time_t peerDigestIncDelay(const PeerDigest * pd);
58static time_t peerDigestNewDelay(const StoreEntry * e);
59static void peerDigestSetCheck(PeerDigest * pd, time_t delay);
eb16313f 60static void peerDigestClean(PeerDigest *);
e13ee7ad 61static EVH peerDigestCheck;
62static void peerDigestRequest(PeerDigest * pd);
add2192d 63static STCB peerDigestHandleReply;
3dfaa3d2 64static int peerDigestFetchReply(void *, char *, ssize_t);
1f140227 65int peerDigestSwapInHeaders(void *, char *, ssize_t);
66int peerDigestSwapInCBlock(void *, char *, ssize_t);
67int peerDigestSwapInMask(void *, char *, ssize_t);
4b4cd312 68static int peerDigestFetchedEnough(DigestFetchState * fetch, char *buf, ssize_t size, const char *step_name);
551e60a9 69static void peerDigestFetchStop(DigestFetchState * fetch, char *buf, const char *reason);
e13ee7ad 70static void peerDigestFetchAbort(DigestFetchState * fetch, char *buf, const char *reason);
71static void peerDigestReqFinish(DigestFetchState * fetch, char *buf, int, int, int, const char *reason, int err);
72static void peerDigestPDFinish(DigestFetchState * fetch, int pcb_valid, int err);
73static void peerDigestFetchFinish(DigestFetchState * fetch, int err);
74static void peerDigestFetchSetStats(DigestFetchState * fetch);
75static int peerDigestSetCBlock(PeerDigest * pd, const char *buf);
76static int peerDigestUseful(const PeerDigest * pd);
395b813e 77
9b7de833 78/* local constants */
aa839030 79Version const CacheDigestVer = { 5, 3 };
9d486b43 80
9b7de833 81#define StoreDigestCBlockSize sizeof(StoreDigestCBlock)
82
e13ee7ad 83/* min interval for requesting digests from a given peer */
84static const time_t PeerDigestReqMinGap = 5 * 60; /* seconds */
85/* min interval for requesting digests (cumulative request stream) */
86static const time_t GlobDigestReqMinGap = 1 * 60; /* seconds */
bd890734 87
88/* local vars */
9d486b43 89
8a6218c6 90static time_t pd_last_req_time = 0; /* last call to Check */
9b7de833 91
e13ee7ad 92/* initialize peer digest */
93static void
a3c6762c 94peerDigestInit(PeerDigest * pd, CachePeer * p)
9b7de833 95{
e13ee7ad 96 assert(pd && p);
97
98 memset(pd, 0, sizeof(*pd));
4e454ade 99 /*
100 * DPW 2007-04-12
101 * Lock on to the peer here. The corresponding cbdataReferenceDone()
102 * is in peerDigestDestroy().
103 */
104 pd->peer = cbdataReference(p);
e13ee7ad 105 /* if peer disappears, we will know it's name */
528b2c61 106 pd->host = p->host;
e13ee7ad 107
108 pd->times.initialized = squid_curtime;
9b7de833 109}
110
4b4cd312 111static void
8a6218c6 112peerDigestClean(PeerDigest * pd)
9b7de833 113{
e13ee7ad 114 assert(pd);
62e76326 115
e13ee7ad 116 if (pd->cd)
62e76326 117 cacheDigestDestroy(pd->cd);
118
30abd221 119 pd->host.clean();
9b7de833 120}
121
0353e724 122CBDATA_CLASS_INIT(PeerDigest);
123
e13ee7ad 124/* allocate new peer digest, call Init, and lock everything */
125PeerDigest *
a3c6762c 126peerDigestCreate(CachePeer * p)
e13ee7ad 127{
128 PeerDigest *pd;
8a6218c6 129 assert(p);
e13ee7ad 130
0353e724 131 pd = new PeerDigest;
e13ee7ad 132 peerDigestInit(pd, p);
e13ee7ad 133
fa80a8ef 134 /* XXX This does not look right, and the same thing again in the caller */
135 return cbdataReference(pd);
e13ee7ad 136}
137
138/* call Clean and free/unlock everything */
2d72d4fd 139static void
8a6218c6 140peerDigestDestroy(PeerDigest * pd)
e13ee7ad 141{
8abf232c 142 void *p;
e13ee7ad 143 assert(pd);
8abf232c 144 void * peerTmp = pd->peer;
e13ee7ad 145
4e454ade 146 /*
147 * DPW 2007-04-12
148 * We locked the peer in peerDigestInit(), this is
149 * where we unlock it. If the peer is still valid,
150 * tell it that the digest is gone.
151 */
8abf232c 152 if (cbdataReferenceValidDone(peerTmp, &p))
a3c6762c 153 peerNoteDigestGone((CachePeer *)p);
e13ee7ad 154
155 peerDigestClean(pd);
62e76326 156
00d77d6b 157 delete pd;
e13ee7ad 158}
159
160/* called by peer to indicate that somebody actually needs this digest */
161void
8a6218c6 162peerDigestNeeded(PeerDigest * pd)
e13ee7ad 163{
164 assert(pd);
165 assert(!pd->flags.needed);
166 assert(!pd->cd);
167
be4d35dc 168 pd->flags.needed = true;
e13ee7ad 169 pd->times.needed = squid_curtime;
8a6218c6 170 peerDigestSetCheck(pd, 0); /* check asap */
e13ee7ad 171}
172
173/* currently we do not have a reason to disable without destroying */
8a6218c6 174#if FUTURE_CODE
395b813e 175/* disables peer for good */
176static void
8a6218c6 177peerDigestDisable(PeerDigest * pd)
395b813e 178{
bf8fe701 179 debugs(72, 2, "peerDigestDisable: peer " << pd->host.buf() << " disabled for good");
e13ee7ad 180 pd->times.disabled = squid_curtime;
8a6218c6 181 pd->times.next_check = -1; /* never */
e13ee7ad 182 pd->flags.usable = 0;
183
184 if (pd->cd) {
62e76326 185 cacheDigestDestroy(pd->cd);
186 pd->cd = NULL;
e13ee7ad 187 }
62e76326 188
e13ee7ad 189 /* we do not destroy the pd itself to preserve its "history" and stats */
395b813e 190}
62e76326 191
e13ee7ad 192#endif
395b813e 193
e13ee7ad 194/* increment retry delay [after an unsuccessful attempt] */
395b813e 195static time_t
8a6218c6 196peerDigestIncDelay(const PeerDigest * pd)
395b813e 197{
e13ee7ad 198 assert(pd);
199 return pd->times.retry_delay > 0 ?
62e76326 200 2 * pd->times.retry_delay : /* exponential backoff */
201 PeerDigestReqMinGap; /* minimal delay */
395b813e 202}
203
62e76326 204/* artificially increases Expires: setting to avoid race conditions
e13ee7ad 205 * returns the delay till that [increased] expiration time */
00485c29 206static time_t
e13ee7ad 207peerDigestNewDelay(const StoreEntry * e)
00485c29 208{
e13ee7ad 209 assert(e);
62e76326 210
00485c29 211 if (e->expires > 0)
62e76326 212 return e->expires + PeerDigestReqMinGap - squid_curtime;
213
e13ee7ad 214 return PeerDigestReqMinGap;
00485c29 215}
216
e13ee7ad 217/* registers next digest verification */
395b813e 218static void
e13ee7ad 219peerDigestSetCheck(PeerDigest * pd, time_t delay)
395b813e 220{
e13ee7ad 221 eventAdd("peerDigestCheck", peerDigestCheck, pd, (double) delay, 1);
222 pd->times.next_check = squid_curtime + delay;
bb790702 223 debugs(72, 3, "peerDigestSetCheck: will check peer " << pd->host << " in " << delay << " secs");
e13ee7ad 224}
225
5385c86a 226/*
227 * called when peer is about to disappear or have already disappeared
228 */
e13ee7ad 229void
8a6218c6 230peerDigestNotePeerGone(PeerDigest * pd)
231{
e13ee7ad 232 if (pd->flags.requested) {
bb790702 233 debugs(72, 2, "peerDigest: peer " << pd->host << " gone, will destroy after fetch.");
62e76326 234 /* do nothing now, the fetching chain will notice and take action */
395b813e 235 } else {
bb790702 236 debugs(72, 2, "peerDigest: peer " << pd->host << " is gone, destroying now.");
62e76326 237 peerDigestDestroy(pd);
395b813e 238 }
239}
240
e13ee7ad 241/* callback for eventAdd() (with peer digest locked)
26ac0430 242 * request new digest if our copy is too old or if we lack one;
e13ee7ad 243 * schedule next check otherwise */
9b7de833 244static void
e13ee7ad 245peerDigestCheck(void *data)
9b7de833 246{
e6ccf245 247 PeerDigest *pd = (PeerDigest *)data;
e13ee7ad 248 time_t req_time;
249
e13ee7ad 250 assert(!pd->flags.requested);
5d9bb360 251
8a6218c6 252 pd->times.next_check = 0; /* unknown */
e13ee7ad 253
fa80a8ef 254 if (!cbdataReferenceValid(pd->peer)) {
62e76326 255 peerDigestNotePeerGone(pd);
256 return;
9b7de833 257 }
62e76326 258
cc192b50 259 debugs(72, 3, "peerDigestCheck: peer " << pd->peer->host << ":" << pd->peer->http_port);
26ac0430
AJ
260 debugs(72, 3, "peerDigestCheck: time: " << squid_curtime <<
261 ", last received: " << (long int) pd->times.received << " (" <<
bf8fe701 262 std::showpos << (int) (squid_curtime - pd->times.received) << ")");
e13ee7ad 263
264 /* decide when we should send the request:
265 * request now unless too close to other requests */
266 req_time = squid_curtime;
267
268 /* per-peer limit */
62e76326 269
e13ee7ad 270 if (req_time - pd->times.received < PeerDigestReqMinGap) {
b4197865 271 debugs(72, 2, "peerDigestCheck: " << pd->host <<
bf8fe701 272 ", avoiding close peer requests (" <<
273 (int) (req_time - pd->times.received) << " < " <<
274 (int) PeerDigestReqMinGap << " secs).");
275
62e76326 276 req_time = pd->times.received + PeerDigestReqMinGap;
bd890734 277 }
62e76326 278
e13ee7ad 279 /* global limit */
280 if (req_time - pd_last_req_time < GlobDigestReqMinGap) {
b4197865 281 debugs(72, 2, "peerDigestCheck: " << pd->host <<
bf8fe701 282 ", avoiding close requests (" <<
283 (int) (req_time - pd_last_req_time) << " < " <<
284 (int) GlobDigestReqMinGap << " secs).");
285
62e76326 286 req_time = pd_last_req_time + GlobDigestReqMinGap;
9b7de833 287 }
62e76326 288
e13ee7ad 289 if (req_time <= squid_curtime)
62e76326 290 peerDigestRequest(pd); /* will set pd->flags.requested */
e13ee7ad 291 else
62e76326 292 peerDigestSetCheck(pd, req_time - squid_curtime);
9b7de833 293}
294
2812146b 295CBDATA_TYPE(DigestFetchState);
296
e13ee7ad 297/* ask store for a digest */
9b7de833 298static void
e13ee7ad 299peerDigestRequest(PeerDigest * pd)
9b7de833 300{
a3c6762c 301 CachePeer *p = pd->peer;
9b7de833 302 StoreEntry *e, *old_e;
303 char *url;
9d486b43 304 const cache_key *key;
190154cf 305 HttpRequest *req;
9b7de833 306 DigestFetchState *fetch = NULL;
528b2c61 307 StoreIOBuffer tempBuffer;
e13ee7ad 308
309 pd->req_result = NULL;
be4d35dc 310 pd->flags.requested = true;
e13ee7ad 311
9b7de833 312 /* compute future request components */
62e76326 313
7e3ce7b9 314 if (p->digest_url)
62e76326 315 url = xstrdup(p->digest_url);
7e3ce7b9 316 else
62e76326 317 url = internalRemoteUri(p->host, p->http_port,
318 "/squid-internal-periodic/", StoreDigestFileName);
7e3ce7b9 319
c21ad0f5 320 req = HttpRequest::CreateFromUrl(url);
62e76326 321
e13ee7ad 322 assert(req);
62e76326 323
f66a9ef4 324 key = storeKeyPublicByRequest(req);
62e76326 325
bf8fe701 326 debugs(72, 2, "peerDigestRequest: " << url << " key: " << storeKeyText(key));
e13ee7ad 327
9b7de833 328 /* add custom headers */
2246b732 329 assert(!req->header.len);
62e76326 330
a9925b40 331 req->header.putStr(HDR_ACCEPT, StoreDigestMimeStr);
62e76326 332
a9925b40 333 req->header.putStr(HDR_ACCEPT, "text/html");
62e76326 334
89000349
AJ
335 if (p->login &&
336 p->login[0] != '*' &&
b552ea1f
A
337 strcmp(p->login, "PASS") != 0 &&
338 strcmp(p->login, "PASSTHRU") != 0 &&
339 strcmp(p->login, "NEGOTIATE") != 0 &&
89000349 340 strcmp(p->login, "PROXYPASS") != 0) {
62e76326 341 xstrncpy(req->login, p->login, MAX_LOGIN_SZ);
89000349 342 }
9b7de833 343 /* create fetch state structure */
2812146b 344 CBDATA_INIT_TYPE(DigestFetchState);
62e76326 345
72711e31 346 fetch = cbdataAlloc(DigestFetchState);
62e76326 347
b248c2a3
AJ
348 fetch->request = req;
349 HTTPMSGLOCK(fetch->request);
62e76326 350
fa80a8ef 351 fetch->pd = cbdataReference(pd);
62e76326 352
e13ee7ad 353 fetch->offset = 0;
62e76326 354
add2192d 355 fetch->state = DIGEST_READ_REPLY;
e13ee7ad 356
357 /* update timestamps */
9b7de833 358 fetch->start_time = squid_curtime;
62e76326 359
e13ee7ad 360 pd->times.requested = squid_curtime;
62e76326 361
e13ee7ad 362 pd_last_req_time = squid_curtime;
363
e857372a 364 req->flags.cachable = true;
62e76326 365
9b7de833 366 /* the rest is based on clientProcessExpired() */
e857372a 367 req->flags.refresh = true;
62e76326 368
c8f4eac4 369 old_e = fetch->old_entry = Store::Root().get(key);
62e76326 370
9b7de833 371 if (old_e) {
bf8fe701 372 debugs(72, 5, "peerDigestRequest: found old entry");
34266cde 373
acc5dc4c 374 old_e->lock("peerDigestRequest");
c877c0bc 375 old_e->createMemObject(url, url, req->method);
34266cde 376
62e76326 377 fetch->old_sc = storeClientListAdd(old_e, fetch);
9b7de833 378 }
62e76326 379
9b7de833 380 e = fetch->entry = storeCreateEntry(url, url, req->flags, req->method);
e13ee7ad 381 assert(EBIT_TEST(e->flags, KEY_PRIVATE));
06d2839d 382 fetch->sc = storeClientListAdd(e, fetch);
9b7de833 383 /* set lastmod to trigger IMS request if possible */
62e76326 384
9b7de833 385 if (old_e)
62e76326 386 e->lastmod = old_e->lastmod;
e13ee7ad 387
9b7de833 388 /* push towards peer cache */
bf8fe701 389 debugs(72, 3, "peerDigestRequest: forwarding to fwdStart...");
62e76326 390
e83cc785 391 FwdState::fwdStart(Comm::ConnectionPointer(), e, req);
62e76326 392
598465f1 393 tempBuffer.offset = 0;
62e76326 394
f95db407 395 tempBuffer.length = SM_PAGE_SIZE;
62e76326 396
598465f1 397 tempBuffer.data = fetch->buf;
62e76326 398
598465f1 399 storeClientCopy(fetch->sc, e, tempBuffer,
62e76326 400 peerDigestHandleReply, fetch);
9b7de833 401}
402
add2192d 403/* Handle the data copying .. */
404
405/*
406 * This routine handles the copy data and then redirects the
407 * copy to a bunch of subfunctions depending upon the copy state.
408 * It also tracks the buffer offset and "seen", since I'm actually
409 * not interested in rewriting everything to suit my little idea.
410 */
9b7de833 411static void
2324cda2 412peerDigestHandleReply(void *data, StoreIOBuffer receivedData)
add2192d 413{
e6ccf245 414 DigestFetchState *fetch = (DigestFetchState *)data;
add2192d 415 int retsize = -1;
416 digest_read_state_t prevstate;
417 int newsize;
418
2324cda2 419 assert(fetch->pd && receivedData.data);
420 /* The existing code assumes that the received pointer is
598465f1 421 * where we asked the data to be put
422 */
2324cda2 423 assert(fetch->buf + fetch->bufofs == receivedData.data);
add2192d 424
425 /* Update the buffer size */
2324cda2 426 fetch->bufofs += receivedData.length;
add2192d 427
428 assert(fetch->bufofs <= SM_PAGE_SIZE);
429
430 /* If we've fetched enough, return */
62e76326 431
add2192d 432 if (peerDigestFetchedEnough(fetch, fetch->buf, fetch->bufofs, "peerDigestHandleReply"))
62e76326 433 return;
add2192d 434
435 /* Call the right function based on the state */
436 /* (Those functions will update the state if needed) */
fa80a8ef 437
e2848e94 438 /* Give us a temporary reference. Some of the calls we make may
439 * try to destroy the fetch structure, and we like to know if they
440 * do
bac6d4bd 441 */
e2848e94 442 fetch = cbdataReference(fetch);
add2192d 443
444 /* Repeat this loop until we're out of data OR the state changes */
445 /* (So keep going if the state has changed and we still have data */
446 do {
62e76326 447 prevstate = fetch->state;
448
449 switch (fetch->state) {
450
451 case DIGEST_READ_REPLY:
452 retsize = peerDigestFetchReply(fetch, fetch->buf, fetch->bufofs);
453 break;
454
455 case DIGEST_READ_HEADERS:
456 retsize = peerDigestSwapInHeaders(fetch, fetch->buf, fetch->bufofs);
457 break;
458
459 case DIGEST_READ_CBLOCK:
460 retsize = peerDigestSwapInCBlock(fetch, fetch->buf, fetch->bufofs);
461 break;
462
463 case DIGEST_READ_MASK:
464 retsize = peerDigestSwapInMask(fetch, fetch->buf, fetch->bufofs);
465 break;
466
467 case DIGEST_READ_NONE:
468 break;
469
470 case DIGEST_READ_DONE:
471 goto finish;
472 break;
473
474 default:
475 fatal("Bad digest transfer mode!\n");
476 }
477
478 if (retsize < 0)
479 goto finish;
480
481 /*
482 * The returned size indicates how much of the buffer was read -
483 * so move the remainder of the buffer to the beginning
484 * and update the bufofs / bufsize
485 */
486 newsize = fetch->bufofs - retsize;
487
41d00cd3 488 memmove(fetch->buf, fetch->buf + retsize, fetch->bufofs - newsize);
62e76326 489
490 fetch->bufofs = newsize;
add2192d 491
e2848e94 492 } while (cbdataReferenceValid(fetch) && prevstate != fetch->state && fetch->bufofs > 0);
add2192d 493
494 /* Update the copy offset */
2324cda2 495 fetch->offset += receivedData.length;
add2192d 496
497 /* Schedule another copy */
fa80a8ef 498 if (cbdataReferenceValid(fetch)) {
62e76326 499 StoreIOBuffer tempBuffer;
500 tempBuffer.offset = fetch->offset;
501 tempBuffer.length = SM_PAGE_SIZE - fetch->bufofs;
502 tempBuffer.data = fetch->buf + fetch->bufofs;
503 storeClientCopy(fetch->sc, fetch->entry, tempBuffer,
504 peerDigestHandleReply, fetch);
add2192d 505 }
62e76326 506
507finish:
e2848e94 508 /* Get rid of our reference, we've finished with it for now */
509 cbdataReferenceDone(fetch);
add2192d 510}
511
add2192d 512/* wait for full http headers to be received then parse them */
513/*
514 * This routine handles parsing the reply line.
515 * If the reply line indicates an OK, the same data is thrown
516 * to SwapInHeaders(). If the reply line is a NOT_MODIFIED,
517 * we simply stop parsing.
518 */
519static int
9b7de833 520peerDigestFetchReply(void *data, char *buf, ssize_t size)
521{
e6ccf245 522 DigestFetchState *fetch = (DigestFetchState *)data;
e13ee7ad 523 PeerDigest *pd = fetch->pd;
9bc73deb 524 size_t hdr_size;
e13ee7ad 525 assert(pd && buf);
9b7de833 526 assert(!fetch->offset);
e13ee7ad 527
add2192d 528 assert(fetch->state == DIGEST_READ_REPLY);
62e76326 529
9b7de833 530 if (peerDigestFetchedEnough(fetch, buf, size, "peerDigestFetchReply"))
62e76326 531 return -1;
e13ee7ad 532
9bc73deb 533 if ((hdr_size = headersEnd(buf, size))) {
62e76326 534 HttpReply const *reply = fetch->entry->getReply();
535 assert(reply);
9b769c67
AJ
536 assert(reply->sline.status() != Http::scNone);
537 const Http::StatusCode status = reply->sline.status();
b4197865 538 debugs(72, 3, "peerDigestFetchReply: " << pd->host << " status: " << status <<
26ac0430 539 ", expires: " << (long int) reply->expires << " (" << std::showpos <<
bf8fe701 540 (int) (reply->expires - squid_curtime) << ")");
62e76326 541
542 /* this "if" is based on clientHandleIMSReply() */
543
955394ce 544 if (status == Http::scNotModified) {
62e76326 545 /* our old entry is fine */
546 assert(fetch->old_entry);
547
b248c2a3
AJ
548 if (!fetch->old_entry->mem_obj->request) {
549 fetch->old_entry->mem_obj->request = fetch->entry->mem_obj->request;
550 HTTPMSGLOCK(fetch->old_entry->mem_obj->request);
551 }
62e76326 552
553 assert(fetch->old_entry->mem_obj->request);
554
07947ad8 555 HttpReply *old_rep = (HttpReply *) fetch->old_entry->getReply();
556
06a5ae20 557 old_rep->updateOnNotModified(reply);
62e76326 558
3900307b 559 fetch->old_entry->timestampsSet();
62e76326 560
561 /* get rid of 304 reply */
562 storeUnregister(fetch->sc, fetch->entry, fetch);
563
acc5dc4c 564 fetch->entry->unlock("peerDigestFetchReply 304");
62e76326 565
566 fetch->entry = fetch->old_entry;
567
568 fetch->old_entry = NULL;
569
570 /* preserve request -- we need its size to update counters */
571 /* requestUnlink(r); */
572 /* fetch->entry->mem_obj->request = NULL; */
955394ce 573 } else if (status == Http::scOkay) {
62e76326 574 /* get rid of old entry if any */
575
576 if (fetch->old_entry) {
bf8fe701 577 debugs(72, 3, "peerDigestFetchReply: got new digest, releasing old one");
62e76326 578 storeUnregister(fetch->old_sc, fetch->old_entry, fetch);
d88e3c49 579 fetch->old_entry->releaseRequest();
acc5dc4c 580 fetch->old_entry->unlock("peerDigestFetchReply 200");
62e76326 581 fetch->old_entry = NULL;
582 }
583 } else {
584 /* some kind of a bug */
9b769c67 585 peerDigestFetchAbort(fetch, buf, reply->sline.reason());
62e76326 586 return -1; /* XXX -1 will abort stuff in ReadReply! */
587 }
588
589 /* must have a ready-to-use store entry if we got here */
590 /* can we stay with the old in-memory digest? */
955394ce 591 if (status == Http::scNotModified && fetch->pd->cd) {
62e76326 592 peerDigestFetchStop(fetch, buf, "Not modified");
593 fetch->state = DIGEST_READ_DONE;
594 } else {
595 fetch->state = DIGEST_READ_HEADERS;
596 }
9b7de833 597 } else {
62e76326 598 /* need more data, do we have space? */
599
600 if (size >= SM_PAGE_SIZE)
601 peerDigestFetchAbort(fetch, buf, "reply header too big");
9b7de833 602 }
add2192d 603
604 /* We don't want to actually ack that we've handled anything,
605 * otherwise SwapInHeaders() won't get the reply line .. */
606 return 0;
9b7de833 607}
608
609/* fetch headers from disk, pass on to SwapInCBlock */
1f140227 610int
9b7de833 611peerDigestSwapInHeaders(void *data, char *buf, ssize_t size)
612{
e6ccf245 613 DigestFetchState *fetch = (DigestFetchState *)data;
9b7de833 614 size_t hdr_size;
e13ee7ad 615
add2192d 616 assert(fetch->state == DIGEST_READ_HEADERS);
62e76326 617
9b7de833 618 if (peerDigestFetchedEnough(fetch, buf, size, "peerDigestSwapInHeaders"))
62e76326 619 return -1;
e13ee7ad 620
9d486b43 621 assert(!fetch->offset);
62e76326 622
9b7de833 623 if ((hdr_size = headersEnd(buf, size))) {
62e76326 624 assert(fetch->entry->getReply());
9b769c67 625 assert(fetch->entry->getReply()->sline.status() != Http::scNone);
62e76326 626
9b769c67 627 if (fetch->entry->getReply()->sline.status() != Http::scOkay) {
e0236918 628 debugs(72, DBG_IMPORTANT, "peerDigestSwapInHeaders: " << fetch->pd->host <<
9b769c67 629 " status " << fetch->entry->getReply()->sline.status() <<
bf8fe701 630 " got cached!");
631
62e76326 632 peerDigestFetchAbort(fetch, buf, "internal status error");
633 return -1;
634 }
635
636 fetch->state = DIGEST_READ_CBLOCK;
637 return hdr_size; /* Say how much data we read */
9b7de833 638 } else {
62e76326 639 /* need more data, do we have space? */
640
641 if (size >= SM_PAGE_SIZE) {
642 peerDigestFetchAbort(fetch, buf, "stored header too big");
643 return -1;
644 } else {
645 return 0; /* We need to read more to parse .. */
646 }
9b7de833 647 }
62e76326 648
add2192d 649 fatal("peerDigestSwapInHeaders() - shouldn't get here!\n");
0b5f04b8 650 return 0; /* keep gcc happy */
9b7de833 651}
652
1f140227 653int
9b7de833 654peerDigestSwapInCBlock(void *data, char *buf, ssize_t size)
655{
e6ccf245 656 DigestFetchState *fetch = (DigestFetchState *)data;
e13ee7ad 657
add2192d 658 assert(fetch->state == DIGEST_READ_CBLOCK);
62e76326 659
9b7de833 660 if (peerDigestFetchedEnough(fetch, buf, size, "peerDigestSwapInCBlock"))
62e76326 661 return -1;
e13ee7ad 662
e6ccf245 663 if (size >= (ssize_t)StoreDigestCBlockSize) {
62e76326 664 PeerDigest *pd = fetch->pd;
62e76326 665
e4a67a80 666 assert(pd && fetch->entry->getReply());
62e76326 667
668 if (peerDigestSetCBlock(pd, buf)) {
669 /* XXX: soon we will have variable header size */
670 /* switch to CD buffer and fetch digest guts */
671 buf = NULL;
672 assert(pd->cd->mask);
673 fetch->state = DIGEST_READ_MASK;
674 return StoreDigestCBlockSize;
675 } else {
676 peerDigestFetchAbort(fetch, buf, "invalid digest cblock");
677 return -1;
678 }
9b7de833 679 } else {
62e76326 680 /* need more data, do we have space? */
681
682 if (size >= SM_PAGE_SIZE) {
683 peerDigestFetchAbort(fetch, buf, "digest cblock too big");
684 return -1;
685 } else {
686 return 0; /* We need more data */
687 }
9b7de833 688 }
62e76326 689
add2192d 690 fatal("peerDigestSwapInCBlock(): shouldn't get here!\n");
0b5f04b8 691 return 0; /* keep gcc happy */
9b7de833 692}
693
1f140227 694int
9b7de833 695peerDigestSwapInMask(void *data, char *buf, ssize_t size)
696{
e6ccf245 697 DigestFetchState *fetch = (DigestFetchState *)data;
e13ee7ad 698 PeerDigest *pd;
699
e13ee7ad 700 pd = fetch->pd;
701 assert(pd->cd && pd->cd->mask);
9d486b43 702
add2192d 703 /*
704 * NOTENOTENOTENOTENOTE: buf doesn't point to pd->cd->mask anymore!
705 * we need to do the copy ourselves!
706 */
41d00cd3 707 memcpy(pd->cd->mask + fetch->mask_offset, buf, size);
add2192d 708
709 /* NOTE! buf points to the middle of pd->cd->mask! */
62e76326 710
add2192d 711 if (peerDigestFetchedEnough(fetch, NULL, size, "peerDigestSwapInMask"))
62e76326 712 return -1;
add2192d 713
da407def 714 fetch->mask_offset += size;
62e76326 715
57d55dfa 716 if (fetch->mask_offset >= pd->cd->mask_size) {
e4049756 717 debugs(72, 2, "peerDigestSwapInMask: Done! Got " <<
718 fetch->mask_offset << ", expected " << pd->cd->mask_size);
57d55dfa 719 assert(fetch->mask_offset == pd->cd->mask_size);
62e76326 720 assert(peerDigestFetchedEnough(fetch, NULL, 0, "peerDigestSwapInMask"));
721 return -1; /* XXX! */
e13ee7ad 722 } else {
62e76326 723 /* We always read everything, so return so */
724 return size;
9b7de833 725 }
62e76326 726
add2192d 727 fatal("peerDigestSwapInMask(): shouldn't get here!\n");
0b5f04b8 728 return 0; /* keep gcc happy */
9b7de833 729}
730
731static int
4b4cd312 732peerDigestFetchedEnough(DigestFetchState * fetch, char *buf, ssize_t size, const char *step_name)
9b7de833 733{
e13ee7ad 734 PeerDigest *pd = NULL;
8a6218c6 735 const char *host = "<unknown>"; /* peer host */
736 const char *reason = NULL; /* reason for completion */
737 const char *no_bug = NULL; /* successful completion if set */
e2848e94 738 const int pdcb_valid = cbdataReferenceValid(fetch->pd);
739 const int pcb_valid = cbdataReferenceValid(fetch->pd->peer);
e13ee7ad 740
741 /* test possible exiting conditions (the same for most steps!)
742 * cases marked with '?!' should not happen */
743
744 if (!reason) {
62e76326 745 if (!(pd = fetch->pd))
746 reason = "peer digest disappeared?!";
747
bac6d4bd 748#if DONT /* WHY NOT? /HNO */
62e76326 749
750 else if (!cbdataReferenceValid(pd))
751 reason = "invalidated peer digest?!";
752
5385c86a 753#endif
62e76326 754
755 else
b4197865 756 host = pd->host.termedBuf();
e13ee7ad 757 }
62e76326 758
e4049756 759 debugs(72, 6, step_name << ": peer " << host << ", offset: " <<
760 fetch->offset << " size: " << size << ".");
e13ee7ad 761
762 /* continue checking (with pd and host known and valid) */
62e76326 763
e13ee7ad 764 if (!reason) {
62e76326 765 if (!cbdataReferenceValid(pd->peer))
766 reason = "peer disappeared";
767 else if (size < 0)
768 reason = "swap failure";
769 else if (!fetch->entry)
770 reason = "swap aborted?!";
771 else if (EBIT_TEST(fetch->entry->flags, ENTRY_ABORTED))
772 reason = "swap aborted";
e13ee7ad 773 }
62e76326 774
e13ee7ad 775 /* continue checking (maybe-successful eof case) */
776 if (!reason && !size) {
62e76326 777 if (!pd->cd)
778 reason = "null digest?!";
47f6e231 779 else if (fetch->mask_offset != (int)pd->cd->mask_size)
62e76326 780 reason = "premature end of digest?!";
781 else if (!peerDigestUseful(pd))
782 reason = "useless digest";
783 else
784 reason = no_bug = "success";
e13ee7ad 785 }
62e76326 786
e13ee7ad 787 /* finish if we have a reason */
9b7de833 788 if (reason) {
62e76326 789 const int level = strstr(reason, "?!") ? 1 : 3;
bf8fe701 790 debugs(72, level, "" << step_name << ": peer " << host << ", exiting after '" << reason << "'");
62e76326 791 peerDigestReqFinish(fetch, buf,
792 1, pdcb_valid, pcb_valid, reason, !no_bug);
e13ee7ad 793 } else {
62e76326 794 /* paranoid check */
795 assert(pdcb_valid && pcb_valid);
e13ee7ad 796 }
62e76326 797
e13ee7ad 798 return reason != NULL;
799}
800
551e60a9 801/* call this when all callback data is valid and fetch must be stopped but
802 * no error has occurred (e.g. we received 304 reply and reuse old digest) */
803static void
804peerDigestFetchStop(DigestFetchState * fetch, char *buf, const char *reason)
805{
806 assert(reason);
b4197865 807 debugs(72, 2, "peerDigestFetchStop: peer " << fetch->pd->host << ", reason: " << reason);
551e60a9 808 peerDigestReqFinish(fetch, buf, 1, 1, 1, reason, 0);
809}
810
e13ee7ad 811/* call this when all callback data is valid but something bad happened */
812static void
813peerDigestFetchAbort(DigestFetchState * fetch, char *buf, const char *reason)
814{
551e60a9 815 assert(reason);
b4197865 816 debugs(72, 2, "peerDigestFetchAbort: peer " << fetch->pd->host << ", reason: " << reason);
e13ee7ad 817 peerDigestReqFinish(fetch, buf, 1, 1, 1, reason, 1);
818}
819
820/* complete the digest transfer, update stats, unlock/release everything */
821static void
822peerDigestReqFinish(DigestFetchState * fetch, char *buf,
62e76326 823 int fcb_valid, int pdcb_valid, int pcb_valid,
824 const char *reason, int err)
e13ee7ad 825{
826 assert(reason);
827
828 /* must go before peerDigestPDFinish */
62e76326 829
e13ee7ad 830 if (pdcb_valid) {
be4d35dc 831 fetch->pd->flags.requested = false;
62e76326 832 fetch->pd->req_result = reason;
e13ee7ad 833 }
62e76326 834
e13ee7ad 835 /* schedule next check if peer is still out there */
836 if (pcb_valid) {
62e76326 837 PeerDigest *pd = fetch->pd;
838
839 if (err) {
840 pd->times.retry_delay = peerDigestIncDelay(pd);
841 peerDigestSetCheck(pd, pd->times.retry_delay);
842 } else {
843 pd->times.retry_delay = 0;
844 peerDigestSetCheck(pd, peerDigestNewDelay(fetch->entry));
845 }
9b7de833 846 }
62e76326 847
e13ee7ad 848 /* note: order is significant */
849 if (fcb_valid)
62e76326 850 peerDigestFetchSetStats(fetch);
851
e13ee7ad 852 if (pdcb_valid)
62e76326 853 peerDigestPDFinish(fetch, pcb_valid, err);
854
e13ee7ad 855 if (fcb_valid)
62e76326 856 peerDigestFetchFinish(fetch, err);
9b7de833 857}
858
e13ee7ad 859/* destroys digest if peer disappeared
860 * must be called only when fetch and pd cbdata are valid */
9b7de833 861static void
e13ee7ad 862peerDigestPDFinish(DigestFetchState * fetch, int pcb_valid, int err)
9b7de833 863{
e13ee7ad 864 PeerDigest *pd = fetch->pd;
b4197865 865 const char *host = pd->host.termedBuf();
e13ee7ad 866
867 pd->times.received = squid_curtime;
868 pd->times.req_delay = fetch->resp_time;
8a6218c6 869 kb_incr(&pd->stats.sent.kbytes, (size_t) fetch->sent.bytes);
870 kb_incr(&pd->stats.recv.kbytes, (size_t) fetch->recv.bytes);
e13ee7ad 871 pd->stats.sent.msgs += fetch->sent.msg;
872 pd->stats.recv.msgs += fetch->recv.msg;
873
874 if (err) {
e0236918 875 debugs(72, DBG_IMPORTANT, "" << (pcb_valid ? "temporary " : "" ) << "disabling (" << pd->req_result << ") digest from " << host);
62e76326 876
877 if (pd->cd) {
878 cacheDigestDestroy(pd->cd);
879 pd->cd = NULL;
880 }
881
be4d35dc 882 pd->flags.usable = false;
62e76326 883
884 if (!pcb_valid)
885 peerDigestNotePeerGone(pd);
e13ee7ad 886 } else {
62e76326 887 assert(pcb_valid);
888
be4d35dc 889 pd->flags.usable = true;
e13ee7ad 890
62e76326 891 /* XXX: ugly condition, but how? */
e13ee7ad 892
62e76326 893 if (fetch->entry->store_status == STORE_OK)
bf8fe701 894 debugs(72, 2, "re-used old digest from " << host);
62e76326 895 else
bf8fe701 896 debugs(72, 2, "received valid digest from " << host);
d1cdaa16 897 }
62e76326 898
fa80a8ef 899 cbdataReferenceDone(fetch->pd);
e13ee7ad 900}
901
902/* free fetch state structures
903 * must be called only when fetch cbdata is valid */
904static void
905peerDigestFetchFinish(DigestFetchState * fetch, int err)
906{
907 assert(fetch->entry && fetch->request);
908
9b7de833 909 if (fetch->old_entry) {
48e7baac 910 debugs(72, 3, "peerDigestFetchFinish: deleting old entry");
8121ba82 911 storeUnregister(fetch->old_sc, fetch->old_entry, fetch);
d88e3c49 912 fetch->old_entry->releaseRequest();
acc5dc4c 913 fetch->old_entry->unlock("peerDigestFetchFinish old");
62e76326 914 fetch->old_entry = NULL;
9b7de833 915 }
62e76326 916
1543ab6c 917 /* update global stats */
83704487 918 kb_incr(&statCounter.cd.kbytes_sent, (size_t) fetch->sent.bytes);
62e76326 919
83704487 920 kb_incr(&statCounter.cd.kbytes_recv, (size_t) fetch->recv.bytes);
62e76326 921
83704487 922 statCounter.cd.msgs_sent += fetch->sent.msg;
62e76326 923
83704487 924 statCounter.cd.msgs_recv += fetch->recv.msg;
e13ee7ad 925
1543ab6c 926 /* unlock everything */
06d2839d 927 storeUnregister(fetch->sc, fetch->entry, fetch);
62e76326 928
acc5dc4c 929 fetch->entry->unlock("peerDigestFetchFinish new");
62e76326 930
6dd9f4bd 931 HTTPMSGUNLOCK(fetch->request);
62e76326 932
9b7de833 933 fetch->entry = NULL;
62e76326 934
3855c318 935 assert(fetch->pd == NULL);
62e76326 936
9b7de833 937 cbdataFree(fetch);
9b7de833 938}
939
e13ee7ad 940/* calculate fetch stats after completion */
941static void
942peerDigestFetchSetStats(DigestFetchState * fetch)
943{
944 MemObject *mem;
945 assert(fetch->entry && fetch->request);
946
947 mem = fetch->entry->mem_obj;
948 assert(mem);
949
950 /* XXX: outgoing numbers are not precise */
951 /* XXX: we must distinguish between 304 hits and misses here */
e11fe29a 952 fetch->sent.bytes = fetch->request->prefixLen();
528b2c61 953 /* XXX: this is slightly wrong: we don't KNOW that the entire memobject
954 * was fetched. We only know how big it is
955 */
956 fetch->recv.bytes = mem->size();
e13ee7ad 957 fetch->sent.msg = fetch->recv.msg = 1;
958 fetch->expires = fetch->entry->expires;
959 fetch->resp_time = squid_curtime - fetch->start_time;
960
bf8fe701 961 debugs(72, 3, "peerDigestFetchFinish: recv " << fetch->recv.bytes <<
962 " bytes in " << (int) fetch->resp_time << " secs");
963
964 debugs(72, 3, "peerDigestFetchFinish: expires: " <<
965 (long int) fetch->expires << " (" << std::showpos <<
966 (int) (fetch->expires - squid_curtime) << "), lmt: " <<
967 std::noshowpos << (long int) fetch->entry->lastmod << " (" <<
968 std::showpos << (int) (fetch->entry->lastmod - squid_curtime) <<
969 ")");
970
e13ee7ad 971}
972
9b7de833 973static int
8a6218c6 974peerDigestSetCBlock(PeerDigest * pd, const char *buf)
9b7de833 975{
976 StoreDigestCBlock cblock;
977 int freed_size = 0;
b4197865 978 const char *host = pd->host.termedBuf();
e13ee7ad 979
41d00cd3 980 memcpy(&cblock, buf, sizeof(cblock));
9b7de833 981 /* network -> host conversions */
982 cblock.ver.current = ntohs(cblock.ver.current);
983 cblock.ver.required = ntohs(cblock.ver.required);
984 cblock.capacity = ntohl(cblock.capacity);
985 cblock.count = ntohl(cblock.count);
986 cblock.del_count = ntohl(cblock.del_count);
987 cblock.mask_size = ntohl(cblock.mask_size);
bf8fe701 988 debugs(72, 2, "got digest cblock from " << host << "; ver: " <<
989 (int) cblock.ver.current << " (req: " << (int) cblock.ver.required <<
990 ")");
991
26ac0430
AJ
992 debugs(72, 2, "\t size: " <<
993 cblock.mask_size << " bytes, e-cnt: " <<
994 cblock.count << ", e-util: " <<
bf8fe701 995 xpercentInt(cblock.count, cblock.capacity) << "%" );
6106c6fc 996 /* check version requirements (both ways) */
62e76326 997
9b7de833 998 if (cblock.ver.required > CacheDigestVer.current) {
e0236918 999 debugs(72, DBG_IMPORTANT, "" << host << " digest requires version " <<
bf8fe701 1000 cblock.ver.required << "; have: " << CacheDigestVer.current);
1001
62e76326 1002 return 0;
9b7de833 1003 }
62e76326 1004
6106c6fc 1005 if (cblock.ver.current < CacheDigestVer.required) {
e0236918 1006 debugs(72, DBG_IMPORTANT, "" << host << " digest is version " <<
bf8fe701 1007 cblock.ver.current << "; we require: " <<
1008 CacheDigestVer.required);
1009
62e76326 1010 return 0;
6106c6fc 1011 }
62e76326 1012
9b7de833 1013 /* check consistency */
4b4cd312 1014 if (cblock.ver.required > cblock.ver.current ||
62e76326 1015 cblock.mask_size <= 0 || cblock.capacity <= 0 ||
1016 cblock.bits_per_entry <= 0 || cblock.hash_func_count <= 0) {
fa84c01d 1017 debugs(72, DBG_CRITICAL, "" << host << " digest cblock is corrupted.");
62e76326 1018 return 0;
9b7de833 1019 }
62e76326 1020
d1cdaa16 1021 /* check consistency further */
e6ccf245 1022 if ((size_t)cblock.mask_size != cacheDigestCalcMaskSize(cblock.capacity, cblock.bits_per_entry)) {
fa84c01d 1023 debugs(72, DBG_CRITICAL, host << " digest cblock is corrupted " <<
e4049756 1024 "(mask size mismatch: " << cblock.mask_size << " ? " <<
1025 cacheDigestCalcMaskSize(cblock.capacity, cblock.bits_per_entry)
1026 << ").");
62e76326 1027 return 0;
d1cdaa16 1028 }
62e76326 1029
d1cdaa16 1030 /* there are some things we cannot do yet */
1031 if (cblock.hash_func_count != CacheDigestHashFuncCount) {
fa84c01d 1032 debugs(72, DBG_CRITICAL, "" << host << " digest: unsupported #hash functions: " <<
bf8fe701 1033 cblock.hash_func_count << " ? " << CacheDigestHashFuncCount << ".");
62e76326 1034 return 0;
d1cdaa16 1035 }
62e76326 1036
9b7de833 1037 /*
1038 * no cblock bugs below this point
1039 */
1040 /* check size changes */
e6ccf245 1041 if (pd->cd && cblock.mask_size != (ssize_t)pd->cd->mask_size) {
e4049756 1042 debugs(72, 2, host << " digest changed size: " << cblock.mask_size <<
1043 " -> " << pd->cd->mask_size);
62e76326 1044 freed_size = pd->cd->mask_size;
1045 cacheDigestDestroy(pd->cd);
1046 pd->cd = NULL;
9b7de833 1047 }
62e76326 1048
e13ee7ad 1049 if (!pd->cd) {
26ac0430
AJ
1050 debugs(72, 2, "creating " << host << " digest; size: " << cblock.mask_size << " (" <<
1051 std::showpos << (int) (cblock.mask_size - freed_size) << ") bytes");
62e76326 1052 pd->cd = cacheDigestCreate(cblock.capacity, cblock.bits_per_entry);
1053
1054 if (cblock.mask_size >= freed_size)
1055 kb_incr(&statCounter.cd.memory, cblock.mask_size - freed_size);
9b7de833 1056 }
62e76326 1057
e13ee7ad 1058 assert(pd->cd);
9b7de833 1059 /* these assignments leave us in an inconsistent state until we finish reading the digest */
e13ee7ad 1060 pd->cd->count = cblock.count;
1061 pd->cd->del_count = cblock.del_count;
9b7de833 1062 return 1;
1063}
1064
9b7de833 1065static int
8a6218c6 1066peerDigestUseful(const PeerDigest * pd)
9b7de833 1067{
d1cdaa16 1068 /* TODO: we should calculate the prob of a false hit instead of bit util */
e13ee7ad 1069 const int bit_util = cacheDigestBitUtil(pd->cd);
62e76326 1070
e13ee7ad 1071 if (bit_util > 65) {
fa84c01d 1072 debugs(72, DBG_CRITICAL, "Warning: " << pd->host <<
bf8fe701 1073 " peer digest has too many bits on (" << bit_util << "%%).");
1074
62e76326 1075 return 0;
9b7de833 1076 }
62e76326 1077
9b7de833 1078 return 1;
1079}
7f6eb0fe 1080
e13ee7ad 1081static int
1082saneDiff(time_t diff)
1083{
3db6fb54 1084 return abs((int) diff) > squid_curtime / 2 ? 0 : diff;
e13ee7ad 1085}
1086
1087void
8a6218c6 1088peerDigestStatsReport(const PeerDigest * pd, StoreEntry * e)
e13ee7ad 1089{
1090#define f2s(flag) (pd->flags.flag ? "yes" : "no")
38650cc8 1091#define appendTime(tm) storeAppendPrintf(e, "%s\t %10ld\t %+d\t %+d\n", \
1092 ""#tm, (long int)pd->times.tm, \
e13ee7ad 1093 saneDiff(pd->times.tm - squid_curtime), \
1094 saneDiff(pd->times.tm - pd->times.initialized))
1095
e13ee7ad 1096 assert(pd);
1097
b4197865 1098 const char *host = pd->host.termedBuf();
e13ee7ad 1099 storeAppendPrintf(e, "\npeer digest from %s\n", host);
1100
1101 cacheDigestGuessStatsReport(&pd->stats.guess, e, host);
1102
1103 storeAppendPrintf(e, "\nevent\t timestamp\t secs from now\t secs from init\n");
1104 appendTime(initialized);
1105 appendTime(needed);
1106 appendTime(requested);
1107 appendTime(received);
1108 appendTime(next_check);
1109
1110 storeAppendPrintf(e, "peer digest state:\n");
1111 storeAppendPrintf(e, "\tneeded: %3s, usable: %3s, requested: %3s\n",
62e76326 1112 f2s(needed), f2s(usable), f2s(requested));
e13ee7ad 1113 storeAppendPrintf(e, "\n\tlast retry delay: %d secs\n",
62e76326 1114 (int) pd->times.retry_delay);
e13ee7ad 1115 storeAppendPrintf(e, "\tlast request response time: %d secs\n",
62e76326 1116 (int) pd->times.req_delay);
e13ee7ad 1117 storeAppendPrintf(e, "\tlast request result: %s\n",
62e76326 1118 pd->req_result ? pd->req_result : "(none)");
e13ee7ad 1119
1120 storeAppendPrintf(e, "\npeer digest traffic:\n");
1121 storeAppendPrintf(e, "\trequests sent: %d, volume: %d KB\n",
62e76326 1122 pd->stats.sent.msgs, (int) pd->stats.sent.kbytes.kb);
e13ee7ad 1123 storeAppendPrintf(e, "\treplies recv: %d, volume: %d KB\n",
62e76326 1124 pd->stats.recv.msgs, (int) pd->stats.recv.kbytes.kb);
e13ee7ad 1125
1126 storeAppendPrintf(e, "\npeer digest structure:\n");
62e76326 1127
e13ee7ad 1128 if (pd->cd)
62e76326 1129 cacheDigestReport(pd->cd, host, e);
e13ee7ad 1130 else
62e76326 1131 storeAppendPrintf(e, "\tno in-memory copy\n");
e13ee7ad 1132}
1133
7f6eb0fe 1134#endif