]> git.ipfire.org Git - thirdparty/squid.git/blame - src/peer_digest.cc
format error fixes
[thirdparty/squid.git] / src / peer_digest.cc
CommitLineData
9b7de833 1
2/*
f66a9ef4 3 * $Id: peer_digest.cc,v 1.81 2001/04/14 00:25:18 hno Exp $
9b7de833 4 *
5 * DEBUG: section 72 Peer Digest Routines
6 * AUTHOR: Alex Rousskov
7 *
2b6662ba 8 * SQUID Web Proxy Cache http://www.squid-cache.org/
e25c139f 9 * ----------------------------------------------------------
9b7de833 10 *
2b6662ba 11 * Squid is the result of efforts by numerous individuals from
12 * the Internet community; see the CONTRIBUTORS file for full
13 * details. Many organizations have provided support for Squid's
14 * development; see the SPONSORS file for full details. Squid is
15 * Copyrighted (C) 2001 by the Regents of the University of
16 * California; see the COPYRIGHT file for full details. Squid
17 * incorporates software developed and/or copyrighted by other
18 * sources; see the CREDITS file for full details.
9b7de833 19 *
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
cbdec147 32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
e25c139f 33 *
9b7de833 34 */
35
36#include "squid.h"
37
6cfa8966 38#if USE_CACHE_DIGESTS
7a2e5bb5 39
9b7de833 40/* local types */
41
42/* local prototypes */
e13ee7ad 43static time_t peerDigestIncDelay(const PeerDigest * pd);
44static time_t peerDigestNewDelay(const StoreEntry * e);
45static void peerDigestSetCheck(PeerDigest * pd, time_t delay);
eb16313f 46static void peerDigestClean(PeerDigest *);
e13ee7ad 47static EVH peerDigestCheck;
48static void peerDigestRequest(PeerDigest * pd);
c68e9c6b 49static STCB peerDigestFetchReply;
c68e9c6b 50static STCB peerDigestSwapInHeaders;
51static STCB peerDigestSwapInCBlock;
da407def 52static STCB peerDigestSwapInMask;
4b4cd312 53static int peerDigestFetchedEnough(DigestFetchState * fetch, char *buf, ssize_t size, const char *step_name);
551e60a9 54static void peerDigestFetchStop(DigestFetchState * fetch, char *buf, const char *reason);
e13ee7ad 55static void peerDigestFetchAbort(DigestFetchState * fetch, char *buf, const char *reason);
56static void peerDigestReqFinish(DigestFetchState * fetch, char *buf, int, int, int, const char *reason, int err);
57static void peerDigestPDFinish(DigestFetchState * fetch, int pcb_valid, int err);
58static void peerDigestFetchFinish(DigestFetchState * fetch, int err);
59static void peerDigestFetchSetStats(DigestFetchState * fetch);
60static int peerDigestSetCBlock(PeerDigest * pd, const char *buf);
61static int peerDigestUseful(const PeerDigest * pd);
395b813e 62
9b7de833 63
64/* local constants */
9d486b43 65
9b7de833 66#define StoreDigestCBlockSize sizeof(StoreDigestCBlock)
67
e13ee7ad 68/* min interval for requesting digests from a given peer */
69static const time_t PeerDigestReqMinGap = 5 * 60; /* seconds */
70/* min interval for requesting digests (cumulative request stream) */
71static const time_t GlobDigestReqMinGap = 1 * 60; /* seconds */
bd890734 72
73/* local vars */
9d486b43 74
8a6218c6 75static time_t pd_last_req_time = 0; /* last call to Check */
9b7de833 76
e13ee7ad 77/* initialize peer digest */
78static void
8a6218c6 79peerDigestInit(PeerDigest * pd, peer * p)
9b7de833 80{
e13ee7ad 81 assert(pd && p);
82
83 memset(pd, 0, sizeof(*pd));
84 pd->peer = p;
85 /* if peer disappears, we will know it's name */
86 stringInit(&pd->host, p->host);
87
88 pd->times.initialized = squid_curtime;
9b7de833 89}
90
4b4cd312 91static void
8a6218c6 92peerDigestClean(PeerDigest * pd)
9b7de833 93{
e13ee7ad 94 assert(pd);
95 if (pd->cd)
96 cacheDigestDestroy(pd->cd);
97 stringClean(&pd->host);
9b7de833 98}
99
28c60158 100CBDATA_TYPE(PeerDigest);
101
e13ee7ad 102/* allocate new peer digest, call Init, and lock everything */
103PeerDigest *
8a6218c6 104peerDigestCreate(peer * p)
e13ee7ad 105{
106 PeerDigest *pd;
8a6218c6 107 assert(p);
e13ee7ad 108
28c60158 109 CBDATA_INIT_TYPE(PeerDigest);
72711e31 110 pd = cbdataAlloc(PeerDigest);
e13ee7ad 111 peerDigestInit(pd, p);
8a6218c6 112 cbdataLock(pd->peer); /* we will use the peer */
e13ee7ad 113
114 return pd;
115}
116
117/* call Clean and free/unlock everything */
2d72d4fd 118static void
8a6218c6 119peerDigestDestroy(PeerDigest * pd)
e13ee7ad 120{
fcbbccfe 121 peer *p;
e13ee7ad 122 assert(pd);
e13ee7ad 123
fcbbccfe 124 p = pd->peer;
e13ee7ad 125 pd->peer = NULL;
fcbbccfe 126 /* inform peer (if any) that we are gone */
127 if (cbdataValid(p))
128 peerNoteDigestGone(p);
48c27a0e 129 cbdataUnlock(p); /* must unlock, valid or not */
e13ee7ad 130
131 peerDigestClean(pd);
132 cbdataFree(pd);
133}
134
135/* called by peer to indicate that somebody actually needs this digest */
136void
8a6218c6 137peerDigestNeeded(PeerDigest * pd)
e13ee7ad 138{
139 assert(pd);
140 assert(!pd->flags.needed);
141 assert(!pd->cd);
142
143 pd->flags.needed = 1;
144 pd->times.needed = squid_curtime;
8a6218c6 145 peerDigestSetCheck(pd, 0); /* check asap */
e13ee7ad 146}
147
148/* currently we do not have a reason to disable without destroying */
8a6218c6 149#if FUTURE_CODE
395b813e 150/* disables peer for good */
151static void
8a6218c6 152peerDigestDisable(PeerDigest * pd)
395b813e 153{
8a6218c6 154 debug(72, 2) ("peerDigestDisable: peer %s disabled for good\n",
e13ee7ad 155 strBuf(pd->host));
156 pd->times.disabled = squid_curtime;
8a6218c6 157 pd->times.next_check = -1; /* never */
e13ee7ad 158 pd->flags.usable = 0;
159
160 if (pd->cd) {
161 cacheDigestDestroy(pd->cd);
162 pd->cd = NULL;
163 }
164 /* we do not destroy the pd itself to preserve its "history" and stats */
395b813e 165}
e13ee7ad 166#endif
395b813e 167
e13ee7ad 168/* increment retry delay [after an unsuccessful attempt] */
395b813e 169static time_t
8a6218c6 170peerDigestIncDelay(const PeerDigest * pd)
395b813e 171{
e13ee7ad 172 assert(pd);
173 return pd->times.retry_delay > 0 ?
8a6218c6 174 2 * pd->times.retry_delay : /* exponential backoff */
175 PeerDigestReqMinGap; /* minimal delay */
395b813e 176}
177
e13ee7ad 178/* artificially increases Expires: setting to avoid race conditions
179 * returns the delay till that [increased] expiration time */
00485c29 180static time_t
e13ee7ad 181peerDigestNewDelay(const StoreEntry * e)
00485c29 182{
e13ee7ad 183 assert(e);
00485c29 184 if (e->expires > 0)
e13ee7ad 185 return e->expires + PeerDigestReqMinGap - squid_curtime;
186 return PeerDigestReqMinGap;
00485c29 187}
188
e13ee7ad 189/* registers next digest verification */
395b813e 190static void
e13ee7ad 191peerDigestSetCheck(PeerDigest * pd, time_t delay)
395b813e 192{
e13ee7ad 193 eventAdd("peerDigestCheck", peerDigestCheck, pd, (double) delay, 1);
194 pd->times.next_check = squid_curtime + delay;
195 debug(72, 3) ("peerDigestSetCheck: will check peer %s in %d secs\n",
196 strBuf(pd->host), delay);
197}
198
5385c86a 199/*
200 * called when peer is about to disappear or have already disappeared
201 */
e13ee7ad 202void
8a6218c6 203peerDigestNotePeerGone(PeerDigest * pd)
204{
e13ee7ad 205 if (pd->flags.requested) {
5385c86a 206 debug(72, 2) ("peerDigest: peer %s gone, will destroy after fetch.\n",
207 strBuf(pd->host));
e13ee7ad 208 /* do nothing now, the fetching chain will notice and take action */
395b813e 209 } else {
5385c86a 210 debug(72, 2) ("peerDigest: peer %s is gone, destroying now.\n",
211 strBuf(pd->host));
e13ee7ad 212 peerDigestDestroy(pd);
395b813e 213 }
214}
215
e13ee7ad 216/* callback for eventAdd() (with peer digest locked)
217 * request new digest if our copy is too old or if we lack one;
218 * schedule next check otherwise */
9b7de833 219static void
e13ee7ad 220peerDigestCheck(void *data)
9b7de833 221{
e13ee7ad 222 PeerDigest *pd = data;
223 time_t req_time;
224
48c27a0e 225 /*
226 * you can't assert(cbdataValid(pd)) -- if its not valid this
227 * function never gets called
228 */
e13ee7ad 229 assert(!pd->flags.requested);
5d9bb360 230
8a6218c6 231 pd->times.next_check = 0; /* unknown */
e13ee7ad 232
233 if (!cbdataValid(pd->peer)) {
234 peerDigestNotePeerGone(pd);
235 return;
9b7de833 236 }
e13ee7ad 237 debug(72, 3) ("peerDigestCheck: peer %s:%d\n", pd->peer->host, pd->peer->http_port);
238 debug(72, 3) ("peerDigestCheck: time: %d, last received: %d (%+d)\n",
8a6218c6 239 squid_curtime, pd->times.received, (squid_curtime - pd->times.received));
e13ee7ad 240
241 /* decide when we should send the request:
242 * request now unless too close to other requests */
243 req_time = squid_curtime;
244
245 /* per-peer limit */
246 if (req_time - pd->times.received < PeerDigestReqMinGap) {
247 debug(72, 2) ("peerDigestCheck: %s, avoiding close peer requests (%d < %d secs).\n",
8a6218c6 248 strBuf(pd->host), req_time - pd->times.received,
e13ee7ad 249 PeerDigestReqMinGap);
250 req_time = pd->times.received + PeerDigestReqMinGap;
bd890734 251 }
e13ee7ad 252 /* global limit */
253 if (req_time - pd_last_req_time < GlobDigestReqMinGap) {
254 debug(72, 2) ("peerDigestCheck: %s, avoiding close requests (%d < %d secs).\n",
255 strBuf(pd->host), req_time - pd_last_req_time,
256 GlobDigestReqMinGap);
257 req_time = pd_last_req_time + GlobDigestReqMinGap;
9b7de833 258 }
e13ee7ad 259 if (req_time <= squid_curtime)
8a6218c6 260 peerDigestRequest(pd); /* will set pd->flags.requested */
e13ee7ad 261 else
262 peerDigestSetCheck(pd, req_time - squid_curtime);
9b7de833 263}
264
2812146b 265CBDATA_TYPE(DigestFetchState);
266
e13ee7ad 267/* ask store for a digest */
9b7de833 268static void
e13ee7ad 269peerDigestRequest(PeerDigest * pd)
9b7de833 270{
e13ee7ad 271 peer *p = pd->peer;
9b7de833 272 StoreEntry *e, *old_e;
273 char *url;
9d486b43 274 const cache_key *key;
9b7de833 275 request_t *req;
276 DigestFetchState *fetch = NULL;
e13ee7ad 277
278 pd->req_result = NULL;
279 pd->flags.requested = 1;
280
9b7de833 281 /* compute future request components */
7e3ce7b9 282 if (p->digest_url)
283 url = xstrdup(p->digest_url);
284 else
285 url = internalRemoteUri(p->host, p->http_port,
286 "/squid-internal-periodic/", StoreDigestFileName);
287
50a97cee 288 req = urlParse(METHOD_GET, url);
e13ee7ad 289 assert(req);
f66a9ef4 290 key = storeKeyPublicByRequest(req);
291 debug(72, 2) ("peerDigestRequest: %s key: %s\n", url, storeKeyText(key));
e13ee7ad 292
9b7de833 293 /* add custom headers */
2246b732 294 assert(!req->header.len);
4820f62b 295 httpHeaderPutStr(&req->header, HDR_ACCEPT, StoreDigestMimeStr);
296 httpHeaderPutStr(&req->header, HDR_ACCEPT, "text/html");
9bc73deb 297 if (p->login)
298 xstrncpy(req->login, p->login, MAX_LOGIN_SZ);
9b7de833 299 /* create fetch state structure */
2812146b 300 CBDATA_INIT_TYPE(DigestFetchState);
72711e31 301 fetch = cbdataAlloc(DigestFetchState);
903c39e4 302 fetch->request = requestLink(req);
e13ee7ad 303 fetch->pd = pd;
304 fetch->offset = 0;
305
306 /* update timestamps */
9b7de833 307 fetch->start_time = squid_curtime;
e13ee7ad 308 pd->times.requested = squid_curtime;
309 pd_last_req_time = squid_curtime;
310
92695e5e 311 req->flags.cachable = 1;
9b7de833 312 /* the rest is based on clientProcessExpired() */
92695e5e 313 req->flags.refresh = 1;
9d486b43 314 old_e = fetch->old_entry = storeGet(key);
9b7de833 315 if (old_e) {
4b4cd312 316 debug(72, 5) ("peerDigestRequest: found old entry\n");
9b7de833 317 storeLockObject(old_e);
318 storeCreateMemObject(old_e, url, url);
06d2839d 319 fetch->old_sc = storeClientListAdd(old_e, fetch);
9b7de833 320 }
321 e = fetch->entry = storeCreateEntry(url, url, req->flags, req->method);
e13ee7ad 322 assert(EBIT_TEST(e->flags, KEY_PRIVATE));
06d2839d 323 fetch->sc = storeClientListAdd(e, fetch);
9b7de833 324 /* set lastmod to trigger IMS request if possible */
325 if (old_e)
326 e->lastmod = old_e->lastmod;
e13ee7ad 327
9b7de833 328 /* push towards peer cache */
e13ee7ad 329 debug(72, 3) ("peerDigestRequest: forwarding to fwdStart...\n");
7e3ce7b9 330 fwdStart(-1, e, req);
e13ee7ad 331 cbdataLock(fetch);
332 cbdataLock(fetch->pd);
06d2839d 333 storeClientCopy(fetch->sc, e, 0, 0, 4096, memAllocate(MEM_4K_BUF),
9b7de833 334 peerDigestFetchReply, fetch);
335}
336
e13ee7ad 337/* wait for full http headers to be received then parse them */
9b7de833 338static void
339peerDigestFetchReply(void *data, char *buf, ssize_t size)
340{
341 DigestFetchState *fetch = data;
e13ee7ad 342 PeerDigest *pd = fetch->pd;
9bc73deb 343 size_t hdr_size;
e13ee7ad 344 assert(pd && buf);
9b7de833 345 assert(!fetch->offset);
e13ee7ad 346
9b7de833 347 if (peerDigestFetchedEnough(fetch, buf, size, "peerDigestFetchReply"))
348 return;
e13ee7ad 349
9bc73deb 350 if ((hdr_size = headersEnd(buf, size))) {
9b7de833 351 http_status status;
352 HttpReply *reply = fetch->entry->mem_obj->reply;
353 assert(reply);
9bc73deb 354 httpReplyParse(reply, buf, hdr_size);
9b7de833 355 status = reply->sline.status;
e13ee7ad 356 debug(72, 3) ("peerDigestFetchReply: %s status: %d, expires: %d (%+d)\n",
357 strBuf(pd->host), status,
8a6218c6 358 reply->expires, reply->expires - squid_curtime);
e13ee7ad 359
9b7de833 360 /* this "if" is based on clientHandleIMSReply() */
361 if (status == HTTP_NOT_MODIFIED) {
362 request_t *r = NULL;
363 /* our old entry is fine */
364 assert(fetch->old_entry);
365 if (!fetch->old_entry->mem_obj->request)
366 fetch->old_entry->mem_obj->request = r =
2920225f 367 requestLink(fetch->entry->mem_obj->request);
368 assert(fetch->old_entry->mem_obj->request);
9b7de833 369 httpReplyUpdateOnNotModified(fetch->old_entry->mem_obj->reply, reply);
370 storeTimestampsSet(fetch->old_entry);
371 /* get rid of 304 reply */
06d2839d 372 storeUnregister(fetch->sc, fetch->entry, fetch);
9b7de833 373 storeUnlockObject(fetch->entry);
374 fetch->entry = fetch->old_entry;
375 fetch->old_entry = NULL;
2920225f 376 /* preserve request -- we need its size to update counters */
377 /* requestUnlink(r); */
378 /* fetch->entry->mem_obj->request = NULL; */
4b4cd312 379 } else if (status == HTTP_OK) {
9b7de833 380 /* get rid of old entry if any */
381 if (fetch->old_entry) {
e13ee7ad 382 debug(72, 3) ("peerDigestFetchReply: got new digest, releasing old one\n");
06d2839d 383 storeUnregister(fetch->old_sc, fetch->old_entry, fetch);
9b7de833 384 storeReleaseRequest(fetch->old_entry);
385 storeUnlockObject(fetch->old_entry);
386 fetch->old_entry = NULL;
387 }
388 } else {
389 /* some kind of a bug */
e13ee7ad 390 peerDigestFetchAbort(fetch, buf, httpStatusLineReason(&reply->sline));
9b7de833 391 return;
392 }
393 /* must have a ready-to-use store entry if we got here */
e13ee7ad 394 /* can we stay with the old in-memory digest? */
395 if (status == HTTP_NOT_MODIFIED && fetch->pd->cd)
551e60a9 396 peerDigestFetchStop(fetch, buf, "Not modified");
9b7de833 397 else
06d2839d 398 storeClientCopy(fetch->sc, fetch->entry, /* have to swap in */
9b7de833 399 0, 0, SM_PAGE_SIZE, buf, peerDigestSwapInHeaders, fetch);
9b7de833 400 } else {
401 /* need more data, do we have space? */
402 if (size >= SM_PAGE_SIZE)
e13ee7ad 403 peerDigestFetchAbort(fetch, buf, "reply header too big");
9b7de833 404 else
06d2839d 405 storeClientCopy(fetch->sc, fetch->entry, size, 0, SM_PAGE_SIZE, buf,
9b7de833 406 peerDigestFetchReply, fetch);
407 }
408}
409
410/* fetch headers from disk, pass on to SwapInCBlock */
411static void
412peerDigestSwapInHeaders(void *data, char *buf, ssize_t size)
413{
414 DigestFetchState *fetch = data;
9b7de833 415 size_t hdr_size;
e13ee7ad 416
9b7de833 417 if (peerDigestFetchedEnough(fetch, buf, size, "peerDigestSwapInHeaders"))
418 return;
e13ee7ad 419
9d486b43 420 assert(!fetch->offset);
9b7de833 421 if ((hdr_size = headersEnd(buf, size))) {
422 assert(fetch->entry->mem_obj->reply);
423 if (!fetch->entry->mem_obj->reply->sline.status)
9bc73deb 424 httpReplyParse(fetch->entry->mem_obj->reply, buf, hdr_size);
dba2bbcd 425 if (fetch->entry->mem_obj->reply->sline.status != HTTP_OK) {
426 debug(72, 1) ("peerDigestSwapInHeaders: %s status %d got cached!\n",
e13ee7ad 427 strBuf(fetch->pd->host), fetch->entry->mem_obj->reply->sline.status);
428 peerDigestFetchAbort(fetch, buf, "internal status error");
dba2bbcd 429 return;
430 }
9b7de833 431 fetch->offset += hdr_size;
06d2839d 432 storeClientCopy(fetch->sc, fetch->entry, size, fetch->offset,
9b7de833 433 SM_PAGE_SIZE, buf,
434 peerDigestSwapInCBlock, fetch);
435 } else {
436 /* need more data, do we have space? */
437 if (size >= SM_PAGE_SIZE)
e13ee7ad 438 peerDigestFetchAbort(fetch, buf, "stored header too big");
9b7de833 439 else
06d2839d 440 storeClientCopy(fetch->sc, fetch->entry, size, 0, SM_PAGE_SIZE, buf,
9b7de833 441 peerDigestSwapInHeaders, fetch);
442 }
443}
444
445static void
446peerDigestSwapInCBlock(void *data, char *buf, ssize_t size)
447{
448 DigestFetchState *fetch = data;
e13ee7ad 449
9b7de833 450 if (peerDigestFetchedEnough(fetch, buf, size, "peerDigestSwapInCBlock"))
451 return;
e13ee7ad 452
9b7de833 453 if (size >= StoreDigestCBlockSize) {
e13ee7ad 454 PeerDigest *pd = fetch->pd;
9d486b43 455 HttpReply *rep = fetch->entry->mem_obj->reply;
456 const int seen = fetch->offset + size;
457
e13ee7ad 458 assert(pd && rep);
459 if (peerDigestSetCBlock(pd, buf)) {
460 /* XXX: soon we will have variable header size */
9b7de833 461 fetch->offset += StoreDigestCBlockSize;
e13ee7ad 462 /* switch to CD buffer and fetch digest guts */
db1cd23c 463 memFree(buf, MEM_4K_BUF);
da407def 464 buf = NULL;
e13ee7ad 465 assert(pd->cd->mask);
06d2839d 466 storeClientCopy(fetch->sc, fetch->entry,
da407def 467 seen,
468 fetch->offset,
e13ee7ad 469 pd->cd->mask_size,
470 pd->cd->mask,
9b7de833 471 peerDigestSwapInMask, fetch);
472 } else {
e13ee7ad 473 peerDigestFetchAbort(fetch, buf, "invalid digest cblock");
9b7de833 474 }
475 } else {
476 /* need more data, do we have space? */
477 if (size >= SM_PAGE_SIZE)
e13ee7ad 478 peerDigestFetchAbort(fetch, buf, "digest cblock too big");
9b7de833 479 else
06d2839d 480 storeClientCopy(fetch->sc, fetch->entry, size, 0, SM_PAGE_SIZE, buf,
9b7de833 481 peerDigestSwapInCBlock, fetch);
482 }
483}
484
485static void
486peerDigestSwapInMask(void *data, char *buf, ssize_t size)
487{
488 DigestFetchState *fetch = data;
e13ee7ad 489 PeerDigest *pd;
490
491 /* NOTE! buf points to the middle of pd->cd->mask! */
da407def 492 if (peerDigestFetchedEnough(fetch, NULL, size, "peerDigestSwapInMask"))
493 return;
9d486b43 494
e13ee7ad 495 pd = fetch->pd;
496 assert(pd->cd && pd->cd->mask);
9d486b43 497
da407def 498 fetch->offset += size;
499 fetch->mask_offset += size;
e13ee7ad 500 if (fetch->mask_offset >= pd->cd->mask_size) {
68814ffd 501 debug(72, 2) ("peerDigestSwapInMask: Done! Got %d, expected %d\n",
e13ee7ad 502 fetch->mask_offset, pd->cd->mask_size);
503 assert(fetch->mask_offset == pd->cd->mask_size);
504 assert(peerDigestFetchedEnough(fetch, NULL, 0, "peerDigestSwapInMask"));
505 } else {
506 const size_t buf_sz = pd->cd->mask_size - fetch->mask_offset;
507 assert(buf_sz > 0);
06d2839d 508 storeClientCopy(fetch->sc, fetch->entry,
e13ee7ad 509 fetch->offset,
510 fetch->offset,
511 buf_sz,
512 pd->cd->mask + fetch->mask_offset,
513 peerDigestSwapInMask, fetch);
9b7de833 514 }
515}
516
517static int
4b4cd312 518peerDigestFetchedEnough(DigestFetchState * fetch, char *buf, ssize_t size, const char *step_name)
9b7de833 519{
e13ee7ad 520 PeerDigest *pd = NULL;
8a6218c6 521 const char *host = "<unknown>"; /* peer host */
522 const char *reason = NULL; /* reason for completion */
523 const char *no_bug = NULL; /* successful completion if set */
9d486b43 524 const int fcb_valid = cbdataValid(fetch);
e13ee7ad 525 const int pdcb_valid = fcb_valid && cbdataValid(fetch->pd);
526 const int pcb_valid = pdcb_valid && cbdataValid(fetch->pd->peer);
527
528 /* test possible exiting conditions (the same for most steps!)
529 * cases marked with '?!' should not happen */
530
531 if (!reason) {
532 if (!fcb_valid)
8a6218c6 533 reason = "fetch aborted?!";
e13ee7ad 534 else if (!(pd = fetch->pd))
535 reason = "peer digest disappeared?!";
5385c86a 536#if DONT
e13ee7ad 537 else if (!cbdataValid(pd))
538 reason = "invalidated peer digest?!";
5385c86a 539#endif
e13ee7ad 540 else
541 host = strBuf(pd->host);
542 }
e13ee7ad 543 debug(72, 6) ("%s: peer %s, offset: %d size: %d.\n",
544 step_name, host, fcb_valid ? fetch->offset : -1, size);
545
546 /* continue checking (with pd and host known and valid) */
547 if (!reason) {
548 if (!cbdataValid(pd->peer))
549 reason = "peer disappeared";
550 else if (size < 0)
551 reason = "swap failure";
552 else if (!fetch->entry)
553 reason = "swap aborted?!";
b7fe0ab0 554 else if (EBIT_TEST(fetch->entry->flags, ENTRY_ABORTED))
e13ee7ad 555 reason = "swap aborted";
556 }
e13ee7ad 557 /* continue checking (maybe-successful eof case) */
558 if (!reason && !size) {
559 if (!pd->cd)
560 reason = "null digest?!";
561 else if (fetch->mask_offset != pd->cd->mask_size)
562 reason = "premature end of digest?!";
563 else if (!peerDigestUseful(pd))
564 reason = "useless digest";
565 else
566 reason = no_bug = "success";
567 }
e13ee7ad 568 /* finish if we have a reason */
9b7de833 569 if (reason) {
e13ee7ad 570 const int level = strstr(reason, "?!") ? 1 : 3;
571 debug(72, level) ("%s: peer %s, exiting after '%s'\n",
572 step_name, host, reason);
573 peerDigestReqFinish(fetch, buf,
574 fcb_valid, pdcb_valid, pcb_valid, reason, !no_bug);
575 } else {
576 /* paranoid check */
577 assert(fcb_valid && pdcb_valid && pcb_valid);
578 }
579 return reason != NULL;
580}
581
551e60a9 582/* call this when all callback data is valid and fetch must be stopped but
583 * no error has occurred (e.g. we received 304 reply and reuse old digest) */
584static void
585peerDigestFetchStop(DigestFetchState * fetch, char *buf, const char *reason)
586{
587 assert(reason);
588 debug(72, 2) ("peerDigestFetchStop: peer %s, reason: %s\n",
589 strBuf(fetch->pd->host), reason);
590 peerDigestReqFinish(fetch, buf, 1, 1, 1, reason, 0);
591}
592
e13ee7ad 593/* call this when all callback data is valid but something bad happened */
594static void
595peerDigestFetchAbort(DigestFetchState * fetch, char *buf, const char *reason)
596{
551e60a9 597 assert(reason);
598 debug(72, 2) ("peerDigestFetchAbort: peer %s, reason: %s\n",
599 strBuf(fetch->pd->host), reason);
e13ee7ad 600 peerDigestReqFinish(fetch, buf, 1, 1, 1, reason, 1);
601}
602
603/* complete the digest transfer, update stats, unlock/release everything */
604static void
605peerDigestReqFinish(DigestFetchState * fetch, char *buf,
8a6218c6 606 int fcb_valid, int pdcb_valid, int pcb_valid,
e13ee7ad 607 const char *reason, int err)
608{
609 assert(reason);
610
611 /* must go before peerDigestPDFinish */
612 if (pdcb_valid) {
613 fetch->pd->flags.requested = 0;
614 fetch->pd->req_result = reason;
615 }
e13ee7ad 616 /* schedule next check if peer is still out there */
617 if (pcb_valid) {
618 PeerDigest *pd = fetch->pd;
619 if (err) {
620 pd->times.retry_delay = peerDigestIncDelay(pd);
621 peerDigestSetCheck(pd, pd->times.retry_delay);
9d486b43 622 } else {
e13ee7ad 623 pd->times.retry_delay = 0;
624 peerDigestSetCheck(pd, peerDigestNewDelay(fetch->entry));
9d486b43 625 }
9b7de833 626 }
e13ee7ad 627 /* note: order is significant */
628 if (fcb_valid)
629 peerDigestFetchSetStats(fetch);
630 if (pdcb_valid)
631 peerDigestPDFinish(fetch, pcb_valid, err);
632 if (fcb_valid)
633 peerDigestFetchFinish(fetch, err);
634 if (buf)
db1cd23c 635 memFree(buf, MEM_4K_BUF);
9b7de833 636}
637
e13ee7ad 638
639/* destroys digest if peer disappeared
640 * must be called only when fetch and pd cbdata are valid */
9b7de833 641static void
e13ee7ad 642peerDigestPDFinish(DigestFetchState * fetch, int pcb_valid, int err)
9b7de833 643{
e13ee7ad 644 PeerDigest *pd = fetch->pd;
645 const char *host = strBuf(pd->host);
646
647 pd->times.received = squid_curtime;
648 pd->times.req_delay = fetch->resp_time;
8a6218c6 649 kb_incr(&pd->stats.sent.kbytes, (size_t) fetch->sent.bytes);
650 kb_incr(&pd->stats.recv.kbytes, (size_t) fetch->recv.bytes);
e13ee7ad 651 pd->stats.sent.msgs += fetch->sent.msg;
652 pd->stats.recv.msgs += fetch->recv.msg;
653
654 if (err) {
655 debug(72, 1) ("%sdisabling (%s) digest from %s\n",
656 pcb_valid ? "temporary " : "",
657 pd->req_result, host);
658
659 if (pd->cd) {
660 cacheDigestDestroy(pd->cd);
661 pd->cd = NULL;
662 }
e13ee7ad 663 pd->flags.usable = 0;
664
665 if (!pcb_valid)
666 peerDigestNotePeerGone(pd);
667 } else {
668 assert(pcb_valid);
669
670 pd->flags.usable = 1;
671
672 /* XXX: ugly condition, but how? */
673 if (fetch->entry->store_status == STORE_OK)
674 debug(72, 2) ("re-used old digest from %s\n", host);
675 else
676 debug(72, 2) ("received valid digest from %s\n", host);
d1cdaa16 677 }
3855c318 678 fetch->pd = NULL;
5d9bb360 679 cbdataUnlock(pd);
e13ee7ad 680}
681
682/* free fetch state structures
683 * must be called only when fetch cbdata is valid */
684static void
685peerDigestFetchFinish(DigestFetchState * fetch, int err)
686{
687 assert(fetch->entry && fetch->request);
688
9b7de833 689 if (fetch->old_entry) {
4b4cd312 690 debug(72, 2) ("peerDigestFetchFinish: deleting old entry\n");
06d2839d 691 storeUnregister(fetch->sc, fetch->old_entry, fetch);
9b7de833 692 storeReleaseRequest(fetch->old_entry);
693 storeUnlockObject(fetch->old_entry);
694 fetch->old_entry = NULL;
695 }
1543ab6c 696 /* update global stats */
83704487 697 kb_incr(&statCounter.cd.kbytes_sent, (size_t) fetch->sent.bytes);
698 kb_incr(&statCounter.cd.kbytes_recv, (size_t) fetch->recv.bytes);
699 statCounter.cd.msgs_sent += fetch->sent.msg;
700 statCounter.cd.msgs_recv += fetch->recv.msg;
e13ee7ad 701
1543ab6c 702 /* unlock everything */
06d2839d 703 storeUnregister(fetch->sc, fetch->entry, fetch);
9b7de833 704 storeUnlockObject(fetch->entry);
903c39e4 705 requestUnlink(fetch->request);
9b7de833 706 fetch->entry = NULL;
903c39e4 707 fetch->request = NULL;
3855c318 708 assert(fetch->pd == NULL);
9d486b43 709 cbdataUnlock(fetch);
9b7de833 710 cbdataFree(fetch);
9b7de833 711}
712
e13ee7ad 713/* calculate fetch stats after completion */
714static void
715peerDigestFetchSetStats(DigestFetchState * fetch)
716{
717 MemObject *mem;
718 assert(fetch->entry && fetch->request);
719
720 mem = fetch->entry->mem_obj;
721 assert(mem);
722
723 /* XXX: outgoing numbers are not precise */
724 /* XXX: we must distinguish between 304 hits and misses here */
725 fetch->sent.bytes = httpRequestPrefixLen(fetch->request);
726 fetch->recv.bytes = fetch->entry->store_status == STORE_PENDING ?
727 mem->inmem_hi : mem->object_sz;
728 fetch->sent.msg = fetch->recv.msg = 1;
729 fetch->expires = fetch->entry->expires;
730 fetch->resp_time = squid_curtime - fetch->start_time;
731
732 debug(72, 3) ("peerDigestFetchFinish: recv %d bytes in %d secs\n",
733 fetch->recv.bytes, fetch->resp_time);
734 debug(72, 3) ("peerDigestFetchFinish: expires: %d (%+d), lmt: %d (%+d)\n",
8a6218c6 735 fetch->expires, fetch->expires - squid_curtime,
736 fetch->entry->lastmod, fetch->entry->lastmod - squid_curtime);
e13ee7ad 737}
738
739
9b7de833 740static int
8a6218c6 741peerDigestSetCBlock(PeerDigest * pd, const char *buf)
9b7de833 742{
743 StoreDigestCBlock cblock;
744 int freed_size = 0;
e13ee7ad 745 const char *host = strBuf(pd->host);
746
9b7de833 747 xmemcpy(&cblock, buf, sizeof(cblock));
748 /* network -> host conversions */
749 cblock.ver.current = ntohs(cblock.ver.current);
750 cblock.ver.required = ntohs(cblock.ver.required);
751 cblock.capacity = ntohl(cblock.capacity);
752 cblock.count = ntohl(cblock.count);
753 cblock.del_count = ntohl(cblock.del_count);
754 cblock.mask_size = ntohl(cblock.mask_size);
4b4cd312 755 debug(72, 2) ("got digest cblock from %s; ver: %d (req: %d)\n",
e13ee7ad 756 host, (int) cblock.ver.current, (int) cblock.ver.required);
4b4cd312 757 debug(72, 2) ("\t size: %d bytes, e-cnt: %d, e-util: %d%%\n",
9b7de833 758 cblock.mask_size, cblock.count,
759 xpercentInt(cblock.count, cblock.capacity));
6106c6fc 760 /* check version requirements (both ways) */
9b7de833 761 if (cblock.ver.required > CacheDigestVer.current) {
4b4cd312 762 debug(72, 1) ("%s digest requires version %d; have: %d\n",
e13ee7ad 763 host, cblock.ver.required, CacheDigestVer.current);
9b7de833 764 return 0;
765 }
6106c6fc 766 if (cblock.ver.current < CacheDigestVer.required) {
4b4cd312 767 debug(72, 1) ("%s digest is version %d; we require: %d\n",
e13ee7ad 768 host, cblock.ver.current, CacheDigestVer.required);
6106c6fc 769 return 0;
770 }
9b7de833 771 /* check consistency */
4b4cd312 772 if (cblock.ver.required > cblock.ver.current ||
d1cdaa16 773 cblock.mask_size <= 0 || cblock.capacity <= 0 ||
774 cblock.bits_per_entry <= 0 || cblock.hash_func_count <= 0) {
e13ee7ad 775 debug(72, 0) ("%s digest cblock is corrupted.\n", host);
9b7de833 776 return 0;
777 }
d1cdaa16 778 /* check consistency further */
779 if (cblock.mask_size != cacheDigestCalcMaskSize(cblock.capacity, cblock.bits_per_entry)) {
4b4cd312 780 debug(72, 0) ("%s digest cblock is corrupted (mask size mismatch: %d ? %d).\n",
e13ee7ad 781 host, cblock.mask_size, cacheDigestCalcMaskSize(cblock.capacity, cblock.bits_per_entry));
d1cdaa16 782 return 0;
783 }
784 /* there are some things we cannot do yet */
785 if (cblock.hash_func_count != CacheDigestHashFuncCount) {
4b4cd312 786 debug(72, 0) ("%s digest: unsupported #hash functions: %d ? %d.\n",
e13ee7ad 787 host, cblock.hash_func_count, CacheDigestHashFuncCount);
d1cdaa16 788 return 0;
789 }
9b7de833 790 /*
791 * no cblock bugs below this point
792 */
793 /* check size changes */
e13ee7ad 794 if (pd->cd && cblock.mask_size != pd->cd->mask_size) {
4b4cd312 795 debug(72, 2) ("%s digest changed size: %d -> %d\n",
8a6218c6 796 host, cblock.mask_size, pd->cd->mask_size);
e13ee7ad 797 freed_size = pd->cd->mask_size;
798 cacheDigestDestroy(pd->cd);
799 pd->cd = NULL;
9b7de833 800 }
e13ee7ad 801 if (!pd->cd) {
45694534 802 debug(72, 2) ("creating %s digest; size: %d (%+d) bytes\n",
e13ee7ad 803 host, cblock.mask_size, (int) (cblock.mask_size - freed_size));
804 pd->cd = cacheDigestCreate(cblock.capacity, cblock.bits_per_entry);
9b7de833 805 if (cblock.mask_size >= freed_size)
83704487 806 kb_incr(&statCounter.cd.memory, cblock.mask_size - freed_size);
9b7de833 807 }
e13ee7ad 808 assert(pd->cd);
9b7de833 809 /* these assignments leave us in an inconsistent state until we finish reading the digest */
e13ee7ad 810 pd->cd->count = cblock.count;
811 pd->cd->del_count = cblock.del_count;
9b7de833 812 return 1;
813}
814
9b7de833 815static int
8a6218c6 816peerDigestUseful(const PeerDigest * pd)
9b7de833 817{
d1cdaa16 818 /* TODO: we should calculate the prob of a false hit instead of bit util */
e13ee7ad 819 const int bit_util = cacheDigestBitUtil(pd->cd);
820 if (bit_util > 65) {
4b4cd312 821 debug(72, 0) ("Warning: %s peer digest has too many bits on (%d%%).\n",
e13ee7ad 822 strBuf(pd->host), bit_util);
d1cdaa16 823 return 0;
9b7de833 824 }
825 return 1;
826}
7f6eb0fe 827
e13ee7ad 828static int
829saneDiff(time_t diff)
830{
8a6218c6 831 return abs(diff) > squid_curtime / 2 ? 0 : diff;
e13ee7ad 832}
833
834void
8a6218c6 835peerDigestStatsReport(const PeerDigest * pd, StoreEntry * e)
e13ee7ad 836{
837#define f2s(flag) (pd->flags.flag ? "yes" : "no")
838#define appendTime(tm) storeAppendPrintf(e, "%s\t %10d\t %+d\t %+d\n", \
839 ""#tm, pd->times.tm, \
840 saneDiff(pd->times.tm - squid_curtime), \
841 saneDiff(pd->times.tm - pd->times.initialized))
842
843 const char *host = pd ? strBuf(pd->host) : NULL;
844 assert(pd);
845
846 storeAppendPrintf(e, "\npeer digest from %s\n", host);
847
848 cacheDigestGuessStatsReport(&pd->stats.guess, e, host);
849
850 storeAppendPrintf(e, "\nevent\t timestamp\t secs from now\t secs from init\n");
851 appendTime(initialized);
852 appendTime(needed);
853 appendTime(requested);
854 appendTime(received);
855 appendTime(next_check);
856
857 storeAppendPrintf(e, "peer digest state:\n");
858 storeAppendPrintf(e, "\tneeded: %3s, usable: %3s, requested: %3s\n",
859 f2s(needed), f2s(usable), f2s(requested));
860 storeAppendPrintf(e, "\n\tlast retry delay: %d secs\n",
861 pd->times.retry_delay);
862 storeAppendPrintf(e, "\tlast request response time: %d secs\n",
863 pd->times.req_delay);
864 storeAppendPrintf(e, "\tlast request result: %s\n",
865 pd->req_result ? pd->req_result : "(none)");
866
867 storeAppendPrintf(e, "\npeer digest traffic:\n");
868 storeAppendPrintf(e, "\trequests sent: %d, volume: %d KB\n",
869 pd->stats.sent.msgs, (int) pd->stats.sent.kbytes.kb);
870 storeAppendPrintf(e, "\treplies recv: %d, volume: %d KB\n",
871 pd->stats.recv.msgs, (int) pd->stats.recv.kbytes.kb);
872
873 storeAppendPrintf(e, "\npeer digest structure:\n");
874 if (pd->cd)
875 cacheDigestReport(pd->cd, host, e);
876 else
877 storeAppendPrintf(e, "\tno in-memory copy\n");
878}
879
7f6eb0fe 880#endif