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