]> git.ipfire.org Git - thirdparty/squid.git/blame - src/store_client.cc
Source Format Enforcement (#1234)
[thirdparty/squid.git] / src / store_client.cc
CommitLineData
9cef6668 1/*
b8ae064d 2 * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
26ac0430 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.
9cef6668 7 */
8
bbc27441
AJ
9/* DEBUG: section 90 Storage Manager Client-Side Interface */
10
582c2af2 11#include "squid.h"
819be284 12#include "acl/FilledChecklist.h"
1fa761af 13#include "base/AsyncCbdataCalls.h"
f925268a 14#include "base/CodeContext.h"
a553a5a3 15#include "event.h"
22bbd840 16#include "globals.h"
528b2c61 17#include "HttpReply.h"
582c2af2
FC
18#include "HttpRequest.h"
19#include "MemBuf.h"
528b2c61 20#include "MemObject.h"
b6149797 21#include "mime_header.h"
4d5904f7 22#include "SquidConfig.h"
e4f1fdae 23#include "StatCounters.h"
582c2af2 24#include "Store.h"
d448e1eb 25#include "store/SwapMetaIn.h"
f82b5c64 26#include "store_swapin.h"
602d9612 27#include "StoreClient.h"
9a0a18de 28#if USE_DELAY_POOLS
b67e2c8c 29#include "DelayPools.h"
30#endif
c8be6d7b 31
e3ef2b09 32/*
33 * NOTE: 'Header' refers to the swapfile metadata header.
2f8abb64 34 * 'OBJHeader' refers to the object header, with canonical
f53969cc
SM
35 * processed object headers (which may derive from FTP/HTTP etc
36 * upstream protocols
e3ef2b09 37 * 'Body' refers to the swapfile body, which is the full
38 * HTTP reply (including HTTP headers and body).
39 */
4fcc8876 40static StoreIOState::STRCB storeClientReadBody;
41static StoreIOState::STRCB storeClientReadHeader;
f09f5b26 42static void storeClientCopy2(StoreEntry * e, store_client * sc);
ae6568e7 43static bool CheckQuickAbortIsReasonable(StoreEntry * entry);
f09f5b26 44
b001e822 45CBDATA_CLASS_INIT(store_client);
528b2c61 46
819be284
EB
47/* StoreClient */
48
49bool
50StoreClient::onCollapsingPath() const
51{
52 if (!Config.onoff.collapsed_forwarding)
53 return false;
54
55 if (!Config.accessList.collapsedForwardingAccess)
56 return true;
57
58 ACLFilledChecklist checklist(Config.accessList.collapsedForwardingAccess, nullptr, nullptr);
59 fillChecklist(checklist);
60 return checklist.fastCheck().allowed();
61}
62
63bool
7976fed3 64StoreClient::startCollapsingOn(const StoreEntry &e, const bool doingRevalidation) const
819be284 65{
d2a6dcba
EB
66 if (!e.hittingRequiresCollapsing())
67 return false; // collapsing is impossible due to the entry state
68
69 if (!onCollapsingPath())
70 return false; // collapsing is impossible due to Squid configuration
71
72 /* collapsing is possible; the caller must collapse */
73
74 if (const auto tags = loggingTags()) {
75 if (doingRevalidation)
76 tags->collapsingHistory.revalidationCollapses++;
77 else
78 tags->collapsingHistory.otherCollapses++;
79 }
80
81 debugs(85, 5, e << " doingRevalidation=" << doingRevalidation);
82 return true;
819be284
EB
83}
84
819be284
EB
85/* store_client */
86
528b2c61 87int
88store_client::getType() const
89{
90 return type;
91}
92
06d2839d 93#if STORE_CLIENT_LIST_DEBUG
fa80a8ef 94static store_client *
f09f5b26 95storeClientListSearch(const MemObject * mem, void *data)
96{
06d2839d 97 dlink_node *node;
98 store_client *sc = NULL;
62e76326 99
06d2839d 100 for (node = mem->clients.head; node; node = node->next) {
62e76326 101 sc = node->data;
102
103 if (sc->owner == data)
104 return sc;
f09f5b26 105 }
62e76326 106
06d2839d 107 return NULL;
f09f5b26 108}
c8be6d7b 109
110int
111storeClientIsThisAClient(store_client * sc, void *someClient)
112{
113 return sc->owner == someClient;
114}
62e76326 115
06d2839d 116#endif
924f73bc 117#include "HttpRequest.h"
f09f5b26 118
119/* add client with fd to client list */
06d2839d 120store_client *
f09f5b26 121storeClientListAdd(StoreEntry * e, void *data)
122{
123 MemObject *mem = e->mem_obj;
f09f5b26 124 store_client *sc;
125 assert(mem);
06d2839d 126#if STORE_CLIENT_LIST_DEBUG
62e76326 127
f09f5b26 128 if (storeClientListSearch(mem, data) != NULL)
62e76326 129 /* XXX die! */
130 assert(1 == 0);
8b082ed9
FC
131#else
132 (void)data;
6b8e7481 133#endif
62e76326 134
528b2c61 135 sc = new store_client (e);
62e76326 136
528b2c61 137 mem->addClient(sc);
62e76326 138
06d2839d 139 return sc;
f09f5b26 140}
141
1fa761af
EB
142/// schedules asynchronous STCB call to relay disk or memory read results
143/// \param outcome an error signal (if negative), an EOF signal (if zero), or the number of bytes read
528b2c61 144void
1fa761af 145store_client::callback(const ssize_t outcome)
b04e66e0 146{
1fa761af
EB
147 if (outcome > 0)
148 return noteCopiedBytes(outcome);
62e76326 149
1fa761af
EB
150 if (outcome < 0)
151 return fail();
63326655 152
1fa761af
EB
153 noteEof();
154}
63326655 155
1fa761af
EB
156/// finishCallback() wrapper; TODO: Add NullaryMemFunT for non-jobs.
157void
158store_client::FinishCallback(store_client * const sc)
159{
160 sc->finishCallback();
161}
62e76326 162
1fa761af
EB
163/// finishes a copy()-STCB sequence by synchronously calling STCB
164void
165store_client::finishCallback()
166{
167 Assure(_callback.callback_handler);
168 Assure(_callback.notifier);
169
170 // callers are not ready to handle a content+error combination
171 Assure(object_ok || !copiedSize);
172
173 StoreIOBuffer result(copiedSize, copyInto.offset, copyInto.data);
174 result.flags.error = object_ok ? 0 : 1;
175 copiedSize = 0;
176
177 cmp_offset = result.offset + result.length;
528b2c61 178 STCB *temphandler = _callback.callback_handler;
179 void *cbdata = _callback.callback_data;
aee3523a
AR
180 _callback = Callback(nullptr, nullptr);
181 copyInto.data = nullptr;
62e76326 182
528b2c61 183 if (cbdataReferenceValid(cbdata))
62e76326 184 temphandler(cbdata, result);
185
528b2c61 186 cbdataReferenceDone(cbdata);
b04e66e0 187}
188
1fa761af
EB
189/// schedules asynchronous STCB call to relay a successful disk or memory read
190/// \param bytesCopied the number of response bytes copied into copyInto
191void
192store_client::noteCopiedBytes(const size_t bytesCopied)
f115fadd 193{
1fa761af
EB
194 debugs(90, 5, bytesCopied);
195 Assure(bytesCopied > 0);
196 Assure(!copiedSize);
197 copiedSize = bytesCopied;
198 noteNews();
199}
62e76326 200
1fa761af
EB
201void
202store_client::noteEof()
203{
204 debugs(90, 5, copiedSize);
205 Assure(!copiedSize);
206 noteNews();
f115fadd 207}
208
cc8c4af2
AJ
209store_client::store_client(StoreEntry *e) :
210 cmp_offset(0),
211#if STORE_CLIENT_LIST_DEBUG
212 owner(cbdataReference(data)),
b67e2c8c 213#endif
cc8c4af2
AJ
214 entry(e),
215 type(e->storeClientType()),
1fa761af
EB
216 object_ok(true),
217 copiedSize(0)
528b2c61 218{
3dd52a0b 219 flags.disk_io_pending = false;
cc8c4af2 220 flags.store_copying = false;
5db6bf73 221 ++ entry->refcount;
62e76326 222
cc8c4af2 223 if (getType() == STORE_DISK_CLIENT) {
62e76326 224 /* assert we'll be able to get the data we want */
02a2d80b 225 /* maybe we should open swapin_sio here */
02ba667b 226 assert(entry->hasDisk() && !entry->swapoutFailed());
cc8c4af2 227 }
528b2c61 228}
229
2f44bd34 230store_client::~store_client()
62e76326 231{}
2f44bd34 232
f09f5b26 233/* copy bytes requested by the client */
234void
a4b8110e 235storeClientCopy(store_client * sc,
62e76326 236 StoreEntry * e,
237 StoreIOBuffer copyInto,
238 STCB * callback,
239 void *data)
f09f5b26 240{
aee3523a 241 assert (sc != nullptr);
528b2c61 242 sc->copy(e, copyInto,callback,data);
243}
244
245void
246store_client::copy(StoreEntry * anEntry,
62e76326 247 StoreIOBuffer copyRequest,
248 STCB * callback_fn,
249 void *data)
528b2c61 250{
251 assert (anEntry == entry);
252 assert (callback_fn);
253 assert (data);
254 assert(!EBIT_TEST(entry->flags, ENTRY_ABORTED));
bf8fe701 255 debugs(90, 3, "store_client::copy: " << entry->getMD5Text() << ", from " <<
47f6e231 256 copyRequest.offset << ", for length " <<
bf8fe701 257 (int) copyRequest.length << ", cb " << callback_fn << ", cbdata " <<
258 data);
259
06d2839d 260#if STORE_CLIENT_LIST_DEBUG
62e76326 261
528b2c61 262 assert(this == storeClientListSearch(entry->mem_obj, data));
06d2839d 263#endif
62e76326 264
90703668 265 assert(!_callback.pending());
528b2c61 266#if ONLYCONTIGUOUSREQUESTS
62e76326 267
528b2c61 268 assert(cmp_offset == copyRequest.offset);
269#endif
270 /* range requests will skip into the body */
271 cmp_offset = copyRequest.offset;
272 _callback = Callback (callback_fn, cbdataReference(data));
273 copyInto.data = copyRequest.data;
274 copyInto.length = copyRequest.length;
275 copyInto.offset = copyRequest.offset;
276
1d5161bd 277 static bool copying (false);
278 assert (!copying);
279 copying = true;
a46d2c0e 280 /* we might be blocking comm reads due to readahead limits
281 * now we have a new offset, trigger those reads...
282 */
283 entry->mem_obj->kickReads();
1d5161bd 284 copying = false;
a46d2c0e 285
4475555f
AR
286 anEntry->lock("store_client::copy"); // see deletion note below
287
528b2c61 288 storeClientCopy2(entry, this);
0ad2b63b 289
4475555f
AR
290 // Bug 3480: This store_client object may be deleted now if, for example,
291 // the client rejects the hit response copied above. Use on-stack pointers!
292
0ad2b63b 293#if USE_ADAPTATION
4475555f 294 anEntry->kickProducer();
0ad2b63b 295#endif
4475555f 296 anEntry->unlock("store_client::copy");
1809cebd 297
1809cebd 298 // Add no code here. This object may no longer exist.
f09f5b26 299}
300
f25d697f
AR
301/// Whether there is (or will be) more entry data for us.
302bool
303store_client::moreToSend() const
07304bf9 304{
f25d697f
AR
305 if (entry->store_status == STORE_PENDING)
306 return true; // there may be more coming
307
308 /* STORE_OK, including aborted entries: no more data is coming */
309
310 const int64_t len = entry->objectLen();
62e76326 311
0cdcf3d7 312 // If we do not know the entry length, then we have to open the swap file.
4310f8b0 313 const bool canSwapIn = entry->hasDisk();
f25d697f
AR
314 if (len < 0)
315 return canSwapIn;
62e76326 316
f25d697f
AR
317 if (copyInto.offset >= len)
318 return false; // sent everything there is
62e76326 319
f25d697f
AR
320 if (canSwapIn)
321 return true; // if we lack prefix, we can swap it in
62e76326 322
f25d697f 323 // If we cannot swap in, make sure we have what we want in RAM. Otherwise,
0cdcf3d7 324 // scheduleRead calls scheduleDiskRead which asserts without a swap file.
f25d697f
AR
325 const MemObject *mem = entry->mem_obj;
326 return mem &&
9d4e9cfb 327 mem->inmem_lo <= copyInto.offset && copyInto.offset < mem->endOffset();
07304bf9 328}
329
f09f5b26 330static void
331storeClientCopy2(StoreEntry * e, store_client * sc)
332{
528b2c61 333 /* reentrancy not allowed - note this could lead to
1fa761af 334 * dropped notifications about response data availability
528b2c61 335 */
62e76326 336
67fd69de 337 if (sc->flags.store_copying) {
1fa761af 338 debugs(90, 3, "prevented recursive copying for " << *e);
62e76326 339 return;
67fd69de 340 }
62e76326 341
bf8fe701 342 debugs(90, 3, "storeClientCopy2: " << e->getMD5Text());
90703668 343 assert(sc->_callback.pending());
0bb129ee 344 /*
b7fe0ab0 345 * We used to check for ENTRY_ABORTED here. But there were some
0bb129ee 346 * problems. For example, we might have a slow client (or two) and
d5430dc8
AJ
347 * the peer server is reading far ahead and swapping to disk. Even
348 * if the peer aborts, we want to give the client(s)
0bb129ee 349 * everything we got before the abort condition occurred.
350 */
528b2c61 351 sc->doCopy(e);
cfac48c2 352}
353
528b2c61 354void
355store_client::doCopy(StoreEntry *anEntry)
cfac48c2 356{
1fa761af
EB
357 Assure(_callback.pending());
358 Assure(!flags.disk_io_pending);
359 Assure(!flags.store_copying);
360
528b2c61 361 assert (anEntry == entry);
3dd52a0b 362 flags.store_copying = true;
528b2c61 363 MemObject *mem = entry->mem_obj;
cd748f27 364
bf8fe701 365 debugs(33, 5, "store_client::doCopy: co: " <<
47f6e231 366 copyInto.offset << ", hi: " <<
367 mem->endOffset());
add2192d 368
f25d697f 369 if (!moreToSend()) {
62e76326 370 /* There is no more to send! */
bf95c10a 371 debugs(33, 3, "There is no more to send!");
1fa761af 372 noteEof();
3dd52a0b 373 flags.store_copying = false;
62e76326 374 return;
cfac48c2 375 }
62e76326 376
add2192d 377 /* Check that we actually have data */
528b2c61 378 if (anEntry->store_status == STORE_PENDING && copyInto.offset >= mem->endOffset()) {
bf8fe701 379 debugs(90, 3, "store_client::doCopy: Waiting for more");
3dd52a0b 380 flags.store_copying = false;
62e76326 381 return;
cfac48c2 382 }
62e76326 383
cfac48c2 384 /*
385 * Slight weirdness here. We open a swapin file for any
386 * STORE_DISK_CLIENT, even if we can copy the requested chunk
387 * from memory in the next block. We must try to open the
388 * swapin file before sending any data to the client side. If
389 * we postpone the open, and then can not open the file later
390 * on, the client loses big time. Its transfer just gets cut
391 * off. Better to open it early (while the client side handler
392 * is clientCacheHit) so that we can fall back to a cache miss
393 * if needed.
394 */
fa80a8ef 395
aee3523a 396 if (STORE_DISK_CLIENT == getType() && swapin_sio == nullptr) {
0cdcf3d7
AR
397 if (!startSwapin())
398 return; // failure
399 }
400 scheduleRead();
4e70dae3 401}
402
0cdcf3d7
AR
403/// opens the swapin "file" if possible; otherwise, fail()s and returns false
404bool
4e70dae3 405store_client::startSwapin()
406{
bf8fe701 407 debugs(90, 3, "store_client::doCopy: Need to open swap in file");
4e70dae3 408 /* gotta open the swapin file */
409
410 if (storeTooManyDiskFilesOpen()) {
411 /* yuck -- this causes a TCP_SWAPFAIL_MISS on the client side */
412 fail();
3dd52a0b 413 flags.store_copying = false;
0cdcf3d7 414 return false;
4e70dae3 415 } else if (!flags.disk_io_pending) {
416 /* Don't set store_io_pending here */
417 storeSwapInStart(this);
62e76326 418
aee3523a 419 if (swapin_sio == nullptr) {
62e76326 420 fail();
3dd52a0b 421 flags.store_copying = false;
0cdcf3d7 422 return false;
62e76326 423 }
62e76326 424
0cdcf3d7 425 return true;
4e70dae3 426 } else {
e0236918 427 debugs(90, DBG_IMPORTANT, "WARNING: Averted multiple fd operation (1)");
3dd52a0b 428 flags.store_copying = false;
0cdcf3d7 429 return false;
cfac48c2 430 }
4e70dae3 431}
62e76326 432
1fa761af
EB
433void
434store_client::noteSwapInDone(const bool error)
435{
436 Assure(_callback.pending());
437 if (error)
438 fail();
439 else
440 noteEof();
441}
442
4e70dae3 443void
444store_client::scheduleRead()
445{
446 MemObject *mem = entry->mem_obj;
447
448 if (copyInto.offset >= mem->inmem_lo && copyInto.offset < mem->endOffset())
449 scheduleMemRead();
450 else
451 scheduleDiskRead();
452}
453
454void
455store_client::scheduleDiskRead()
456{
cd748f27 457 /* What the client wants is not in memory. Schedule a disk read */
0cdcf3d7
AR
458 if (getType() == STORE_DISK_CLIENT) {
459 // we should have called startSwapin() already
aee3523a 460 assert(swapin_sio != nullptr);
2da4bfe6 461 } else if (!swapin_sio && !startSwapin()) {
0cdcf3d7
AR
462 debugs(90, 3, "bailing after swapin start failure for " << *entry);
463 assert(!flags.store_copying);
464 return;
465 }
62e76326 466
528b2c61 467 assert(!flags.disk_io_pending);
62e76326 468
0cdcf3d7 469 debugs(90, 3, "reading " << *entry << " from disk");
62e76326 470
528b2c61 471 fileRead();
62e76326 472
3dd52a0b 473 flags.store_copying = false;
f09f5b26 474}
475
4e70dae3 476void
477store_client::scheduleMemRead()
478{
479 /* What the client wants is in memory */
480 /* Old style */
bf8fe701 481 debugs(90, 3, "store_client::doCopy: Copying normal from memory");
1fa761af 482 const auto sz = entry->mem_obj->data_hdr.copy(copyInto); // may be <= 0 per copy() API
4e70dae3 483 callback(sz);
3dd52a0b 484 flags.store_copying = false;
4e70dae3 485}
486
528b2c61 487void
488store_client::fileRead()
f09f5b26 489{
528b2c61 490 MemObject *mem = entry->mem_obj;
491
90703668 492 assert(_callback.pending());
528b2c61 493 assert(!flags.disk_io_pending);
3dd52a0b 494 flags.disk_io_pending = true;
62e76326 495
528b2c61 496 if (mem->swap_hdr_sz != 0)
4310f8b0 497 if (entry->swappingOut())
47f6e231 498 assert(mem->swapout.sio->offset() > copyInto.offset + (int64_t)mem->swap_hdr_sz);
62e76326 499
528b2c61 500 storeRead(swapin_sio,
62e76326 501 copyInto.data,
502 copyInto.length,
503 copyInto.offset + mem->swap_hdr_sz,
504 mem->swap_hdr_sz == 0 ? storeClientReadHeader
505 : storeClientReadBody,
506 this);
f09f5b26 507}
508
beae59b0 509void
ced8def3 510store_client::readBody(const char *, ssize_t len)
beae59b0 511{
beae59b0 512 // Don't assert disk_io_pending here.. may be called by read_header
3dd52a0b 513 flags.disk_io_pending = false;
beae59b0 514 assert(_callback.pending());
4a7a3d56 515 debugs(90, 3, "storeClientReadBody: len " << len << "");
62e76326 516
18102f7d
EB
517 if (len < 0)
518 return fail();
519
66d51f4f
AR
520 const auto rep = entry->mem_obj ? &entry->mem().baseReply() : nullptr;
521 if (copyInto.offset == 0 && len > 0 && rep && rep->sline.status() == Http::scNone) {
62e76326 522 /* Our structure ! */
66d51f4f 523 if (!entry->mem_obj->adjustableBaseReply().parseCharBuf(copyInto.data, headersEnd(copyInto.data, len))) {
d816f28d 524 debugs(90, DBG_CRITICAL, "ERROR: Could not parse headers from on disk object");
3196f526 525 }
06a5ae20 526 }
62e76326 527
ff4b33f4 528 if (len > 0 && rep && entry->mem_obj->inmem_lo == 0 && entry->objectLen() <= (int64_t)Config.Store.maxInMemObjSize && Config.onoff.memory_cache_disk) {
6d3c2758
HN
529 storeGetMemSpace(len);
530 // The above may start to free our object so we need to check again
531 if (entry->mem_obj->inmem_lo == 0) {
532 /* Copy read data back into memory.
55759ffb 533 * copyInto.offset includes headers, which is what mem cache needs
6d3c2758 534 */
c0bc2b1b 535 if (copyInto.offset == entry->mem_obj->endOffset()) {
55759ffb 536 entry->mem_obj->write(StoreIOBuffer(len, copyInto.offset, copyInto.data));
6d3c2758 537 }
3196f526 538 }
beae59b0
HN
539 }
540
541 callback(len);
528b2c61 542}
543
544void
62e76326 545store_client::fail()
528b2c61 546{
1fa761af
EB
547 debugs(90, 3, (object_ok ? "once" : "again"));
548 if (!object_ok)
549 return; // we failed earlier; nothing to do now
550
528b2c61 551 object_ok = false;
1fa761af
EB
552
553 noteNews();
554}
555
556/// if necessary and possible, informs the Store reader about copy() result
557void
558store_client::noteNews()
559{
db2ff94c 560 /* synchronous open failures callback from the store,
561 * before startSwapin detects the failure.
562 * TODO: fix this inconsistent behaviour - probably by
26ac0430 563 * having storeSwapInStart become a callback functions,
db2ff94c 564 * not synchronous
565 */
566
1fa761af
EB
567 if (!_callback.callback_handler) {
568 debugs(90, 5, "client lost interest");
569 return;
570 }
571
572 if (_callback.notifier) {
573 debugs(90, 5, "earlier news is being delivered by " << _callback.notifier);
574 return;
575 }
576
577 _callback.notifier = asyncCall(90, 4, "store_client::FinishCallback", cbdataDialer(store_client::FinishCallback, this));
578 ScheduleCallHere(_callback.notifier);
579
580 Assure(!_callback.pending());
f09f5b26 581}
582
e3ef2b09 583static void
ced8def3 584storeClientReadHeader(void *data, const char *buf, ssize_t len, StoreIOState::Pointer)
e3ef2b09 585{
e6ccf245 586 store_client *sc = (store_client *)data;
528b2c61 587 sc->readHeader(buf, len);
588}
589
beae59b0 590static void
ced8def3 591storeClientReadBody(void *data, const char *buf, ssize_t len, StoreIOState::Pointer)
beae59b0
HN
592{
593 store_client *sc = (store_client *)data;
594 sc->readBody(buf, len);
595}
596
528b2c61 597void
598store_client::readHeader(char const *buf, ssize_t len)
599{
600 MemObject *const mem = entry->mem_obj;
62e76326 601
528b2c61 602 assert(flags.disk_io_pending);
3dd52a0b 603 flags.disk_io_pending = false;
90703668 604 assert(_callback.pending());
528b2c61 605
4ea266b7 606 // abort if we fail()'d earlier
528b2c61 607 if (!object_ok)
62e76326 608 return;
609
18102f7d
EB
610 if (len < 0)
611 return fail();
612
d448e1eb
EB
613 try {
614 Store::UnpackHitSwapMeta(buf, len, *entry);
615 } catch (...) {
616 debugs(90, DBG_IMPORTANT, "ERROR: Failed to unpack Store entry metadata: " << CurrentException);
4ea266b7
CF
617 fail();
618 return;
619 }
620
e3ef2b09 621 /*
622 * If our last read got some data the client wants, then give
623 * it to them, otherwise schedule another read.
624 */
528b2c61 625 size_t body_sz = len - mem->swap_hdr_sz;
62e76326 626
47f6e231 627 if (copyInto.offset < static_cast<int64_t>(body_sz)) {
62e76326 628 /*
629 * we have (part of) what they want
630 */
d85c3078 631 size_t copy_sz = min(copyInto.length, body_sz);
4a7a3d56 632 debugs(90, 3, "storeClientReadHeader: copying " << copy_sz << " bytes of body");
41d00cd3 633 memmove(copyInto.data, copyInto.data + mem->swap_hdr_sz, copy_sz);
62e76326 634
3196f526 635 readBody(copyInto.data, copy_sz);
528b2c61 636
62e76326 637 return;
e3ef2b09 638 }
62e76326 639
e3ef2b09 640 /*
641 * we don't have what the client wants, but at least we now
642 * know the swap header size.
643 */
528b2c61 644 fileRead();
e3ef2b09 645}
646
f09f5b26 647int
a4b8110e 648storeClientCopyPending(store_client * sc, StoreEntry * e, void *data)
f09f5b26 649{
06d2839d 650#if STORE_CLIENT_LIST_DEBUG
651 assert(sc == storeClientListSearch(e->mem_obj, data));
8b082ed9
FC
652#else
653 (void)data;
edce4d98 654#endif
62e76326 655
edce4d98 656 assert(sc);
06d2839d 657 assert(sc->entry == e);
62e76326 658
90703668 659 if (!sc->_callback.pending())
62e76326 660 return 0;
661
f09f5b26 662 return 1;
663}
664
06d2839d 665/*
666 * This routine hasn't been optimised to take advantage of the
667 * passed sc. Yet.
668 */
f09f5b26 669int
a4b8110e 670storeUnregister(store_client * sc, StoreEntry * e, void *data)
f09f5b26 671{
672 MemObject *mem = e->mem_obj;
06d2839d 673#if STORE_CLIENT_LIST_DEBUG
674 assert(sc == storeClientListSearch(e->mem_obj, data));
8b082ed9
FC
675#else
676 (void)data;
06d2839d 677#endif
62e76326 678
aee3523a 679 if (mem == nullptr)
62e76326 680 return 0;
681
26ac0430 682 debugs(90, 3, "storeUnregister: called for '" << e->getMD5Text() << "'");
62e76326 683
aee3523a 684 if (sc == nullptr) {
bf8fe701 685 debugs(90, 3, "storeUnregister: No matching client for '" << e->getMD5Text() << "'");
62e76326 686 return 0;
2f44bd34 687 }
62e76326 688
0e3f3e0d 689 if (mem->clientCount() == 0) {
bf8fe701 690 debugs(90, 3, "storeUnregister: Consistency failure - store client being unregistered is not in the mem object's list for '" << e->getMD5Text() << "'");
62e76326 691 return 0;
2f44bd34 692 }
62e76326 693
06d2839d 694 dlinkDelete(&sc->node, &mem->clients);
5e263176 695 -- mem->nclients;
62e76326 696
02ba667b
EB
697 const auto swapoutFinished = e->swappedOut() || e->swapoutFailed();
698 if (e->store_status == STORE_OK && !swapoutFinished)
c07cbbf4 699 e->swapOut();
62e76326 700
aee3523a 701 if (sc->swapin_sio != nullptr) {
aa1a691e 702 storeClose(sc->swapin_sio, StoreIOState::readerDone);
aee3523a 703 sc->swapin_sio = nullptr;
5db6bf73 704 ++statCounter.swap.ins;
eb824054 705 }
62e76326 706
1fa761af
EB
707 if (sc->_callback.callback_handler || sc->_callback.notifier) {
708 debugs(90, 3, "forgetting store_client callback for " << *e);
709 // Do not notify: Callers want to stop copying and forget about this
710 // pending copy request. Some would mishandle a notification from here.
711 if (sc->_callback.notifier)
712 sc->_callback.notifier->cancel("storeUnregister");
f09f5b26 713 }
62e76326 714
fa80a8ef 715#if STORE_CLIENT_LIST_DEBUG
716 cbdataReferenceDone(sc->owner);
62e76326 717
fa80a8ef 718#endif
62e76326 719
1fa761af
EB
720 // XXX: We might be inside sc store_client method somewhere up the call
721 // stack. TODO: Convert store_client to AsyncJob to make destruction async.
528b2c61 722 delete sc;
62e76326 723
1bfe9ade 724 assert(e->locked());
178c7e33 725 // An entry locked by others may be unlocked (and destructed) by others, so
f1ba1fba 726 // we must lock again to safely dereference e after CheckQuickAbortIsReasonable().
178c7e33 727 e->lock("storeUnregister");
62e76326 728
f1ba1fba
EB
729 if (CheckQuickAbortIsReasonable(e))
730 e->abort();
a46d2c0e 731 else
732 mem->kickReads();
62e76326 733
0ad2b63b
CT
734#if USE_ADAPTATION
735 e->kickProducer();
736#endif
737
178c7e33 738 e->unlock("storeUnregister");
f09f5b26 739 return 1;
740}
741
f09f5b26 742/* Call handlers waiting for data to be appended to E. */
743void
d88e3c49 744StoreEntry::invokeHandlers()
f09f5b26 745{
70eb3fde
EB
746 if (EBIT_TEST(flags, DELAY_SENDING)) {
747 debugs(90, 3, "DELAY_SENDING is on, exiting " << *this);
748 return;
749 }
750 if (EBIT_TEST(flags, ENTRY_FWD_HDR_WAIT)) {
751 debugs(90, 3, "ENTRY_FWD_HDR_WAIT is on, exiting " << *this);
752 return;
753 }
754
528b2c61 755 /* Commit what we can to disk, if appropriate */
d88e3c49 756 swapOut();
f09f5b26 757 int i = 0;
f09f5b26 758 store_client *sc;
aee3523a 759 dlink_node *nx = nullptr;
06d2839d 760 dlink_node *node;
761
f925268a 762 debugs(90, 3, mem_obj->nclients << " clients; " << *this << ' ' << getMD5Text());
f09f5b26 763 /* walk the entire list looking for valid callbacks */
62e76326 764
f925268a 765 const auto savedContext = CodeContext::Current();
d88e3c49 766 for (node = mem_obj->clients.head; node; node = nx) {
62e76326 767 sc = (store_client *)node->data;
768 nx = node->next;
5db6bf73 769 ++i;
62e76326 770
90703668 771 if (!sc->_callback.pending())
62e76326 772 continue;
773
774 if (sc->flags.disk_io_pending)
775 continue;
776
1fa761af
EB
777 if (sc->flags.store_copying)
778 continue;
779
780 // XXX: If invokeHandlers() is (indirectly) called from a store_client
781 // method, then the above three conditions may not be sufficient to
782 // prevent us from reentering the same store_client object! This
783 // probably does not happen in the current code, but no observed
784 // invariant prevents this from (accidentally) happening in the future.
785
786 // TODO: Convert store_client into AsyncJob; make this call asynchronous
f925268a
AR
787 CodeContext::Reset(sc->_callback.codeContext);
788 debugs(90, 3, "checking client #" << i);
d88e3c49 789 storeClientCopy2(this, sc);
f09f5b26 790 }
f925268a 791 CodeContext::Reset(savedContext);
f09f5b26 792}
793
22bbd840 794// Does not account for remote readers/clients.
f09f5b26 795int
796storePendingNClients(const StoreEntry * e)
797{
f09f5b26 798 MemObject *mem = e->mem_obj;
aee3523a 799 int npend = nullptr == mem ? 0 : mem->nclients;
bf8fe701 800 debugs(90, 3, "storePendingNClients: returning " << npend);
f09f5b26 801 return npend;
802}
77b32a34 803
ae6568e7
AJ
804/* return true if the request should be aborted */
805static bool
806CheckQuickAbortIsReasonable(StoreEntry * entry)
77b32a34 807{
f1ba1fba
EB
808 assert(entry);
809 debugs(90, 3, "entry=" << *entry);
810
811 if (storePendingNClients(entry) > 0) {
812 debugs(90, 3, "quick-abort? NO storePendingNClients() > 0");
813 return false;
814 }
815
23b79630 816 if (Store::Root().transientReaders(*entry)) {
f1ba1fba
EB
817 debugs(90, 3, "quick-abort? NO still have one or more transient readers");
818 return false;
819 }
820
821 if (entry->store_status != STORE_PENDING) {
822 debugs(90, 3, "quick-abort? NO store_status != STORE_PENDING");
823 return false;
824 }
825
826 if (EBIT_TEST(entry->flags, ENTRY_SPECIAL)) {
827 debugs(90, 3, "quick-abort? NO ENTRY_SPECIAL");
828 return false;
829 }
830
23b79630
AR
831 if (shutting_down) {
832 debugs(90, 3, "quick-abort? YES avoid heavy optional work during shutdown");
833 return true;
834 }
835
528b2c61 836 MemObject * const mem = entry->mem_obj;
77b32a34 837 assert(mem);
f1ba1fba 838 debugs(90, 3, "mem=" << mem);
62e76326 839
45e5102d 840 if (mem->request && !mem->request->flags.cachable) {
ae6568e7
AJ
841 debugs(90, 3, "quick-abort? YES !mem->request->flags.cachable");
842 return true;
77b32a34 843 }
62e76326 844
77b32a34 845 if (EBIT_TEST(entry->flags, KEY_PRIVATE)) {
ae6568e7
AJ
846 debugs(90, 3, "quick-abort? YES KEY_PRIVATE");
847 return true;
77b32a34 848 }
62e76326 849
66d51f4f 850 const auto &reply = mem->baseReply();
0e3f3e0d 851
66d51f4f
AR
852 if (reply.hdr_sz <= 0) {
853 // TODO: Check whether this condition works for HTTP/0 responses.
ae6568e7
AJ
854 debugs(90, 3, "quick-abort? YES no object data received yet");
855 return true;
856 }
0e3f3e0d 857
47f6e231 858 if (Config.quickAbort.min < 0) {
ae6568e7
AJ
859 debugs(90, 3, "quick-abort? NO disabled");
860 return false;
77b32a34 861 }
62e76326 862
ae6568e7 863 if (mem->request && mem->request->range && mem->request->getRangeOffsetLimit() < 0) {
66d51f4f 864 // the admin has configured "range_offset_limit none"
ae6568e7
AJ
865 debugs(90, 3, "quick-abort? NO admin configured range replies to full-download");
866 return false;
ab275c7b
AJ
867 }
868
66d51f4f
AR
869 if (reply.content_length < 0) {
870 // XXX: cf.data.pre does not document what should happen in this case
871 // We know that quick_abort is enabled, but no limit can be applied.
872 debugs(90, 3, "quick-abort? YES unknown content length");
873 return true;
874 }
875 const auto expectlen = reply.hdr_sz + reply.content_length;
876
877 int64_t curlen = mem->endOffset();
878
77b32a34 879 if (curlen > expectlen) {
f22b0af0 880 debugs(90, 3, "quick-abort? YES bad content length (" << curlen << " of " << expectlen << " bytes received)");
ae6568e7 881 return true;
77b32a34 882 }
62e76326 883
47f6e231 884 if ((expectlen - curlen) < (Config.quickAbort.min << 10)) {
ae6568e7
AJ
885 debugs(90, 3, "quick-abort? NO only a little more object left to receive");
886 return false;
77b32a34 887 }
62e76326 888
77b32a34 889 if ((expectlen - curlen) > (Config.quickAbort.max << 10)) {
ae6568e7
AJ
890 debugs(90, 3, "quick-abort? YES too much left to go");
891 return true;
77b32a34 892 }
62e76326 893
66d51f4f 894 // XXX: This is absurd! TODO: For positives, "a/(b/c) > d" is "a*c > b*d".
77b32a34 895 if (expectlen < 100) {
ae6568e7
AJ
896 debugs(90, 3, "quick-abort? NO avoid FPE");
897 return false;
77b32a34 898 }
62e76326 899
47f6e231 900 if ((curlen / (expectlen / 100)) > (Config.quickAbort.pct)) {
ae6568e7
AJ
901 debugs(90, 3, "quick-abort? NO past point of no return");
902 return false;
77b32a34 903 }
62e76326 904
ae6568e7
AJ
905 debugs(90, 3, "quick-abort? YES default");
906 return true;
77b32a34 907}
908
c8be6d7b 909void
fcc35180 910store_client::dumpStats(MemBuf * output, int clientNumber) const
c8be6d7b 911{
90703668 912 if (_callback.pending())
62e76326 913 return;
914
4391cd15
AJ
915 output->appendf("\tClient #%d, %p\n", clientNumber, _callback.callback_data);
916 output->appendf("\t\tcopy_offset: %" PRId64 "\n", copyInto.offset);
fa938e45 917 output->appendf("\t\tcopy_size: %" PRIuSIZE "\n", copyInto.length);
4391cd15 918 output->append("\t\tflags:", 8);
62e76326 919
528b2c61 920 if (flags.disk_io_pending)
4391cd15 921 output->append(" disk_io_pending", 16);
62e76326 922
528b2c61 923 if (flags.store_copying)
4391cd15 924 output->append(" store_copying", 14);
62e76326 925
1fa761af
EB
926 if (_callback.notifier)
927 output->append(" notifying", 10);
62e76326 928
4391cd15 929 output->append("\n",1);
528b2c61 930}
c8be6d7b 931
528b2c61 932bool
90703668 933store_client::Callback::pending() const
528b2c61 934{
1fa761af 935 return callback_handler && !notifier;
c8be6d7b 936}
528b2c61 937
f925268a
AR
938store_client::Callback::Callback(STCB *function, void *data):
939 callback_handler(function),
940 callback_data(data),
941 codeContext(CodeContext::Current())
942{
943}
b67e2c8c 944
9a0a18de 945#if USE_DELAY_POOLS
1fa761af
EB
946int
947store_client::bytesWanted() const
948{
949 // TODO: To avoid using stale copyInto, return zero if !_callback.pending()?
950 return delayId.bytesWanted(0, copyInto.length);
951}
952
b67e2c8c 953void
954store_client::setDelayId(DelayId delay_id)
955{
956 delayId = delay_id;
957}
515ec4dc 958#endif
f53969cc 959