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