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