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