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