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