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