]> git.ipfire.org Git - thirdparty/squid.git/blame - src/store_client.cc
Maintenance: rework SASL detection (#1694)
[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"
122a6e3c 22#include "sbuf/Stream.h"
4d5904f7 23#include "SquidConfig.h"
122a6e3c 24#include "SquidMath.h"
e4f1fdae 25#include "StatCounters.h"
582c2af2 26#include "Store.h"
d448e1eb 27#include "store/SwapMetaIn.h"
f82b5c64 28#include "store_swapin.h"
602d9612 29#include "StoreClient.h"
9a0a18de 30#if USE_DELAY_POOLS
b67e2c8c 31#include "DelayPools.h"
32#endif
c8be6d7b 33
e3ef2b09 34/*
35 * NOTE: 'Header' refers to the swapfile metadata header.
2f8abb64 36 * 'OBJHeader' refers to the object header, with canonical
f53969cc
SM
37 * processed object headers (which may derive from FTP/HTTP etc
38 * upstream protocols
e3ef2b09 39 * 'Body' refers to the swapfile body, which is the full
40 * HTTP reply (including HTTP headers and body).
41 */
4fcc8876 42static StoreIOState::STRCB storeClientReadBody;
43static StoreIOState::STRCB storeClientReadHeader;
f09f5b26 44static void storeClientCopy2(StoreEntry * e, store_client * sc);
ae6568e7 45static bool CheckQuickAbortIsReasonable(StoreEntry * entry);
f09f5b26 46
b001e822 47CBDATA_CLASS_INIT(store_client);
528b2c61 48
819be284
EB
49/* StoreClient */
50
51bool
52StoreClient::onCollapsingPath() const
53{
54 if (!Config.onoff.collapsed_forwarding)
55 return false;
56
57 if (!Config.accessList.collapsedForwardingAccess)
58 return true;
59
60 ACLFilledChecklist checklist(Config.accessList.collapsedForwardingAccess, nullptr, nullptr);
61 fillChecklist(checklist);
62 return checklist.fastCheck().allowed();
63}
64
65bool
7976fed3 66StoreClient::startCollapsingOn(const StoreEntry &e, const bool doingRevalidation) const
819be284 67{
d2a6dcba
EB
68 if (!e.hittingRequiresCollapsing())
69 return false; // collapsing is impossible due to the entry state
70
71 if (!onCollapsingPath())
72 return false; // collapsing is impossible due to Squid configuration
73
74 /* collapsing is possible; the caller must collapse */
75
76 if (const auto tags = loggingTags()) {
77 if (doingRevalidation)
78 tags->collapsingHistory.revalidationCollapses++;
79 else
80 tags->collapsingHistory.otherCollapses++;
81 }
82
83 debugs(85, 5, e << " doingRevalidation=" << doingRevalidation);
84 return true;
819be284
EB
85}
86
819be284
EB
87/* store_client */
88
528b2c61 89int
90store_client::getType() const
91{
92 return type;
93}
94
06d2839d 95#if STORE_CLIENT_LIST_DEBUG
fa80a8ef 96static store_client *
f09f5b26 97storeClientListSearch(const MemObject * mem, void *data)
98{
06d2839d 99 dlink_node *node;
a1b1756c 100 store_client *sc = nullptr;
62e76326 101
06d2839d 102 for (node = mem->clients.head; node; node = node->next) {
62e76326 103 sc = node->data;
104
105 if (sc->owner == data)
106 return sc;
f09f5b26 107 }
62e76326 108
a1b1756c 109 return nullptr;
f09f5b26 110}
c8be6d7b 111
112int
113storeClientIsThisAClient(store_client * sc, void *someClient)
114{
115 return sc->owner == someClient;
116}
62e76326 117
06d2839d 118#endif
924f73bc 119#include "HttpRequest.h"
f09f5b26 120
121/* add client with fd to client list */
06d2839d 122store_client *
f09f5b26 123storeClientListAdd(StoreEntry * e, void *data)
124{
125 MemObject *mem = e->mem_obj;
f09f5b26 126 store_client *sc;
127 assert(mem);
06d2839d 128#if STORE_CLIENT_LIST_DEBUG
62e76326 129
f09f5b26 130 if (storeClientListSearch(mem, data) != NULL)
62e76326 131 /* XXX die! */
132 assert(1 == 0);
8b082ed9
FC
133#else
134 (void)data;
6b8e7481 135#endif
62e76326 136
528b2c61 137 sc = new store_client (e);
62e76326 138
528b2c61 139 mem->addClient(sc);
62e76326 140
06d2839d 141 return sc;
f09f5b26 142}
143
1fa761af
EB
144/// finishCallback() wrapper; TODO: Add NullaryMemFunT for non-jobs.
145void
146store_client::FinishCallback(store_client * const sc)
147{
148 sc->finishCallback();
149}
62e76326 150
1fa761af
EB
151/// finishes a copy()-STCB sequence by synchronously calling STCB
152void
153store_client::finishCallback()
154{
155 Assure(_callback.callback_handler);
156 Assure(_callback.notifier);
157
122a6e3c
AR
158 // XXX: Some legacy code relies on zero-length buffers having nil data
159 // pointers. Some other legacy code expects "correct" result.offset even
160 // when there is no body to return. Accommodate all those expectations.
161 auto result = StoreIOBuffer(0, copyInto.offset, nullptr);
162 if (object_ok && parsingBuffer && parsingBuffer->contentSize())
163 result = parsingBuffer->packBack();
1fa761af 164 result.flags.error = object_ok ? 0 : 1;
1fa761af 165
3a5c8b44
AR
166 // TODO: Move object_ok handling above into this `if` statement.
167 if (object_ok) {
168 // works for zero hdr_sz cases as well; see also: nextHttpReadOffset()
169 discardableHttpEnd_ = NaturalSum<int64_t>(entry->mem().baseReply().hdr_sz, result.offset, result.length).value();
170 } else {
171 // object_ok is sticky, so we will not be able to use any response bytes
172 discardableHttpEnd_ = entry->mem().endOffset();
173 }
174 debugs(90, 7, "with " << result << "; discardableHttpEnd_=" << discardableHttpEnd_);
175
122a6e3c
AR
176 // no HTTP headers and no body bytes (but not because there was no space)
177 atEof_ = !sendingHttpHeaders() && !result.length && copyInto.length;
178
179 parsingBuffer.reset();
180 ++answers;
181
528b2c61 182 STCB *temphandler = _callback.callback_handler;
6ec49cda
AR
183 const auto cbdata = _callback.cbData.validDone();
184 _callback = Callback();
aee3523a 185 copyInto.data = nullptr;
62e76326 186
6ec49cda 187 if (cbdata)
62e76326 188 temphandler(cbdata, result);
b04e66e0 189}
190
cc8c4af2 191store_client::store_client(StoreEntry *e) :
cc8c4af2
AJ
192#if STORE_CLIENT_LIST_DEBUG
193 owner(cbdataReference(data)),
b67e2c8c 194#endif
cc8c4af2
AJ
195 entry(e),
196 type(e->storeClientType()),
1fa761af 197 object_ok(true),
122a6e3c
AR
198 atEof_(false),
199 answers(0)
528b2c61 200{
836d3c0b
AR
201 Assure(entry);
202 entry->lock("store_client");
203
3dd52a0b 204 flags.disk_io_pending = false;
cc8c4af2 205 flags.store_copying = false;
5db6bf73 206 ++ entry->refcount;
62e76326 207
cc8c4af2 208 if (getType() == STORE_DISK_CLIENT) {
62e76326 209 /* assert we'll be able to get the data we want */
02a2d80b 210 /* maybe we should open swapin_sio here */
02ba667b 211 assert(entry->hasDisk() && !entry->swapoutFailed());
cc8c4af2 212 }
528b2c61 213}
214
2f44bd34 215store_client::~store_client()
836d3c0b
AR
216{
217 assert(entry);
218 entry->unlock("store_client");
219}
2f44bd34 220
f09f5b26 221/* copy bytes requested by the client */
222void
a4b8110e 223storeClientCopy(store_client * sc,
62e76326 224 StoreEntry * e,
225 StoreIOBuffer copyInto,
226 STCB * callback,
227 void *data)
f09f5b26 228{
aee3523a 229 assert (sc != nullptr);
528b2c61 230 sc->copy(e, copyInto,callback,data);
231}
232
233void
234store_client::copy(StoreEntry * anEntry,
62e76326 235 StoreIOBuffer copyRequest,
236 STCB * callback_fn,
237 void *data)
528b2c61 238{
239 assert (anEntry == entry);
240 assert (callback_fn);
241 assert (data);
242 assert(!EBIT_TEST(entry->flags, ENTRY_ABORTED));
bf8fe701 243 debugs(90, 3, "store_client::copy: " << entry->getMD5Text() << ", from " <<
47f6e231 244 copyRequest.offset << ", for length " <<
bf8fe701 245 (int) copyRequest.length << ", cb " << callback_fn << ", cbdata " <<
246 data);
247
06d2839d 248#if STORE_CLIENT_LIST_DEBUG
62e76326 249
528b2c61 250 assert(this == storeClientListSearch(entry->mem_obj, data));
06d2839d 251#endif
62e76326 252
90703668 253 assert(!_callback.pending());
6ec49cda 254 _callback = Callback(callback_fn, data);
528b2c61 255 copyInto.data = copyRequest.data;
256 copyInto.length = copyRequest.length;
257 copyInto.offset = copyRequest.offset;
122a6e3c
AR
258 Assure(copyInto.offset >= 0);
259
260 if (!copyInto.length) {
261 // During the first storeClientCopy() call, a zero-size buffer means
262 // that we will have to drop any HTTP response body bytes we read (with
263 // the HTTP headers from disk). After that, it means we cannot return
264 // anything to the caller at all.
265 debugs(90, 2, "WARNING: zero-size storeClientCopy() buffer: " << copyInto);
266 // keep going; moreToRead() should prevent any from-Store reading
267 }
268
269 // Our nextHttpReadOffset() expects the first copy() call to have zero
270 // offset. More complex code could handle a positive first offset, but it
271 // would only be useful when reading responses from memory: We would not
272 // _delay_ the response (to read the requested HTTP body bytes from disk)
273 // when we already can respond with HTTP headers.
274 Assure(!copyInto.offset || answeredOnce());
275
276 parsingBuffer.emplace(copyInto);
528b2c61 277
3a5c8b44
AR
278 discardableHttpEnd_ = nextHttpReadOffset();
279 debugs(90, 7, "discardableHttpEnd_=" << discardableHttpEnd_);
280
1d5161bd 281 static bool copying (false);
282 assert (!copying);
283 copying = true;
a46d2c0e 284 /* we might be blocking comm reads due to readahead limits
285 * now we have a new offset, trigger those reads...
286 */
287 entry->mem_obj->kickReads();
1d5161bd 288 copying = false;
a46d2c0e 289
4475555f
AR
290 anEntry->lock("store_client::copy"); // see deletion note below
291
528b2c61 292 storeClientCopy2(entry, this);
0ad2b63b 293
4475555f
AR
294 // Bug 3480: This store_client object may be deleted now if, for example,
295 // the client rejects the hit response copied above. Use on-stack pointers!
296
0ad2b63b 297#if USE_ADAPTATION
4475555f 298 anEntry->kickProducer();
0ad2b63b 299#endif
4475555f 300 anEntry->unlock("store_client::copy");
1809cebd 301
1809cebd 302 // Add no code here. This object may no longer exist.
f09f5b26 303}
304
122a6e3c 305/// Whether Store has (or possibly will have) more entry data for us.
f25d697f 306bool
122a6e3c 307store_client::moreToRead() const
07304bf9 308{
122a6e3c
AR
309 if (!copyInto.length)
310 return false; // the client supplied a zero-size buffer
311
f25d697f
AR
312 if (entry->store_status == STORE_PENDING)
313 return true; // there may be more coming
314
315 /* STORE_OK, including aborted entries: no more data is coming */
316
122a6e3c
AR
317 if (canReadFromMemory())
318 return true; // memory has the first byte wanted by the client
62e76326 319
122a6e3c
AR
320 if (!entry->hasDisk())
321 return false; // cannot read anything from disk either
62e76326 322
122a6e3c
AR
323 if (entry->objectLen() >= 0 && copyInto.offset >= entry->contentLen())
324 return false; // the disk cannot have byte(s) wanted by the client
62e76326 325
122a6e3c
AR
326 // we cannot be sure until we swap in metadata and learn contentLen(),
327 // but the disk may have the byte(s) wanted by the client
328 return true;
07304bf9 329}
330
f09f5b26 331static void
332storeClientCopy2(StoreEntry * e, store_client * sc)
333{
528b2c61 334 /* reentrancy not allowed - note this could lead to
1fa761af 335 * dropped notifications about response data availability
528b2c61 336 */
62e76326 337
67fd69de 338 if (sc->flags.store_copying) {
1fa761af 339 debugs(90, 3, "prevented recursive copying for " << *e);
62e76326 340 return;
67fd69de 341 }
62e76326 342
bf8fe701 343 debugs(90, 3, "storeClientCopy2: " << e->getMD5Text());
90703668 344 assert(sc->_callback.pending());
0bb129ee 345 /*
b7fe0ab0 346 * We used to check for ENTRY_ABORTED here. But there were some
0bb129ee 347 * problems. For example, we might have a slow client (or two) and
d5430dc8
AJ
348 * the peer server is reading far ahead and swapping to disk. Even
349 * if the peer aborts, we want to give the client(s)
0bb129ee 350 * everything we got before the abort condition occurred.
351 */
528b2c61 352 sc->doCopy(e);
cfac48c2 353}
354
122a6e3c
AR
355/// Whether our answer, if sent right now, will announce the availability of
356/// HTTP response headers (to the STCB callback) for the first time.
357bool
358store_client::sendingHttpHeaders() const
359{
360 return !answeredOnce() && entry->mem().baseReply().hdr_sz > 0;
361}
362
528b2c61 363void
364store_client::doCopy(StoreEntry *anEntry)
cfac48c2 365{
1fa761af
EB
366 Assure(_callback.pending());
367 Assure(!flags.disk_io_pending);
368 Assure(!flags.store_copying);
369
528b2c61 370 assert (anEntry == entry);
3dd52a0b 371 flags.store_copying = true;
528b2c61 372 MemObject *mem = entry->mem_obj;
cd748f27 373
122a6e3c
AR
374 debugs(33, 5, this << " into " << copyInto <<
375 " hi: " << mem->endOffset() <<
376 " objectLen: " << entry->objectLen() <<
377 " past_answers: " << answers);
add2192d 378
122a6e3c
AR
379 const auto sendHttpHeaders = sendingHttpHeaders();
380
381 if (!sendHttpHeaders && !moreToRead()) {
62e76326 382 /* There is no more to send! */
bf95c10a 383 debugs(33, 3, "There is no more to send!");
122a6e3c 384 noteNews();
3dd52a0b 385 flags.store_copying = false;
62e76326 386 return;
cfac48c2 387 }
62e76326 388
122a6e3c 389 if (!sendHttpHeaders && anEntry->store_status == STORE_PENDING && nextHttpReadOffset() >= mem->endOffset()) {
bf8fe701 390 debugs(90, 3, "store_client::doCopy: Waiting for more");
3dd52a0b 391 flags.store_copying = false;
62e76326 392 return;
cfac48c2 393 }
62e76326 394
cfac48c2 395 /*
396 * Slight weirdness here. We open a swapin file for any
397 * STORE_DISK_CLIENT, even if we can copy the requested chunk
398 * from memory in the next block. We must try to open the
399 * swapin file before sending any data to the client side. If
400 * we postpone the open, and then can not open the file later
401 * on, the client loses big time. Its transfer just gets cut
402 * off. Better to open it early (while the client side handler
403 * is clientCacheHit) so that we can fall back to a cache miss
404 * if needed.
405 */
fa80a8ef 406
aee3523a 407 if (STORE_DISK_CLIENT == getType() && swapin_sio == nullptr) {
0cdcf3d7
AR
408 if (!startSwapin())
409 return; // failure
410 }
122a6e3c 411
6e73b4aa
AR
412 // Send any immediately available body bytes unless we sendHttpHeaders.
413 // TODO: Send those body bytes when we sendHttpHeaders as well.
414 if (!sendHttpHeaders && canReadFromMemory()) {
122a6e3c
AR
415 readFromMemory();
416 noteNews(); // will sendHttpHeaders (if needed) as well
417 flags.store_copying = false;
418 return;
419 }
420
421 if (sendHttpHeaders) {
422 debugs(33, 5, "just send HTTP headers: " << mem->baseReply().hdr_sz);
423 noteNews();
424 flags.store_copying = false;
425 return;
426 }
427
428 // no information that the client needs is available immediately
429 scheduleDiskRead();
4e70dae3 430}
431
0cdcf3d7
AR
432/// opens the swapin "file" if possible; otherwise, fail()s and returns false
433bool
4e70dae3 434store_client::startSwapin()
435{
bf8fe701 436 debugs(90, 3, "store_client::doCopy: Need to open swap in file");
4e70dae3 437 /* gotta open the swapin file */
438
439 if (storeTooManyDiskFilesOpen()) {
440 /* yuck -- this causes a TCP_SWAPFAIL_MISS on the client side */
441 fail();
3dd52a0b 442 flags.store_copying = false;
0cdcf3d7 443 return false;
4e70dae3 444 } else if (!flags.disk_io_pending) {
445 /* Don't set store_io_pending here */
446 storeSwapInStart(this);
62e76326 447
aee3523a 448 if (swapin_sio == nullptr) {
62e76326 449 fail();
3dd52a0b 450 flags.store_copying = false;
0cdcf3d7 451 return false;
62e76326 452 }
62e76326 453
0cdcf3d7 454 return true;
4e70dae3 455 } else {
e0236918 456 debugs(90, DBG_IMPORTANT, "WARNING: Averted multiple fd operation (1)");
3dd52a0b 457 flags.store_copying = false;
0cdcf3d7 458 return false;
cfac48c2 459 }
4e70dae3 460}
62e76326 461
1fa761af
EB
462void
463store_client::noteSwapInDone(const bool error)
464{
465 Assure(_callback.pending());
466 if (error)
467 fail();
468 else
122a6e3c 469 noteNews();
4e70dae3 470}
471
472void
473store_client::scheduleDiskRead()
474{
cd748f27 475 /* What the client wants is not in memory. Schedule a disk read */
0cdcf3d7
AR
476 if (getType() == STORE_DISK_CLIENT) {
477 // we should have called startSwapin() already
aee3523a 478 assert(swapin_sio != nullptr);
2da4bfe6 479 } else if (!swapin_sio && !startSwapin()) {
0cdcf3d7
AR
480 debugs(90, 3, "bailing after swapin start failure for " << *entry);
481 assert(!flags.store_copying);
482 return;
483 }
62e76326 484
528b2c61 485 assert(!flags.disk_io_pending);
62e76326 486
0cdcf3d7 487 debugs(90, 3, "reading " << *entry << " from disk");
62e76326 488
528b2c61 489 fileRead();
62e76326 490
3dd52a0b 491 flags.store_copying = false;
f09f5b26 492}
493
122a6e3c
AR
494/// whether at least one byte wanted by the client is in memory
495bool
496store_client::canReadFromMemory() const
497{
498 const auto &mem = entry->mem();
499 const auto memReadOffset = nextHttpReadOffset();
6e73b4aa 500 // XXX: This (lo <= offset < end) logic does not support Content-Range gaps.
122a6e3c
AR
501 return mem.inmem_lo <= memReadOffset && memReadOffset < mem.endOffset() &&
502 parsingBuffer->spaceSize();
503}
504
505/// The offset of the next stored HTTP response byte wanted by the client.
506int64_t
507store_client::nextHttpReadOffset() const
508{
509 Assure(parsingBuffer);
510 const auto &mem = entry->mem();
511 const auto hdr_sz = mem.baseReply().hdr_sz;
512 // Certain SMP cache manager transactions do not store HTTP headers in
513 // mem_hdr; they store just a kid-specific piece of the future report body.
514 // In such cases, hdr_sz ought to be zero. In all other (known) cases,
515 // mem_hdr contains HTTP response headers (positive hdr_sz if parsed)
516 // followed by HTTP response body. This code math accommodates all cases.
517 return NaturalSum<int64_t>(hdr_sz, copyInto.offset, parsingBuffer->contentSize()).value();
518}
519
520/// Copies at least some of the requested body bytes from MemObject memory,
521/// satisfying the copy() request.
522/// \pre canReadFromMemory() is true
4e70dae3 523void
122a6e3c 524store_client::readFromMemory()
4e70dae3 525{
122a6e3c
AR
526 Assure(parsingBuffer);
527 const auto readInto = parsingBuffer->space().positionAt(nextHttpReadOffset());
528
529 debugs(90, 3, "copying HTTP body bytes from memory into " << readInto);
530 const auto sz = entry->mem_obj->data_hdr.copy(readInto);
531 Assure(sz > 0); // our canReadFromMemory() precondition guarantees that
532 parsingBuffer->appended(readInto.data, sz);
4e70dae3 533}
534
528b2c61 535void
536store_client::fileRead()
f09f5b26 537{
528b2c61 538 MemObject *mem = entry->mem_obj;
539
90703668 540 assert(_callback.pending());
528b2c61 541 assert(!flags.disk_io_pending);
3dd52a0b 542 flags.disk_io_pending = true;
62e76326 543
122a6e3c
AR
544 // mem->swap_hdr_sz is zero here during initial read(s)
545 const auto nextStoreReadOffset = NaturalSum<int64_t>(mem->swap_hdr_sz, nextHttpReadOffset()).value();
546
547 // XXX: If fileRead() is called when we do not yet know mem->swap_hdr_sz,
548 // then we must start reading from disk offset zero to learn it: we cannot
549 // compute correct HTTP response start offset on disk without it. However,
550 // late startSwapin() calls imply that the assertion below might fail.
551 Assure(mem->swap_hdr_sz > 0 || !nextStoreReadOffset);
552
553 // TODO: Remove this assertion. Introduced in 1998 commit 3157c72, it
554 // assumes that swapped out memory is freed unconditionally, but we no
555 // longer do that because trimMemory() path checks lowestMemReaderOffset().
556 // It is also misplaced: We are not swapping out anything here and should
557 // not care about any swapout invariants.
528b2c61 558 if (mem->swap_hdr_sz != 0)
4310f8b0 559 if (entry->swappingOut())
122a6e3c
AR
560 assert(mem->swapout.sio->offset() > nextStoreReadOffset);
561
562 // XXX: We should let individual cache_dirs limit the read size instead, but
563 // we cannot do that without more fixes and research because:
564 // * larger reads corrupt responses when cache_dir uses SharedMemory::get();
565 // * we do not know how to find all I/O code that assumes this limit;
566 // * performance effects of larger disk reads may be negative somewhere.
567 const decltype(StoreIOBuffer::length) maxReadSize = SM_PAGE_SIZE;
568
569 Assure(parsingBuffer);
570 // also, do not read more than we can return (via a copyInto.length buffer)
571 const auto readSize = std::min(copyInto.length, maxReadSize);
572 lastDiskRead = parsingBuffer->makeSpace(readSize).positionAt(nextStoreReadOffset);
573 debugs(90, 5, "into " << lastDiskRead);
62e76326 574
528b2c61 575 storeRead(swapin_sio,
122a6e3c
AR
576 lastDiskRead.data,
577 lastDiskRead.length,
578 lastDiskRead.offset,
62e76326 579 mem->swap_hdr_sz == 0 ? storeClientReadHeader
580 : storeClientReadBody,
581 this);
f09f5b26 582}
583
beae59b0 584void
122a6e3c 585store_client::readBody(const char * const buf, const ssize_t lastIoResult)
beae59b0 586{
122a6e3c 587 Assure(flags.disk_io_pending);
3dd52a0b 588 flags.disk_io_pending = false;
beae59b0 589 assert(_callback.pending());
122a6e3c
AR
590 Assure(parsingBuffer);
591 debugs(90, 3, "got " << lastIoResult << " using " << *parsingBuffer);
62e76326 592
122a6e3c 593 if (lastIoResult < 0)
18102f7d
EB
594 return fail();
595
122a6e3c
AR
596 if (!lastIoResult) {
597 if (answeredOnce())
598 return noteNews();
599
600 debugs(90, DBG_CRITICAL, "ERROR: Truncated HTTP headers in on-disk object");
601 return fail();
06a5ae20 602 }
62e76326 603
122a6e3c
AR
604 assert(lastDiskRead.data == buf);
605 lastDiskRead.length = lastIoResult;
606
607 parsingBuffer->appended(buf, lastIoResult);
608
609 // we know swap_hdr_sz by now and were reading beyond swap metadata because
610 // readHead() would have been called otherwise (to read swap metadata)
611 const auto swap_hdr_sz = entry->mem().swap_hdr_sz;
612 Assure(swap_hdr_sz > 0);
613 Assure(!Less(lastDiskRead.offset, swap_hdr_sz));
614
615 // Map lastDiskRead (i.e. the disk area we just read) to an HTTP reply part.
616 // The bytes are the same, but disk and HTTP offsets differ by swap_hdr_sz.
617 const auto httpOffset = lastDiskRead.offset - swap_hdr_sz;
618 const auto httpPart = StoreIOBuffer(lastDiskRead).positionAt(httpOffset);
619
620 maybeWriteFromDiskToMemory(httpPart);
621 handleBodyFromDisk();
622}
623
624/// de-serializes HTTP response (partially) read from disk storage
625void
626store_client::handleBodyFromDisk()
627{
628 // We cannot de-serialize on-disk HTTP response without MemObject because
629 // without MemObject::swap_hdr_sz we cannot know where that response starts.
630 Assure(entry->mem_obj);
631 Assure(entry->mem_obj->swap_hdr_sz > 0);
632
633 if (!answeredOnce()) {
634 // All on-disk responses have HTTP headers. First disk body read(s)
635 // include HTTP headers that we must parse (if needed) and skip.
39b5a589 636 const auto haveHttpHeaders = entry->hasParsedReplyHeader();
122a6e3c
AR
637 if (!haveHttpHeaders && !parseHttpHeadersFromDisk())
638 return;
639 skipHttpHeadersFromDisk();
640 }
641
642 noteNews();
643}
644
645/// Adds HTTP response data loaded from disk to the memory cache (if
646/// needed/possible). The given part may contain portions of HTTP response
647/// headers and/or HTTP response body.
648void
649store_client::maybeWriteFromDiskToMemory(const StoreIOBuffer &httpResponsePart)
650{
651 // XXX: Reject [memory-]uncachable/unshareable responses instead of assuming
652 // that an HTTP response should be written to MemObject's data_hdr (and that
653 // it may purge already cached entries) just because it "fits" and was
654 // loaded from disk. For example, this response may already be marked for
655 // release. The (complex) cachability decision(s) should be made outside
656 // (and obeyed by) this low-level code.
657 if (httpResponsePart.length && entry->mem_obj->inmem_lo == 0 && entry->objectLen() <= (int64_t)Config.Store.maxInMemObjSize && Config.onoff.memory_cache_disk) {
658 storeGetMemSpace(httpResponsePart.length);
659 // XXX: This "recheck" is not needed because storeGetMemSpace() cannot
660 // purge mem_hdr bytes of a locked entry, and we do lock ours. And
661 // inmem_lo offset itself should not be relevant to appending new bytes.
662 //
836d3c0b 663 // recheck for the above call may purge entry's data from the memory cache
6d3c2758 664 if (entry->mem_obj->inmem_lo == 0) {
122a6e3c
AR
665 // XXX: This code assumes a non-shared memory cache.
666 if (httpResponsePart.offset == entry->mem_obj->endOffset())
667 entry->mem_obj->write(httpResponsePart);
3196f526 668 }
beae59b0 669 }
528b2c61 670}
671
672void
62e76326 673store_client::fail()
528b2c61 674{
1fa761af
EB
675 debugs(90, 3, (object_ok ? "once" : "again"));
676 if (!object_ok)
677 return; // we failed earlier; nothing to do now
678
528b2c61 679 object_ok = false;
1fa761af
EB
680
681 noteNews();
682}
683
684/// if necessary and possible, informs the Store reader about copy() result
685void
686store_client::noteNews()
687{
db2ff94c 688 /* synchronous open failures callback from the store,
689 * before startSwapin detects the failure.
690 * TODO: fix this inconsistent behaviour - probably by
26ac0430 691 * having storeSwapInStart become a callback functions,
db2ff94c 692 * not synchronous
693 */
694
1fa761af
EB
695 if (!_callback.callback_handler) {
696 debugs(90, 5, "client lost interest");
697 return;
698 }
699
700 if (_callback.notifier) {
701 debugs(90, 5, "earlier news is being delivered by " << _callback.notifier);
702 return;
703 }
704
705 _callback.notifier = asyncCall(90, 4, "store_client::FinishCallback", cbdataDialer(store_client::FinishCallback, this));
706 ScheduleCallHere(_callback.notifier);
707
708 Assure(!_callback.pending());
f09f5b26 709}
710
e3ef2b09 711static void
ced8def3 712storeClientReadHeader(void *data, const char *buf, ssize_t len, StoreIOState::Pointer)
e3ef2b09 713{
e6ccf245 714 store_client *sc = (store_client *)data;
528b2c61 715 sc->readHeader(buf, len);
716}
717
beae59b0 718static void
ced8def3 719storeClientReadBody(void *data, const char *buf, ssize_t len, StoreIOState::Pointer)
beae59b0
HN
720{
721 store_client *sc = (store_client *)data;
722 sc->readBody(buf, len);
723}
724
528b2c61 725void
726store_client::readHeader(char const *buf, ssize_t len)
727{
728 MemObject *const mem = entry->mem_obj;
62e76326 729
528b2c61 730 assert(flags.disk_io_pending);
3dd52a0b 731 flags.disk_io_pending = false;
90703668 732 assert(_callback.pending());
528b2c61 733
4ea266b7 734 // abort if we fail()'d earlier
528b2c61 735 if (!object_ok)
62e76326 736 return;
737
122a6e3c
AR
738 Assure(parsingBuffer);
739 debugs(90, 3, "got " << len << " using " << *parsingBuffer);
740
18102f7d
EB
741 if (len < 0)
742 return fail();
743
d448e1eb 744 try {
122a6e3c
AR
745 Assure(!parsingBuffer->contentSize());
746 parsingBuffer->appended(buf, len);
d448e1eb 747 Store::UnpackHitSwapMeta(buf, len, *entry);
122a6e3c 748 parsingBuffer->consume(mem->swap_hdr_sz);
d448e1eb
EB
749 } catch (...) {
750 debugs(90, DBG_IMPORTANT, "ERROR: Failed to unpack Store entry metadata: " << CurrentException);
4ea266b7
CF
751 fail();
752 return;
753 }
754
122a6e3c
AR
755 maybeWriteFromDiskToMemory(parsingBuffer->content());
756 handleBodyFromDisk();
e3ef2b09 757}
758
06d2839d 759/*
760 * This routine hasn't been optimised to take advantage of the
761 * passed sc. Yet.
762 */
f09f5b26 763int
a4b8110e 764storeUnregister(store_client * sc, StoreEntry * e, void *data)
f09f5b26 765{
766 MemObject *mem = e->mem_obj;
06d2839d 767#if STORE_CLIENT_LIST_DEBUG
768 assert(sc == storeClientListSearch(e->mem_obj, data));
8b082ed9
FC
769#else
770 (void)data;
06d2839d 771#endif
62e76326 772
aee3523a 773 if (mem == nullptr)
62e76326 774 return 0;
775
26ac0430 776 debugs(90, 3, "storeUnregister: called for '" << e->getMD5Text() << "'");
62e76326 777
aee3523a 778 if (sc == nullptr) {
bf8fe701 779 debugs(90, 3, "storeUnregister: No matching client for '" << e->getMD5Text() << "'");
62e76326 780 return 0;
2f44bd34 781 }
62e76326 782
0e3f3e0d 783 if (mem->clientCount() == 0) {
bf8fe701 784 debugs(90, 3, "storeUnregister: Consistency failure - store client being unregistered is not in the mem object's list for '" << e->getMD5Text() << "'");
62e76326 785 return 0;
2f44bd34 786 }
62e76326 787
06d2839d 788 dlinkDelete(&sc->node, &mem->clients);
5e263176 789 -- mem->nclients;
62e76326 790
02ba667b
EB
791 const auto swapoutFinished = e->swappedOut() || e->swapoutFailed();
792 if (e->store_status == STORE_OK && !swapoutFinished)
c07cbbf4 793 e->swapOut();
62e76326 794
aee3523a 795 if (sc->swapin_sio != nullptr) {
aa1a691e 796 storeClose(sc->swapin_sio, StoreIOState::readerDone);
aee3523a 797 sc->swapin_sio = nullptr;
5db6bf73 798 ++statCounter.swap.ins;
eb824054 799 }
62e76326 800
1fa761af
EB
801 if (sc->_callback.callback_handler || sc->_callback.notifier) {
802 debugs(90, 3, "forgetting store_client callback for " << *e);
803 // Do not notify: Callers want to stop copying and forget about this
804 // pending copy request. Some would mishandle a notification from here.
805 if (sc->_callback.notifier)
806 sc->_callback.notifier->cancel("storeUnregister");
f09f5b26 807 }
62e76326 808
fa80a8ef 809#if STORE_CLIENT_LIST_DEBUG
810 cbdataReferenceDone(sc->owner);
62e76326 811
fa80a8ef 812#endif
62e76326 813
836d3c0b
AR
814 // We must lock to safely dereference e below, after deleting sc and after
815 // calling CheckQuickAbortIsReasonable().
816 e->lock("storeUnregister");
817
1fa761af
EB
818 // XXX: We might be inside sc store_client method somewhere up the call
819 // stack. TODO: Convert store_client to AsyncJob to make destruction async.
528b2c61 820 delete sc;
62e76326 821
f1ba1fba
EB
822 if (CheckQuickAbortIsReasonable(e))
823 e->abort();
a46d2c0e 824 else
825 mem->kickReads();
62e76326 826
0ad2b63b
CT
827#if USE_ADAPTATION
828 e->kickProducer();
829#endif
830
178c7e33 831 e->unlock("storeUnregister");
f09f5b26 832 return 1;
833}
834
f09f5b26 835/* Call handlers waiting for data to be appended to E. */
836void
d88e3c49 837StoreEntry::invokeHandlers()
f09f5b26 838{
70eb3fde
EB
839 if (EBIT_TEST(flags, DELAY_SENDING)) {
840 debugs(90, 3, "DELAY_SENDING is on, exiting " << *this);
841 return;
842 }
843 if (EBIT_TEST(flags, ENTRY_FWD_HDR_WAIT)) {
844 debugs(90, 3, "ENTRY_FWD_HDR_WAIT is on, exiting " << *this);
845 return;
846 }
847
528b2c61 848 /* Commit what we can to disk, if appropriate */
d88e3c49 849 swapOut();
f09f5b26 850 int i = 0;
f09f5b26 851 store_client *sc;
aee3523a 852 dlink_node *nx = nullptr;
06d2839d 853 dlink_node *node;
854
f925268a 855 debugs(90, 3, mem_obj->nclients << " clients; " << *this << ' ' << getMD5Text());
f09f5b26 856 /* walk the entire list looking for valid callbacks */
62e76326 857
f925268a 858 const auto savedContext = CodeContext::Current();
d88e3c49 859 for (node = mem_obj->clients.head; node; node = nx) {
62e76326 860 sc = (store_client *)node->data;
861 nx = node->next;
5db6bf73 862 ++i;
62e76326 863
90703668 864 if (!sc->_callback.pending())
62e76326 865 continue;
866
867 if (sc->flags.disk_io_pending)
868 continue;
869
1fa761af
EB
870 if (sc->flags.store_copying)
871 continue;
872
873 // XXX: If invokeHandlers() is (indirectly) called from a store_client
874 // method, then the above three conditions may not be sufficient to
875 // prevent us from reentering the same store_client object! This
876 // probably does not happen in the current code, but no observed
877 // invariant prevents this from (accidentally) happening in the future.
878
879 // TODO: Convert store_client into AsyncJob; make this call asynchronous
f925268a
AR
880 CodeContext::Reset(sc->_callback.codeContext);
881 debugs(90, 3, "checking client #" << i);
d88e3c49 882 storeClientCopy2(this, sc);
f09f5b26 883 }
f925268a 884 CodeContext::Reset(savedContext);
f09f5b26 885}
886
22bbd840 887// Does not account for remote readers/clients.
f09f5b26 888int
889storePendingNClients(const StoreEntry * e)
890{
f09f5b26 891 MemObject *mem = e->mem_obj;
aee3523a 892 int npend = nullptr == mem ? 0 : mem->nclients;
bf8fe701 893 debugs(90, 3, "storePendingNClients: returning " << npend);
f09f5b26 894 return npend;
895}
77b32a34 896
ae6568e7
AJ
897/* return true if the request should be aborted */
898static bool
899CheckQuickAbortIsReasonable(StoreEntry * entry)
77b32a34 900{
f1ba1fba
EB
901 assert(entry);
902 debugs(90, 3, "entry=" << *entry);
903
904 if (storePendingNClients(entry) > 0) {
905 debugs(90, 3, "quick-abort? NO storePendingNClients() > 0");
906 return false;
907 }
908
23b79630 909 if (Store::Root().transientReaders(*entry)) {
f1ba1fba
EB
910 debugs(90, 3, "quick-abort? NO still have one or more transient readers");
911 return false;
912 }
913
914 if (entry->store_status != STORE_PENDING) {
915 debugs(90, 3, "quick-abort? NO store_status != STORE_PENDING");
916 return false;
917 }
918
919 if (EBIT_TEST(entry->flags, ENTRY_SPECIAL)) {
920 debugs(90, 3, "quick-abort? NO ENTRY_SPECIAL");
921 return false;
922 }
923
23b79630
AR
924 if (shutting_down) {
925 debugs(90, 3, "quick-abort? YES avoid heavy optional work during shutdown");
926 return true;
927 }
928
528b2c61 929 MemObject * const mem = entry->mem_obj;
77b32a34 930 assert(mem);
f1ba1fba 931 debugs(90, 3, "mem=" << mem);
62e76326 932
45e5102d 933 if (mem->request && !mem->request->flags.cachable) {
ae6568e7
AJ
934 debugs(90, 3, "quick-abort? YES !mem->request->flags.cachable");
935 return true;
77b32a34 936 }
62e76326 937
77b32a34 938 if (EBIT_TEST(entry->flags, KEY_PRIVATE)) {
ae6568e7
AJ
939 debugs(90, 3, "quick-abort? YES KEY_PRIVATE");
940 return true;
77b32a34 941 }
62e76326 942
66d51f4f 943 const auto &reply = mem->baseReply();
0e3f3e0d 944
66d51f4f
AR
945 if (reply.hdr_sz <= 0) {
946 // TODO: Check whether this condition works for HTTP/0 responses.
ae6568e7
AJ
947 debugs(90, 3, "quick-abort? YES no object data received yet");
948 return true;
949 }
0e3f3e0d 950
47f6e231 951 if (Config.quickAbort.min < 0) {
ae6568e7
AJ
952 debugs(90, 3, "quick-abort? NO disabled");
953 return false;
77b32a34 954 }
62e76326 955
ae6568e7 956 if (mem->request && mem->request->range && mem->request->getRangeOffsetLimit() < 0) {
66d51f4f 957 // the admin has configured "range_offset_limit none"
ae6568e7
AJ
958 debugs(90, 3, "quick-abort? NO admin configured range replies to full-download");
959 return false;
ab275c7b
AJ
960 }
961
66d51f4f
AR
962 if (reply.content_length < 0) {
963 // XXX: cf.data.pre does not document what should happen in this case
964 // We know that quick_abort is enabled, but no limit can be applied.
965 debugs(90, 3, "quick-abort? YES unknown content length");
966 return true;
967 }
968 const auto expectlen = reply.hdr_sz + reply.content_length;
969
970 int64_t curlen = mem->endOffset();
971
77b32a34 972 if (curlen > expectlen) {
f22b0af0 973 debugs(90, 3, "quick-abort? YES bad content length (" << curlen << " of " << expectlen << " bytes received)");
ae6568e7 974 return true;
77b32a34 975 }
62e76326 976
47f6e231 977 if ((expectlen - curlen) < (Config.quickAbort.min << 10)) {
ae6568e7
AJ
978 debugs(90, 3, "quick-abort? NO only a little more object left to receive");
979 return false;
77b32a34 980 }
62e76326 981
77b32a34 982 if ((expectlen - curlen) > (Config.quickAbort.max << 10)) {
ae6568e7
AJ
983 debugs(90, 3, "quick-abort? YES too much left to go");
984 return true;
77b32a34 985 }
62e76326 986
66d51f4f 987 // XXX: This is absurd! TODO: For positives, "a/(b/c) > d" is "a*c > b*d".
77b32a34 988 if (expectlen < 100) {
ae6568e7
AJ
989 debugs(90, 3, "quick-abort? NO avoid FPE");
990 return false;
77b32a34 991 }
62e76326 992
47f6e231 993 if ((curlen / (expectlen / 100)) > (Config.quickAbort.pct)) {
ae6568e7
AJ
994 debugs(90, 3, "quick-abort? NO past point of no return");
995 return false;
77b32a34 996 }
62e76326 997
ae6568e7
AJ
998 debugs(90, 3, "quick-abort? YES default");
999 return true;
77b32a34 1000}
1001
122a6e3c
AR
1002/// parses HTTP header bytes loaded from disk
1003/// \returns false if fail() or scheduleDiskRead() has been called and, hence,
1004/// the caller should just quit without any further action
1005bool
1006store_client::parseHttpHeadersFromDisk()
1007{
1008 try {
1009 return tryParsingHttpHeaders();
1010 } catch (...) {
1011 // XXX: Our parser enforces Config.maxReplyHeaderSize limit, but our
1012 // packer does not. Since packing might increase header size, we may
1013 // cache a header that we cannot parse and get here. Same for MemStore.
1014 debugs(90, DBG_CRITICAL, "ERROR: Cannot parse on-disk HTTP headers" <<
1015 Debug::Extra << "exception: " << CurrentException <<
1016 Debug::Extra << "raw input size: " << parsingBuffer->contentSize() << " bytes" <<
1017 Debug::Extra << "current buffer capacity: " << parsingBuffer->capacity() << " bytes");
1018 fail();
1019 return false;
1020 }
1021}
1022
1023/// parseHttpHeadersFromDisk() helper
1024/// \copydoc parseHttpHeaders()
1025bool
1026store_client::tryParsingHttpHeaders()
1027{
1028 Assure(parsingBuffer);
1029 Assure(!copyInto.offset); // otherwise, parsingBuffer cannot have HTTP response headers
1030 auto &adjustableReply = entry->mem().adjustableBaseReply();
1031 if (adjustableReply.parseTerminatedPrefix(parsingBuffer->c_str(), parsingBuffer->contentSize()))
1032 return true;
1033
1034 // TODO: Optimize by checking memory as well. For simplicity sake, we
1035 // continue on the disk-reading path, but readFromMemory() can give us the
1036 // missing header bytes immediately if a concurrent request put those bytes
1037 // into memory while we were waiting for our disk response.
1038 scheduleDiskRead();
1039 return false;
1040}
1041
1042/// skips HTTP header bytes previously loaded from disk
1043void
1044store_client::skipHttpHeadersFromDisk()
1045{
1046 const auto hdr_sz = entry->mem_obj->baseReply().hdr_sz;
1047 Assure(hdr_sz > 0); // all on-disk responses have HTTP headers
1048 if (Less(parsingBuffer->contentSize(), hdr_sz)) {
1049 debugs(90, 5, "discovered " << hdr_sz << "-byte HTTP headers in memory after reading some of them from disk: " << *parsingBuffer);
1050 parsingBuffer->consume(parsingBuffer->contentSize()); // skip loaded HTTP header prefix
1051 } else {
1052 parsingBuffer->consume(hdr_sz); // skip loaded HTTP headers
1053 const auto httpBodyBytesAfterHeader = parsingBuffer->contentSize(); // may be zero
1054 Assure(httpBodyBytesAfterHeader <= copyInto.length);
1055 debugs(90, 5, "read HTTP body prefix: " << httpBodyBytesAfterHeader);
1056 }
1057}
1058
c8be6d7b 1059void
fcc35180 1060store_client::dumpStats(MemBuf * output, int clientNumber) const
c8be6d7b 1061{
90703668 1062 if (_callback.pending())
62e76326 1063 return;
1064
6ec49cda 1065 output->appendf("\tClient #%d, %p\n", clientNumber, this);
4391cd15 1066 output->appendf("\t\tcopy_offset: %" PRId64 "\n", copyInto.offset);
a20724e4 1067 output->appendf("\t\tcopy_size: %zu\n", copyInto.length);
4391cd15 1068 output->append("\t\tflags:", 8);
62e76326 1069
528b2c61 1070 if (flags.disk_io_pending)
4391cd15 1071 output->append(" disk_io_pending", 16);
62e76326 1072
528b2c61 1073 if (flags.store_copying)
4391cd15 1074 output->append(" store_copying", 14);
62e76326 1075
1fa761af
EB
1076 if (_callback.notifier)
1077 output->append(" notifying", 10);
62e76326 1078
4391cd15 1079 output->append("\n",1);
528b2c61 1080}
c8be6d7b 1081
528b2c61 1082bool
90703668 1083store_client::Callback::pending() const
528b2c61 1084{
1fa761af 1085 return callback_handler && !notifier;
c8be6d7b 1086}
528b2c61 1087
f925268a
AR
1088store_client::Callback::Callback(STCB *function, void *data):
1089 callback_handler(function),
6ec49cda 1090 cbData(data),
f925268a
AR
1091 codeContext(CodeContext::Current())
1092{
1093}
b67e2c8c 1094
9a0a18de 1095#if USE_DELAY_POOLS
1fa761af
EB
1096int
1097store_client::bytesWanted() const
1098{
1099 // TODO: To avoid using stale copyInto, return zero if !_callback.pending()?
1100 return delayId.bytesWanted(0, copyInto.length);
1101}
1102
b67e2c8c 1103void
1104store_client::setDelayId(DelayId delay_id)
1105{
1106 delayId = delay_id;
1107}
515ec4dc 1108#endif
f53969cc 1109