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