]> git.ipfire.org Git - thirdparty/squid.git/blame - src/peer_digest.cc
Fixes to the %PATH patch. Must have been tired..
[thirdparty/squid.git] / src / peer_digest.cc
CommitLineData
9b7de833 1
2/*
3dfaa3d2 3 * $Id: peer_digest.cc,v 1.89 2002/06/18 22:52:32 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);
add2192d 49static STCB peerDigestHandleReply;
3dfaa3d2 50static int peerDigestFetchReply(void *, char *, ssize_t);
51static int peerDigestSwapInHeaders(void *, char *, ssize_t);
52static int peerDigestSwapInCBlock(void *, char *, ssize_t);
53static int peerDigestSwapInMask(void *, char *, ssize_t);
4b4cd312 54static int peerDigestFetchedEnough(DigestFetchState * fetch, char *buf, ssize_t size, const char *step_name);
551e60a9 55static void peerDigestFetchStop(DigestFetchState * fetch, char *buf, const char *reason);
e13ee7ad 56static void peerDigestFetchAbort(DigestFetchState * fetch, char *buf, const char *reason);
57static void peerDigestReqFinish(DigestFetchState * fetch, char *buf, int, int, int, const char *reason, int err);
58static void peerDigestPDFinish(DigestFetchState * fetch, int pcb_valid, int err);
59static void peerDigestFetchFinish(DigestFetchState * fetch, int err);
60static void peerDigestFetchSetStats(DigestFetchState * fetch);
61static int peerDigestSetCBlock(PeerDigest * pd, const char *buf);
62static int peerDigestUseful(const PeerDigest * pd);
395b813e 63
9b7de833 64
65/* local constants */
9d486b43 66
9b7de833 67#define StoreDigestCBlockSize sizeof(StoreDigestCBlock)
68
e13ee7ad 69/* min interval for requesting digests from a given peer */
70static const time_t PeerDigestReqMinGap = 5 * 60; /* seconds */
71/* min interval for requesting digests (cumulative request stream) */
72static const time_t GlobDigestReqMinGap = 1 * 60; /* seconds */
bd890734 73
74/* local vars */
9d486b43 75
8a6218c6 76static time_t pd_last_req_time = 0; /* last call to Check */
9b7de833 77
e13ee7ad 78/* initialize peer digest */
79static void
8a6218c6 80peerDigestInit(PeerDigest * pd, peer * p)
9b7de833 81{
e13ee7ad 82 assert(pd && p);
83
84 memset(pd, 0, sizeof(*pd));
85 pd->peer = p;
86 /* if peer disappears, we will know it's name */
87 stringInit(&pd->host, p->host);
88
89 pd->times.initialized = squid_curtime;
9b7de833 90}
91
4b4cd312 92static void
8a6218c6 93peerDigestClean(PeerDigest * pd)
9b7de833 94{
e13ee7ad 95 assert(pd);
96 if (pd->cd)
97 cacheDigestDestroy(pd->cd);
98 stringClean(&pd->host);
9b7de833 99}
100
28c60158 101CBDATA_TYPE(PeerDigest);
102
e13ee7ad 103/* allocate new peer digest, call Init, and lock everything */
104PeerDigest *
8a6218c6 105peerDigestCreate(peer * p)
e13ee7ad 106{
107 PeerDigest *pd;
8a6218c6 108 assert(p);
e13ee7ad 109
28c60158 110 CBDATA_INIT_TYPE(PeerDigest);
72711e31 111 pd = cbdataAlloc(PeerDigest);
e13ee7ad 112 peerDigestInit(pd, p);
e13ee7ad 113
fa80a8ef 114 /* XXX This does not look right, and the same thing again in the caller */
115 return cbdataReference(pd);
e13ee7ad 116}
117
118/* call Clean and free/unlock everything */
2d72d4fd 119static void
8a6218c6 120peerDigestDestroy(PeerDigest * pd)
e13ee7ad 121{
fcbbccfe 122 peer *p;
e13ee7ad 123 assert(pd);
e13ee7ad 124
fa80a8ef 125 /* inform peer (if any) that we are gone */
1054dddc 126 if (cbdataReferenceValidDone(pd->peer, (void **)&p))
fcbbccfe 127 peerNoteDigestGone(p);
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",
84f2d773 194 strBuf(pd->host), (int) delay);
e13ee7ad 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
e13ee7ad 223 assert(!pd->flags.requested);
5d9bb360 224
8a6218c6 225 pd->times.next_check = 0; /* unknown */
e13ee7ad 226
fa80a8ef 227 if (!cbdataReferenceValid(pd->peer)) {
e13ee7ad 228 peerDigestNotePeerGone(pd);
229 return;
9b7de833 230 }
e13ee7ad 231 debug(72, 3) ("peerDigestCheck: peer %s:%d\n", pd->peer->host, pd->peer->http_port);
38650cc8 232 debug(72, 3) ("peerDigestCheck: time: %ld, last received: %ld (%+d)\n",
84f2d773 233 (long int) squid_curtime, (long int) pd->times.received, (int) (squid_curtime - pd->times.received));
e13ee7ad 234
235 /* decide when we should send the request:
236 * request now unless too close to other requests */
237 req_time = squid_curtime;
238
239 /* per-peer limit */
240 if (req_time - pd->times.received < PeerDigestReqMinGap) {
241 debug(72, 2) ("peerDigestCheck: %s, avoiding close peer requests (%d < %d secs).\n",
84f2d773 242 strBuf(pd->host), (int) (req_time - pd->times.received),
243 (int) PeerDigestReqMinGap);
e13ee7ad 244 req_time = pd->times.received + PeerDigestReqMinGap;
bd890734 245 }
e13ee7ad 246 /* global limit */
247 if (req_time - pd_last_req_time < GlobDigestReqMinGap) {
248 debug(72, 2) ("peerDigestCheck: %s, avoiding close requests (%d < %d secs).\n",
84f2d773 249 strBuf(pd->host), (int) (req_time - pd_last_req_time),
250 (int) GlobDigestReqMinGap);
e13ee7ad 251 req_time = pd_last_req_time + GlobDigestReqMinGap;
9b7de833 252 }
e13ee7ad 253 if (req_time <= squid_curtime)
8a6218c6 254 peerDigestRequest(pd); /* will set pd->flags.requested */
e13ee7ad 255 else
256 peerDigestSetCheck(pd, req_time - squid_curtime);
9b7de833 257}
258
2812146b 259CBDATA_TYPE(DigestFetchState);
260
e13ee7ad 261/* ask store for a digest */
9b7de833 262static void
e13ee7ad 263peerDigestRequest(PeerDigest * pd)
9b7de833 264{
e13ee7ad 265 peer *p = pd->peer;
9b7de833 266 StoreEntry *e, *old_e;
267 char *url;
9d486b43 268 const cache_key *key;
9b7de833 269 request_t *req;
270 DigestFetchState *fetch = NULL;
e13ee7ad 271
272 pd->req_result = NULL;
273 pd->flags.requested = 1;
274
9b7de833 275 /* compute future request components */
7e3ce7b9 276 if (p->digest_url)
277 url = xstrdup(p->digest_url);
278 else
279 url = internalRemoteUri(p->host, p->http_port,
280 "/squid-internal-periodic/", StoreDigestFileName);
281
50a97cee 282 req = urlParse(METHOD_GET, url);
e13ee7ad 283 assert(req);
f66a9ef4 284 key = storeKeyPublicByRequest(req);
285 debug(72, 2) ("peerDigestRequest: %s key: %s\n", url, storeKeyText(key));
e13ee7ad 286
9b7de833 287 /* add custom headers */
2246b732 288 assert(!req->header.len);
4820f62b 289 httpHeaderPutStr(&req->header, HDR_ACCEPT, StoreDigestMimeStr);
290 httpHeaderPutStr(&req->header, HDR_ACCEPT, "text/html");
9bc73deb 291 if (p->login)
292 xstrncpy(req->login, p->login, MAX_LOGIN_SZ);
9b7de833 293 /* create fetch state structure */
2812146b 294 CBDATA_INIT_TYPE(DigestFetchState);
72711e31 295 fetch = cbdataAlloc(DigestFetchState);
903c39e4 296 fetch->request = requestLink(req);
fa80a8ef 297 fetch->pd = cbdataReference(pd);
e13ee7ad 298 fetch->offset = 0;
add2192d 299 fetch->state = DIGEST_READ_REPLY;
e13ee7ad 300
301 /* update timestamps */
9b7de833 302 fetch->start_time = squid_curtime;
e13ee7ad 303 pd->times.requested = squid_curtime;
304 pd_last_req_time = squid_curtime;
305
92695e5e 306 req->flags.cachable = 1;
9b7de833 307 /* the rest is based on clientProcessExpired() */
92695e5e 308 req->flags.refresh = 1;
9d486b43 309 old_e = fetch->old_entry = storeGet(key);
9b7de833 310 if (old_e) {
4b4cd312 311 debug(72, 5) ("peerDigestRequest: found old entry\n");
9b7de833 312 storeLockObject(old_e);
313 storeCreateMemObject(old_e, url, url);
06d2839d 314 fetch->old_sc = storeClientListAdd(old_e, fetch);
9b7de833 315 }
316 e = fetch->entry = storeCreateEntry(url, url, req->flags, req->method);
e13ee7ad 317 assert(EBIT_TEST(e->flags, KEY_PRIVATE));
06d2839d 318 fetch->sc = storeClientListAdd(e, fetch);
9b7de833 319 /* set lastmod to trigger IMS request if possible */
320 if (old_e)
321 e->lastmod = old_e->lastmod;
e13ee7ad 322
9b7de833 323 /* push towards peer cache */
e13ee7ad 324 debug(72, 3) ("peerDigestRequest: forwarding to fwdStart...\n");
7e3ce7b9 325 fwdStart(-1, e, req);
add2192d 326 storeClientCopy(fetch->sc, e, 0, SM_PAGE_SIZE, fetch->buf,
327 peerDigestHandleReply, fetch);
9b7de833 328}
329
add2192d 330
331/* Handle the data copying .. */
332
333/*
334 * This routine handles the copy data and then redirects the
335 * copy to a bunch of subfunctions depending upon the copy state.
336 * It also tracks the buffer offset and "seen", since I'm actually
337 * not interested in rewriting everything to suit my little idea.
338 */
9b7de833 339static void
add2192d 340peerDigestHandleReply(void *data, char *buf, ssize_t copysize)
341{
342 DigestFetchState *fetch = data;
343 PeerDigest *pd = fetch->pd;
344 int retsize = -1;
345 digest_read_state_t prevstate;
346 int newsize;
347
348 assert(pd && buf);
349
350 /* Update the buffer size */
351 fetch->bufofs += copysize;
352
353 assert(fetch->bufofs <= SM_PAGE_SIZE);
354
355 /* If we've fetched enough, return */
356 if (peerDigestFetchedEnough(fetch, fetch->buf, fetch->bufofs, "peerDigestHandleReply"))
357 return;
358
359 /* Call the right function based on the state */
360 /* (Those functions will update the state if needed) */
fa80a8ef 361
e2848e94 362 /* Give us a temporary reference. Some of the calls we make may
363 * try to destroy the fetch structure, and we like to know if they
364 * do
365 */
366 fetch = cbdataReference(fetch);
add2192d 367
368 /* Repeat this loop until we're out of data OR the state changes */
369 /* (So keep going if the state has changed and we still have data */
370 do {
fa80a8ef 371 prevstate = fetch->state;
372 switch (fetch->state) {
373 case DIGEST_READ_REPLY:
374 retsize = peerDigestFetchReply(data, fetch->buf, fetch->bufofs);
375 break;
376 case DIGEST_READ_HEADERS:
377 retsize = peerDigestSwapInHeaders(data, fetch->buf, fetch->bufofs);
378 break;
379 case DIGEST_READ_CBLOCK:
380 retsize = peerDigestSwapInCBlock(data, fetch->buf, fetch->bufofs);
381 break;
382 case DIGEST_READ_MASK:
383 retsize = peerDigestSwapInMask(data, fetch->buf, fetch->bufofs);
384 break;
385 case DIGEST_READ_NONE:
386 break;
387 case DIGEST_READ_DONE:
388 goto finish;
389 break;
390 default:
391 fatal("Bad digest transfer mode!\n");
392 }
393
394 if (retsize < 0)
395 goto finish;
396 /*
397 * The returned size indicates how much of the buffer was read -
398 * so move the remainder of the buffer to the beginning
399 * and update the bufofs / bufsize
400 */
401 newsize = fetch->bufofs - retsize;
402 xmemmove(fetch->buf, fetch->buf + retsize, fetch->bufofs - newsize);
403 fetch->bufofs = newsize;
add2192d 404
e2848e94 405 } while (cbdataReferenceValid(fetch) && prevstate != fetch->state && fetch->bufofs > 0);
add2192d 406
407 /* Update the copy offset */
408 fetch->offset += copysize;
409
410 /* Schedule another copy */
fa80a8ef 411 if (cbdataReferenceValid(fetch)) {
412 storeClientCopy(fetch->sc, fetch->entry, fetch->offset, SM_PAGE_SIZE - fetch->bufofs,
413 fetch->buf + fetch->bufofs, peerDigestHandleReply, fetch);
add2192d 414 }
fa80a8ef 415 finish:
e2848e94 416 /* Get rid of our reference, we've finished with it for now */
417 cbdataReferenceDone(fetch);
add2192d 418}
419
420
421
422/* wait for full http headers to be received then parse them */
423/*
424 * This routine handles parsing the reply line.
425 * If the reply line indicates an OK, the same data is thrown
426 * to SwapInHeaders(). If the reply line is a NOT_MODIFIED,
427 * we simply stop parsing.
428 */
429static int
9b7de833 430peerDigestFetchReply(void *data, char *buf, ssize_t size)
431{
432 DigestFetchState *fetch = data;
e13ee7ad 433 PeerDigest *pd = fetch->pd;
9bc73deb 434 size_t hdr_size;
e13ee7ad 435 assert(pd && buf);
9b7de833 436 assert(!fetch->offset);
e13ee7ad 437
add2192d 438 assert(fetch->state == DIGEST_READ_REPLY);
9b7de833 439 if (peerDigestFetchedEnough(fetch, buf, size, "peerDigestFetchReply"))
add2192d 440 return -1;
e13ee7ad 441
9bc73deb 442 if ((hdr_size = headersEnd(buf, size))) {
9b7de833 443 http_status status;
444 HttpReply *reply = fetch->entry->mem_obj->reply;
445 assert(reply);
9bc73deb 446 httpReplyParse(reply, buf, hdr_size);
9b7de833 447 status = reply->sline.status;
38650cc8 448 debug(72, 3) ("peerDigestFetchReply: %s status: %d, expires: %ld (%+d)\n",
e13ee7ad 449 strBuf(pd->host), status,
84f2d773 450 (long int) reply->expires, (int) (reply->expires - squid_curtime));
e13ee7ad 451
9b7de833 452 /* this "if" is based on clientHandleIMSReply() */
453 if (status == HTTP_NOT_MODIFIED) {
454 request_t *r = NULL;
455 /* our old entry is fine */
456 assert(fetch->old_entry);
457 if (!fetch->old_entry->mem_obj->request)
458 fetch->old_entry->mem_obj->request = r =
2920225f 459 requestLink(fetch->entry->mem_obj->request);
460 assert(fetch->old_entry->mem_obj->request);
9b7de833 461 httpReplyUpdateOnNotModified(fetch->old_entry->mem_obj->reply, reply);
462 storeTimestampsSet(fetch->old_entry);
463 /* get rid of 304 reply */
06d2839d 464 storeUnregister(fetch->sc, fetch->entry, fetch);
9b7de833 465 storeUnlockObject(fetch->entry);
466 fetch->entry = fetch->old_entry;
467 fetch->old_entry = NULL;
2920225f 468 /* preserve request -- we need its size to update counters */
469 /* requestUnlink(r); */
470 /* fetch->entry->mem_obj->request = NULL; */
4b4cd312 471 } else if (status == HTTP_OK) {
9b7de833 472 /* get rid of old entry if any */
473 if (fetch->old_entry) {
e13ee7ad 474 debug(72, 3) ("peerDigestFetchReply: got new digest, releasing old one\n");
06d2839d 475 storeUnregister(fetch->old_sc, fetch->old_entry, fetch);
9b7de833 476 storeReleaseRequest(fetch->old_entry);
477 storeUnlockObject(fetch->old_entry);
478 fetch->old_entry = NULL;
479 }
480 } else {
481 /* some kind of a bug */
e13ee7ad 482 peerDigestFetchAbort(fetch, buf, httpStatusLineReason(&reply->sline));
fa80a8ef 483 return -1; /* XXX -1 will abort stuff in ReadReply! */
9b7de833 484 }
485 /* must have a ready-to-use store entry if we got here */
e13ee7ad 486 /* can we stay with the old in-memory digest? */
add2192d 487 if (status == HTTP_NOT_MODIFIED && fetch->pd->cd) {
551e60a9 488 peerDigestFetchStop(fetch, buf, "Not modified");
add2192d 489 fetch->state = DIGEST_READ_DONE;
490 } else {
491 fetch->state = DIGEST_READ_HEADERS;
fa80a8ef 492 }
9b7de833 493 } else {
494 /* need more data, do we have space? */
495 if (size >= SM_PAGE_SIZE)
e13ee7ad 496 peerDigestFetchAbort(fetch, buf, "reply header too big");
9b7de833 497 }
add2192d 498
499 /* We don't want to actually ack that we've handled anything,
500 * otherwise SwapInHeaders() won't get the reply line .. */
501 return 0;
9b7de833 502}
503
504/* fetch headers from disk, pass on to SwapInCBlock */
add2192d 505static int
9b7de833 506peerDigestSwapInHeaders(void *data, char *buf, ssize_t size)
507{
508 DigestFetchState *fetch = data;
9b7de833 509 size_t hdr_size;
e13ee7ad 510
add2192d 511 assert(fetch->state == DIGEST_READ_HEADERS);
9b7de833 512 if (peerDigestFetchedEnough(fetch, buf, size, "peerDigestSwapInHeaders"))
add2192d 513 return -1;
e13ee7ad 514
9d486b43 515 assert(!fetch->offset);
9b7de833 516 if ((hdr_size = headersEnd(buf, size))) {
517 assert(fetch->entry->mem_obj->reply);
518 if (!fetch->entry->mem_obj->reply->sline.status)
9bc73deb 519 httpReplyParse(fetch->entry->mem_obj->reply, buf, hdr_size);
dba2bbcd 520 if (fetch->entry->mem_obj->reply->sline.status != HTTP_OK) {
521 debug(72, 1) ("peerDigestSwapInHeaders: %s status %d got cached!\n",
e13ee7ad 522 strBuf(fetch->pd->host), fetch->entry->mem_obj->reply->sline.status);
523 peerDigestFetchAbort(fetch, buf, "internal status error");
add2192d 524 return -1;
dba2bbcd 525 }
add2192d 526 fetch->state = DIGEST_READ_CBLOCK;
fa80a8ef 527 return hdr_size; /* Say how much data we read */
9b7de833 528 } else {
529 /* need more data, do we have space? */
add2192d 530 if (size >= SM_PAGE_SIZE) {
e13ee7ad 531 peerDigestFetchAbort(fetch, buf, "stored header too big");
add2192d 532 return -1;
533 } else {
fa80a8ef 534 return 0; /* We need to read more to parse .. */
535 }
9b7de833 536 }
add2192d 537 fatal("peerDigestSwapInHeaders() - shouldn't get here!\n");
9b7de833 538}
539
add2192d 540static int
9b7de833 541peerDigestSwapInCBlock(void *data, char *buf, ssize_t size)
542{
543 DigestFetchState *fetch = data;
e13ee7ad 544
add2192d 545 assert(fetch->state == DIGEST_READ_CBLOCK);
9b7de833 546 if (peerDigestFetchedEnough(fetch, buf, size, "peerDigestSwapInCBlock"))
add2192d 547 return -1;
e13ee7ad 548
9b7de833 549 if (size >= StoreDigestCBlockSize) {
e13ee7ad 550 PeerDigest *pd = fetch->pd;
9d486b43 551 HttpReply *rep = fetch->entry->mem_obj->reply;
9d486b43 552
e13ee7ad 553 assert(pd && rep);
554 if (peerDigestSetCBlock(pd, buf)) {
555 /* XXX: soon we will have variable header size */
e13ee7ad 556 /* switch to CD buffer and fetch digest guts */
da407def 557 buf = NULL;
e13ee7ad 558 assert(pd->cd->mask);
fa80a8ef 559 fetch->state = DIGEST_READ_MASK;
560 return StoreDigestCBlockSize;
9b7de833 561 } else {
e13ee7ad 562 peerDigestFetchAbort(fetch, buf, "invalid digest cblock");
fa80a8ef 563 return -1;
9b7de833 564 }
565 } else {
566 /* need more data, do we have space? */
add2192d 567 if (size >= SM_PAGE_SIZE) {
e13ee7ad 568 peerDigestFetchAbort(fetch, buf, "digest cblock too big");
fa80a8ef 569 return -1;
570 } else {
571 return 0; /* We need more data */
572 }
9b7de833 573 }
add2192d 574 fatal("peerDigestSwapInCBlock(): shouldn't get here!\n");
9b7de833 575}
576
add2192d 577static int
9b7de833 578peerDigestSwapInMask(void *data, char *buf, ssize_t size)
579{
580 DigestFetchState *fetch = data;
e13ee7ad 581 PeerDigest *pd;
582
e13ee7ad 583 pd = fetch->pd;
584 assert(pd->cd && pd->cd->mask);
9d486b43 585
add2192d 586 /*
587 * NOTENOTENOTENOTENOTE: buf doesn't point to pd->cd->mask anymore!
588 * we need to do the copy ourselves!
589 */
590 xmemcpy(pd->cd->mask + fetch->mask_offset, buf, size);
591
592 /* NOTE! buf points to the middle of pd->cd->mask! */
593 if (peerDigestFetchedEnough(fetch, NULL, size, "peerDigestSwapInMask"))
594 return -1;
595
da407def 596 fetch->mask_offset += size;
e13ee7ad 597 if (fetch->mask_offset >= pd->cd->mask_size) {
68814ffd 598 debug(72, 2) ("peerDigestSwapInMask: Done! Got %d, expected %d\n",
e13ee7ad 599 fetch->mask_offset, pd->cd->mask_size);
600 assert(fetch->mask_offset == pd->cd->mask_size);
601 assert(peerDigestFetchedEnough(fetch, NULL, 0, "peerDigestSwapInMask"));
fa80a8ef 602 return -1; /* XXX! */
e13ee7ad 603 } else {
fa80a8ef 604 /* We always read everything, so return so */
605 return size;
9b7de833 606 }
add2192d 607 fatal("peerDigestSwapInMask(): shouldn't get here!\n");
9b7de833 608}
609
610static int
4b4cd312 611peerDigestFetchedEnough(DigestFetchState * fetch, char *buf, ssize_t size, const char *step_name)
9b7de833 612{
e13ee7ad 613 PeerDigest *pd = NULL;
8a6218c6 614 const char *host = "<unknown>"; /* peer host */
615 const char *reason = NULL; /* reason for completion */
616 const char *no_bug = NULL; /* successful completion if set */
e2848e94 617 const int pdcb_valid = cbdataReferenceValid(fetch->pd);
618 const int pcb_valid = cbdataReferenceValid(fetch->pd->peer);
e13ee7ad 619
620 /* test possible exiting conditions (the same for most steps!)
621 * cases marked with '?!' should not happen */
622
623 if (!reason) {
e2848e94 624 if (!(pd = fetch->pd))
e13ee7ad 625 reason = "peer digest disappeared?!";
e2848e94 626#if DONT /* WHY NOT? /HNO */
fa80a8ef 627 else if (!cbdataReferenceValid(pd))
e13ee7ad 628 reason = "invalidated peer digest?!";
5385c86a 629#endif
e13ee7ad 630 else
631 host = strBuf(pd->host);
632 }
e13ee7ad 633 debug(72, 6) ("%s: peer %s, offset: %d size: %d.\n",
e2848e94 634 step_name, host,
635 fetch->offset, size);
e13ee7ad 636
637 /* continue checking (with pd and host known and valid) */
638 if (!reason) {
fa80a8ef 639 if (!cbdataReferenceValid(pd->peer))
e13ee7ad 640 reason = "peer disappeared";
641 else if (size < 0)
642 reason = "swap failure";
643 else if (!fetch->entry)
644 reason = "swap aborted?!";
b7fe0ab0 645 else if (EBIT_TEST(fetch->entry->flags, ENTRY_ABORTED))
e13ee7ad 646 reason = "swap aborted";
647 }
e13ee7ad 648 /* continue checking (maybe-successful eof case) */
649 if (!reason && !size) {
650 if (!pd->cd)
651 reason = "null digest?!";
652 else if (fetch->mask_offset != pd->cd->mask_size)
653 reason = "premature end of digest?!";
654 else if (!peerDigestUseful(pd))
655 reason = "useless digest";
656 else
657 reason = no_bug = "success";
658 }
e13ee7ad 659 /* finish if we have a reason */
9b7de833 660 if (reason) {
e13ee7ad 661 const int level = strstr(reason, "?!") ? 1 : 3;
662 debug(72, level) ("%s: peer %s, exiting after '%s'\n",
663 step_name, host, reason);
664 peerDigestReqFinish(fetch, buf,
e2848e94 665 1, pdcb_valid, pcb_valid, reason, !no_bug);
e13ee7ad 666 } else {
667 /* paranoid check */
e2848e94 668 assert(pdcb_valid && pcb_valid);
e13ee7ad 669 }
670 return reason != NULL;
671}
672
551e60a9 673/* call this when all callback data is valid and fetch must be stopped but
674 * no error has occurred (e.g. we received 304 reply and reuse old digest) */
675static void
676peerDigestFetchStop(DigestFetchState * fetch, char *buf, const char *reason)
677{
678 assert(reason);
679 debug(72, 2) ("peerDigestFetchStop: peer %s, reason: %s\n",
680 strBuf(fetch->pd->host), reason);
681 peerDigestReqFinish(fetch, buf, 1, 1, 1, reason, 0);
682}
683
e13ee7ad 684/* call this when all callback data is valid but something bad happened */
685static void
686peerDigestFetchAbort(DigestFetchState * fetch, char *buf, const char *reason)
687{
551e60a9 688 assert(reason);
689 debug(72, 2) ("peerDigestFetchAbort: peer %s, reason: %s\n",
690 strBuf(fetch->pd->host), reason);
e13ee7ad 691 peerDigestReqFinish(fetch, buf, 1, 1, 1, reason, 1);
692}
693
694/* complete the digest transfer, update stats, unlock/release everything */
695static void
696peerDigestReqFinish(DigestFetchState * fetch, char *buf,
8a6218c6 697 int fcb_valid, int pdcb_valid, int pcb_valid,
e13ee7ad 698 const char *reason, int err)
699{
700 assert(reason);
701
702 /* must go before peerDigestPDFinish */
703 if (pdcb_valid) {
704 fetch->pd->flags.requested = 0;
705 fetch->pd->req_result = reason;
706 }
e13ee7ad 707 /* schedule next check if peer is still out there */
708 if (pcb_valid) {
709 PeerDigest *pd = fetch->pd;
710 if (err) {
711 pd->times.retry_delay = peerDigestIncDelay(pd);
712 peerDigestSetCheck(pd, pd->times.retry_delay);
9d486b43 713 } else {
e13ee7ad 714 pd->times.retry_delay = 0;
715 peerDigestSetCheck(pd, peerDigestNewDelay(fetch->entry));
9d486b43 716 }
9b7de833 717 }
e13ee7ad 718 /* note: order is significant */
719 if (fcb_valid)
720 peerDigestFetchSetStats(fetch);
721 if (pdcb_valid)
722 peerDigestPDFinish(fetch, pcb_valid, err);
723 if (fcb_valid)
724 peerDigestFetchFinish(fetch, err);
9b7de833 725}
726
e13ee7ad 727
728/* destroys digest if peer disappeared
729 * must be called only when fetch and pd cbdata are valid */
9b7de833 730static void
e13ee7ad 731peerDigestPDFinish(DigestFetchState * fetch, int pcb_valid, int err)
9b7de833 732{
e13ee7ad 733 PeerDigest *pd = fetch->pd;
734 const char *host = strBuf(pd->host);
735
736 pd->times.received = squid_curtime;
737 pd->times.req_delay = fetch->resp_time;
8a6218c6 738 kb_incr(&pd->stats.sent.kbytes, (size_t) fetch->sent.bytes);
739 kb_incr(&pd->stats.recv.kbytes, (size_t) fetch->recv.bytes);
e13ee7ad 740 pd->stats.sent.msgs += fetch->sent.msg;
741 pd->stats.recv.msgs += fetch->recv.msg;
742
743 if (err) {
744 debug(72, 1) ("%sdisabling (%s) digest from %s\n",
745 pcb_valid ? "temporary " : "",
746 pd->req_result, host);
747
748 if (pd->cd) {
749 cacheDigestDestroy(pd->cd);
750 pd->cd = NULL;
751 }
e13ee7ad 752 pd->flags.usable = 0;
753
754 if (!pcb_valid)
755 peerDigestNotePeerGone(pd);
756 } else {
757 assert(pcb_valid);
758
759 pd->flags.usable = 1;
760
761 /* XXX: ugly condition, but how? */
762 if (fetch->entry->store_status == STORE_OK)
763 debug(72, 2) ("re-used old digest from %s\n", host);
764 else
765 debug(72, 2) ("received valid digest from %s\n", host);
d1cdaa16 766 }
fa80a8ef 767 cbdataReferenceDone(fetch->pd);
e13ee7ad 768}
769
770/* free fetch state structures
771 * must be called only when fetch cbdata is valid */
772static void
773peerDigestFetchFinish(DigestFetchState * fetch, int err)
774{
775 assert(fetch->entry && fetch->request);
776
9b7de833 777 if (fetch->old_entry) {
4b4cd312 778 debug(72, 2) ("peerDigestFetchFinish: deleting old entry\n");
06d2839d 779 storeUnregister(fetch->sc, fetch->old_entry, fetch);
9b7de833 780 storeReleaseRequest(fetch->old_entry);
781 storeUnlockObject(fetch->old_entry);
782 fetch->old_entry = NULL;
783 }
1543ab6c 784 /* update global stats */
83704487 785 kb_incr(&statCounter.cd.kbytes_sent, (size_t) fetch->sent.bytes);
786 kb_incr(&statCounter.cd.kbytes_recv, (size_t) fetch->recv.bytes);
787 statCounter.cd.msgs_sent += fetch->sent.msg;
788 statCounter.cd.msgs_recv += fetch->recv.msg;
e13ee7ad 789
1543ab6c 790 /* unlock everything */
06d2839d 791 storeUnregister(fetch->sc, fetch->entry, fetch);
9b7de833 792 storeUnlockObject(fetch->entry);
903c39e4 793 requestUnlink(fetch->request);
9b7de833 794 fetch->entry = NULL;
903c39e4 795 fetch->request = NULL;
3855c318 796 assert(fetch->pd == NULL);
9b7de833 797 cbdataFree(fetch);
9b7de833 798}
799
e13ee7ad 800/* calculate fetch stats after completion */
801static void
802peerDigestFetchSetStats(DigestFetchState * fetch)
803{
804 MemObject *mem;
805 assert(fetch->entry && fetch->request);
806
807 mem = fetch->entry->mem_obj;
808 assert(mem);
809
810 /* XXX: outgoing numbers are not precise */
811 /* XXX: we must distinguish between 304 hits and misses here */
812 fetch->sent.bytes = httpRequestPrefixLen(fetch->request);
813 fetch->recv.bytes = fetch->entry->store_status == STORE_PENDING ?
814 mem->inmem_hi : mem->object_sz;
815 fetch->sent.msg = fetch->recv.msg = 1;
816 fetch->expires = fetch->entry->expires;
817 fetch->resp_time = squid_curtime - fetch->start_time;
818
819 debug(72, 3) ("peerDigestFetchFinish: recv %d bytes in %d secs\n",
84f2d773 820 fetch->recv.bytes, (int) fetch->resp_time);
38650cc8 821 debug(72, 3) ("peerDigestFetchFinish: expires: %ld (%+d), lmt: %ld (%+d)\n",
84f2d773 822 (long int) fetch->expires, (int) (fetch->expires - squid_curtime),
823 (long int) fetch->entry->lastmod, (int) (fetch->entry->lastmod - squid_curtime));
e13ee7ad 824}
825
826
9b7de833 827static int
8a6218c6 828peerDigestSetCBlock(PeerDigest * pd, const char *buf)
9b7de833 829{
830 StoreDigestCBlock cblock;
831 int freed_size = 0;
e13ee7ad 832 const char *host = strBuf(pd->host);
833
9b7de833 834 xmemcpy(&cblock, buf, sizeof(cblock));
835 /* network -> host conversions */
836 cblock.ver.current = ntohs(cblock.ver.current);
837 cblock.ver.required = ntohs(cblock.ver.required);
838 cblock.capacity = ntohl(cblock.capacity);
839 cblock.count = ntohl(cblock.count);
840 cblock.del_count = ntohl(cblock.del_count);
841 cblock.mask_size = ntohl(cblock.mask_size);
4b4cd312 842 debug(72, 2) ("got digest cblock from %s; ver: %d (req: %d)\n",
e13ee7ad 843 host, (int) cblock.ver.current, (int) cblock.ver.required);
4b4cd312 844 debug(72, 2) ("\t size: %d bytes, e-cnt: %d, e-util: %d%%\n",
9b7de833 845 cblock.mask_size, cblock.count,
846 xpercentInt(cblock.count, cblock.capacity));
6106c6fc 847 /* check version requirements (both ways) */
9b7de833 848 if (cblock.ver.required > CacheDigestVer.current) {
4b4cd312 849 debug(72, 1) ("%s digest requires version %d; have: %d\n",
e13ee7ad 850 host, cblock.ver.required, CacheDigestVer.current);
9b7de833 851 return 0;
852 }
6106c6fc 853 if (cblock.ver.current < CacheDigestVer.required) {
4b4cd312 854 debug(72, 1) ("%s digest is version %d; we require: %d\n",
e13ee7ad 855 host, cblock.ver.current, CacheDigestVer.required);
6106c6fc 856 return 0;
857 }
9b7de833 858 /* check consistency */
4b4cd312 859 if (cblock.ver.required > cblock.ver.current ||
d1cdaa16 860 cblock.mask_size <= 0 || cblock.capacity <= 0 ||
861 cblock.bits_per_entry <= 0 || cblock.hash_func_count <= 0) {
e13ee7ad 862 debug(72, 0) ("%s digest cblock is corrupted.\n", host);
9b7de833 863 return 0;
864 }
d1cdaa16 865 /* check consistency further */
866 if (cblock.mask_size != cacheDigestCalcMaskSize(cblock.capacity, cblock.bits_per_entry)) {
4b4cd312 867 debug(72, 0) ("%s digest cblock is corrupted (mask size mismatch: %d ? %d).\n",
e13ee7ad 868 host, cblock.mask_size, cacheDigestCalcMaskSize(cblock.capacity, cblock.bits_per_entry));
d1cdaa16 869 return 0;
870 }
871 /* there are some things we cannot do yet */
872 if (cblock.hash_func_count != CacheDigestHashFuncCount) {
4b4cd312 873 debug(72, 0) ("%s digest: unsupported #hash functions: %d ? %d.\n",
e13ee7ad 874 host, cblock.hash_func_count, CacheDigestHashFuncCount);
d1cdaa16 875 return 0;
876 }
9b7de833 877 /*
878 * no cblock bugs below this point
879 */
880 /* check size changes */
e13ee7ad 881 if (pd->cd && cblock.mask_size != pd->cd->mask_size) {
4b4cd312 882 debug(72, 2) ("%s digest changed size: %d -> %d\n",
8a6218c6 883 host, cblock.mask_size, pd->cd->mask_size);
e13ee7ad 884 freed_size = pd->cd->mask_size;
885 cacheDigestDestroy(pd->cd);
886 pd->cd = NULL;
9b7de833 887 }
e13ee7ad 888 if (!pd->cd) {
45694534 889 debug(72, 2) ("creating %s digest; size: %d (%+d) bytes\n",
e13ee7ad 890 host, cblock.mask_size, (int) (cblock.mask_size - freed_size));
891 pd->cd = cacheDigestCreate(cblock.capacity, cblock.bits_per_entry);
9b7de833 892 if (cblock.mask_size >= freed_size)
83704487 893 kb_incr(&statCounter.cd.memory, cblock.mask_size - freed_size);
9b7de833 894 }
e13ee7ad 895 assert(pd->cd);
9b7de833 896 /* these assignments leave us in an inconsistent state until we finish reading the digest */
e13ee7ad 897 pd->cd->count = cblock.count;
898 pd->cd->del_count = cblock.del_count;
9b7de833 899 return 1;
900}
901
9b7de833 902static int
8a6218c6 903peerDigestUseful(const PeerDigest * pd)
9b7de833 904{
d1cdaa16 905 /* TODO: we should calculate the prob of a false hit instead of bit util */
e13ee7ad 906 const int bit_util = cacheDigestBitUtil(pd->cd);
907 if (bit_util > 65) {
4b4cd312 908 debug(72, 0) ("Warning: %s peer digest has too many bits on (%d%%).\n",
e13ee7ad 909 strBuf(pd->host), bit_util);
d1cdaa16 910 return 0;
9b7de833 911 }
912 return 1;
913}
7f6eb0fe 914
e13ee7ad 915static int
916saneDiff(time_t diff)
917{
8a6218c6 918 return abs(diff) > squid_curtime / 2 ? 0 : diff;
e13ee7ad 919}
920
921void
8a6218c6 922peerDigestStatsReport(const PeerDigest * pd, StoreEntry * e)
e13ee7ad 923{
924#define f2s(flag) (pd->flags.flag ? "yes" : "no")
38650cc8 925#define appendTime(tm) storeAppendPrintf(e, "%s\t %10ld\t %+d\t %+d\n", \
926 ""#tm, (long int)pd->times.tm, \
e13ee7ad 927 saneDiff(pd->times.tm - squid_curtime), \
928 saneDiff(pd->times.tm - pd->times.initialized))
929
930 const char *host = pd ? strBuf(pd->host) : NULL;
931 assert(pd);
932
933 storeAppendPrintf(e, "\npeer digest from %s\n", host);
934
935 cacheDigestGuessStatsReport(&pd->stats.guess, e, host);
936
937 storeAppendPrintf(e, "\nevent\t timestamp\t secs from now\t secs from init\n");
938 appendTime(initialized);
939 appendTime(needed);
940 appendTime(requested);
941 appendTime(received);
942 appendTime(next_check);
943
944 storeAppendPrintf(e, "peer digest state:\n");
945 storeAppendPrintf(e, "\tneeded: %3s, usable: %3s, requested: %3s\n",
946 f2s(needed), f2s(usable), f2s(requested));
947 storeAppendPrintf(e, "\n\tlast retry delay: %d secs\n",
84f2d773 948 (int) pd->times.retry_delay);
e13ee7ad 949 storeAppendPrintf(e, "\tlast request response time: %d secs\n",
84f2d773 950 (int) pd->times.req_delay);
e13ee7ad 951 storeAppendPrintf(e, "\tlast request result: %s\n",
952 pd->req_result ? pd->req_result : "(none)");
953
954 storeAppendPrintf(e, "\npeer digest traffic:\n");
955 storeAppendPrintf(e, "\trequests sent: %d, volume: %d KB\n",
956 pd->stats.sent.msgs, (int) pd->stats.sent.kbytes.kb);
957 storeAppendPrintf(e, "\treplies recv: %d, volume: %d KB\n",
958 pd->stats.recv.msgs, (int) pd->stats.recv.kbytes.kb);
959
960 storeAppendPrintf(e, "\npeer digest structure:\n");
961 if (pd->cd)
962 cacheDigestReport(pd->cd, host, e);
963 else
964 storeAppendPrintf(e, "\tno in-memory copy\n");
965}
966
7f6eb0fe 967#endif