]> git.ipfire.org Git - thirdparty/squid.git/blob - src/store_client.cc
Fix two read-ahead problems related to delay pools (or lack of thereof).
[thirdparty/squid.git] / src / store_client.cc
1 /*
2 * Copyright (C) 1996-2017 The Squid Software Foundation and contributors
3 *
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.
7 */
8
9 /* DEBUG: section 90 Storage Manager Client-Side Interface */
10
11 #include "squid.h"
12 #include "event.h"
13 #include "globals.h"
14 #include "HttpReply.h"
15 #include "HttpRequest.h"
16 #include "MemBuf.h"
17 #include "MemObject.h"
18 #include "mime_header.h"
19 #include "profiler/Profiler.h"
20 #include "SquidConfig.h"
21 #include "StatCounters.h"
22 #include "Store.h"
23 #include "store_swapin.h"
24 #include "StoreClient.h"
25 #include "StoreMeta.h"
26 #include "StoreMetaUnpacker.h"
27 #if USE_DELAY_POOLS
28 #include "DelayPools.h"
29 #endif
30
31 /*
32 * NOTE: 'Header' refers to the swapfile metadata header.
33 * 'OBJHeader' refers to the object header, with cannonical
34 * processed object headers (which may derive from FTP/HTTP etc
35 * upstream protocols
36 * 'Body' refers to the swapfile body, which is the full
37 * HTTP reply (including HTTP headers and body).
38 */
39 static StoreIOState::STRCB storeClientReadBody;
40 static StoreIOState::STRCB storeClientReadHeader;
41 static void storeClientCopy2(StoreEntry * e, store_client * sc);
42 static EVH storeClientCopyEvent;
43 static bool CheckQuickAbortIsReasonable(StoreEntry * entry);
44
45 CBDATA_CLASS_INIT(store_client);
46
47 bool
48 store_client::memReaderHasLowerOffset(int64_t anOffset) const
49 {
50 return getType() == STORE_MEM_CLIENT && copyInto.offset < anOffset;
51 }
52
53 int
54 store_client::getType() const
55 {
56 return type;
57 }
58
59 #if STORE_CLIENT_LIST_DEBUG
60 static store_client *
61 storeClientListSearch(const MemObject * mem, void *data)
62 {
63 dlink_node *node;
64 store_client *sc = NULL;
65
66 for (node = mem->clients.head; node; node = node->next) {
67 sc = node->data;
68
69 if (sc->owner == data)
70 return sc;
71 }
72
73 return NULL;
74 }
75
76 int
77 storeClientIsThisAClient(store_client * sc, void *someClient)
78 {
79 return sc->owner == someClient;
80 }
81
82 #endif
83 #include "HttpRequest.h"
84
85 /* add client with fd to client list */
86 store_client *
87 storeClientListAdd(StoreEntry * e, void *data)
88 {
89 MemObject *mem = e->mem_obj;
90 store_client *sc;
91 assert(mem);
92 #if STORE_CLIENT_LIST_DEBUG
93
94 if (storeClientListSearch(mem, data) != NULL)
95 /* XXX die! */
96 assert(1 == 0);
97
98 #endif
99
100 sc = new store_client (e);
101
102 mem->addClient(sc);
103
104 return sc;
105 }
106
107 void
108 store_client::callback(ssize_t sz, bool error)
109 {
110 size_t bSz = 0;
111
112 if (sz >= 0 && !error)
113 bSz = sz;
114
115 StoreIOBuffer result(bSz, 0 ,copyInto.data);
116
117 if (sz < 0 || error)
118 result.flags.error = 1;
119
120 result.offset = cmp_offset;
121 assert(_callback.pending());
122 cmp_offset = copyInto.offset + bSz;
123 STCB *temphandler = _callback.callback_handler;
124 void *cbdata = _callback.callback_data;
125 _callback = Callback(NULL, NULL);
126 copyInto.data = NULL;
127
128 if (cbdataReferenceValid(cbdata))
129 temphandler(cbdata, result);
130
131 cbdataReferenceDone(cbdata);
132 }
133
134 static void
135 storeClientCopyEvent(void *data)
136 {
137 store_client *sc = (store_client *)data;
138 debugs(90, 3, "storeClientCopyEvent: Running");
139 assert (sc->flags.copy_event_pending);
140 sc->flags.copy_event_pending = false;
141
142 if (!sc->_callback.pending())
143 return;
144
145 storeClientCopy2(sc->entry, sc);
146 }
147
148 store_client::store_client(StoreEntry *e) :
149 cmp_offset(0),
150 #if STORE_CLIENT_LIST_DEBUG
151 owner(cbdataReference(data)),
152 #endif
153 entry(e),
154 type(e->storeClientType()),
155 object_ok(true)
156 {
157 flags.disk_io_pending = false;
158 flags.store_copying = false;
159 flags.copy_event_pending = false;
160 ++ entry->refcount;
161
162 if (getType() == STORE_DISK_CLIENT) {
163 /* assert we'll be able to get the data we want */
164 /* maybe we should open swapin_sio here */
165 assert(entry->swap_filen > -1 || entry->swappingOut());
166 }
167 }
168
169 store_client::~store_client()
170 {}
171
172 /* copy bytes requested by the client */
173 void
174 storeClientCopy(store_client * sc,
175 StoreEntry * e,
176 StoreIOBuffer copyInto,
177 STCB * callback,
178 void *data)
179 {
180 assert (sc != NULL);
181 sc->copy(e, copyInto,callback,data);
182 }
183
184 void
185 store_client::copy(StoreEntry * anEntry,
186 StoreIOBuffer copyRequest,
187 STCB * callback_fn,
188 void *data)
189 {
190 assert (anEntry == entry);
191 assert (callback_fn);
192 assert (data);
193 assert(!EBIT_TEST(entry->flags, ENTRY_ABORTED));
194 debugs(90, 3, "store_client::copy: " << entry->getMD5Text() << ", from " <<
195 copyRequest.offset << ", for length " <<
196 (int) copyRequest.length << ", cb " << callback_fn << ", cbdata " <<
197 data);
198
199 #if STORE_CLIENT_LIST_DEBUG
200
201 assert(this == storeClientListSearch(entry->mem_obj, data));
202 #endif
203
204 assert(!_callback.pending());
205 #if ONLYCONTIGUOUSREQUESTS
206
207 assert(cmp_offset == copyRequest.offset);
208 #endif
209 /* range requests will skip into the body */
210 cmp_offset = copyRequest.offset;
211 _callback = Callback (callback_fn, cbdataReference(data));
212 copyInto.data = copyRequest.data;
213 copyInto.length = copyRequest.length;
214 copyInto.offset = copyRequest.offset;
215
216 static bool copying (false);
217 assert (!copying);
218 copying = true;
219 PROF_start(storeClient_kickReads);
220 /* we might be blocking comm reads due to readahead limits
221 * now we have a new offset, trigger those reads...
222 */
223 entry->mem_obj->kickReads();
224 PROF_stop(storeClient_kickReads);
225 copying = false;
226
227 anEntry->lock("store_client::copy"); // see deletion note below
228
229 storeClientCopy2(entry, this);
230
231 // Bug 3480: This store_client object may be deleted now if, for example,
232 // the client rejects the hit response copied above. Use on-stack pointers!
233
234 #if USE_ADAPTATION
235 anEntry->kickProducer();
236 #endif
237 anEntry->unlock("store_client::copy");
238
239 // Add no code here. This object may no longer exist.
240 }
241
242 /// Whether there is (or will be) more entry data for us.
243 bool
244 store_client::moreToSend() const
245 {
246 if (entry->store_status == STORE_PENDING)
247 return true; // there may be more coming
248
249 /* STORE_OK, including aborted entries: no more data is coming */
250
251 const int64_t len = entry->objectLen();
252
253 // If we do not know the entry length, then we have to open the swap file.
254 const bool canSwapIn = entry->swap_filen >= 0;
255 if (len < 0)
256 return canSwapIn;
257
258 if (copyInto.offset >= len)
259 return false; // sent everything there is
260
261 if (canSwapIn)
262 return true; // if we lack prefix, we can swap it in
263
264 // If we cannot swap in, make sure we have what we want in RAM. Otherwise,
265 // scheduleRead calls scheduleDiskRead which asserts without a swap file.
266 const MemObject *mem = entry->mem_obj;
267 return mem &&
268 mem->inmem_lo <= copyInto.offset && copyInto.offset < mem->endOffset();
269 }
270
271 static void
272 storeClientCopy2(StoreEntry * e, store_client * sc)
273 {
274 /* reentrancy not allowed - note this could lead to
275 * dropped events
276 */
277
278 if (sc->flags.copy_event_pending) {
279 return;
280 }
281
282 if (EBIT_TEST(e->flags, ENTRY_FWD_HDR_WAIT)) {
283 debugs(90, 5, "storeClientCopy2: returning because ENTRY_FWD_HDR_WAIT set");
284 return;
285 }
286
287 if (sc->flags.store_copying) {
288 sc->flags.copy_event_pending = true;
289 debugs(90, 3, "storeClientCopy2: Queueing storeClientCopyEvent()");
290 eventAdd("storeClientCopyEvent", storeClientCopyEvent, sc, 0.0, 0);
291 return;
292 }
293
294 debugs(90, 3, "storeClientCopy2: " << e->getMD5Text());
295 assert(sc->_callback.pending());
296 /*
297 * We used to check for ENTRY_ABORTED here. But there were some
298 * problems. For example, we might have a slow client (or two) and
299 * the peer server is reading far ahead and swapping to disk. Even
300 * if the peer aborts, we want to give the client(s)
301 * everything we got before the abort condition occurred.
302 */
303 /* Warning: doCopy may indirectly free itself in callbacks,
304 * hence the lock to keep it active for the duration of
305 * this function
306 * XXX: Locking does not prevent calling sc destructor (it only prevents
307 * freeing sc memory) so sc may become invalid from C++ p.o.v.
308 */
309 CbcPointer<store_client> tmpLock = sc;
310 assert (!sc->flags.store_copying);
311 sc->doCopy(e);
312 assert(!sc->flags.store_copying);
313 }
314
315 void
316 store_client::doCopy(StoreEntry *anEntry)
317 {
318 assert (anEntry == entry);
319 flags.store_copying = true;
320 MemObject *mem = entry->mem_obj;
321
322 debugs(33, 5, "store_client::doCopy: co: " <<
323 copyInto.offset << ", hi: " <<
324 mem->endOffset());
325
326 if (!moreToSend()) {
327 /* There is no more to send! */
328 debugs(33, 3, HERE << "There is no more to send!");
329 callback(0);
330 flags.store_copying = false;
331 return;
332 }
333
334 /* Check that we actually have data */
335 if (anEntry->store_status == STORE_PENDING && copyInto.offset >= mem->endOffset()) {
336 debugs(90, 3, "store_client::doCopy: Waiting for more");
337 flags.store_copying = false;
338 return;
339 }
340
341 /*
342 * Slight weirdness here. We open a swapin file for any
343 * STORE_DISK_CLIENT, even if we can copy the requested chunk
344 * from memory in the next block. We must try to open the
345 * swapin file before sending any data to the client side. If
346 * we postpone the open, and then can not open the file later
347 * on, the client loses big time. Its transfer just gets cut
348 * off. Better to open it early (while the client side handler
349 * is clientCacheHit) so that we can fall back to a cache miss
350 * if needed.
351 */
352
353 if (STORE_DISK_CLIENT == getType() && swapin_sio == NULL) {
354 if (!startSwapin())
355 return; // failure
356 }
357 scheduleRead();
358 }
359
360 /// opens the swapin "file" if possible; otherwise, fail()s and returns false
361 bool
362 store_client::startSwapin()
363 {
364 debugs(90, 3, "store_client::doCopy: Need to open swap in file");
365 /* gotta open the swapin file */
366
367 if (storeTooManyDiskFilesOpen()) {
368 /* yuck -- this causes a TCP_SWAPFAIL_MISS on the client side */
369 fail();
370 flags.store_copying = false;
371 return false;
372 } else if (!flags.disk_io_pending) {
373 /* Don't set store_io_pending here */
374 storeSwapInStart(this);
375
376 if (swapin_sio == NULL) {
377 fail();
378 flags.store_copying = false;
379 return false;
380 }
381
382 return true;
383 } else {
384 debugs(90, DBG_IMPORTANT, "WARNING: Averted multiple fd operation (1)");
385 flags.store_copying = false;
386 return false;
387 }
388 }
389
390 void
391 store_client::scheduleRead()
392 {
393 MemObject *mem = entry->mem_obj;
394
395 if (copyInto.offset >= mem->inmem_lo && copyInto.offset < mem->endOffset())
396 scheduleMemRead();
397 else
398 scheduleDiskRead();
399 }
400
401 void
402 store_client::scheduleDiskRead()
403 {
404 /* What the client wants is not in memory. Schedule a disk read */
405 if (getType() == STORE_DISK_CLIENT) {
406 // we should have called startSwapin() already
407 assert(swapin_sio != NULL);
408 } else if (!swapin_sio && !startSwapin()) {
409 debugs(90, 3, "bailing after swapin start failure for " << *entry);
410 assert(!flags.store_copying);
411 return;
412 }
413
414 assert(!flags.disk_io_pending);
415
416 debugs(90, 3, "reading " << *entry << " from disk");
417
418 fileRead();
419
420 flags.store_copying = false;
421 }
422
423 void
424 store_client::scheduleMemRead()
425 {
426 /* What the client wants is in memory */
427 /* Old style */
428 debugs(90, 3, "store_client::doCopy: Copying normal from memory");
429 size_t sz = entry->mem_obj->data_hdr.copy(copyInto);
430 callback(sz);
431 flags.store_copying = false;
432 }
433
434 void
435 store_client::fileRead()
436 {
437 MemObject *mem = entry->mem_obj;
438
439 assert(_callback.pending());
440 assert(!flags.disk_io_pending);
441 flags.disk_io_pending = true;
442
443 if (mem->swap_hdr_sz != 0)
444 if (entry->swap_status == SWAPOUT_WRITING)
445 assert(mem->swapout.sio->offset() > copyInto.offset + (int64_t)mem->swap_hdr_sz);
446
447 storeRead(swapin_sio,
448 copyInto.data,
449 copyInto.length,
450 copyInto.offset + mem->swap_hdr_sz,
451 mem->swap_hdr_sz == 0 ? storeClientReadHeader
452 : storeClientReadBody,
453 this);
454 }
455
456 void
457 store_client::readBody(const char *, ssize_t len)
458 {
459 int parsed_header = 0;
460
461 // Don't assert disk_io_pending here.. may be called by read_header
462 flags.disk_io_pending = false;
463 assert(_callback.pending());
464 debugs(90, 3, "storeClientReadBody: len " << len << "");
465
466 if (copyInto.offset == 0 && len > 0 && entry->getReply()->sline.status() == Http::scNone) {
467 /* Our structure ! */
468 HttpReply *rep = (HttpReply *) entry->getReply(); // bypass const
469
470 if (!rep->parseCharBuf(copyInto.data, headersEnd(copyInto.data, len))) {
471 debugs(90, DBG_CRITICAL, "Could not parse headers from on disk object");
472 } else {
473 parsed_header = 1;
474 }
475 }
476
477 const HttpReply *rep = entry->getReply();
478 if (len > 0 && rep && entry->mem_obj->inmem_lo == 0 && entry->objectLen() <= (int64_t)Config.Store.maxInMemObjSize && Config.onoff.memory_cache_disk) {
479 storeGetMemSpace(len);
480 // The above may start to free our object so we need to check again
481 if (entry->mem_obj->inmem_lo == 0) {
482 /* Copy read data back into memory.
483 * copyInto.offset includes headers, which is what mem cache needs
484 */
485 int64_t mem_offset = entry->mem_obj->endOffset();
486 if ((copyInto.offset == mem_offset) || (parsed_header && mem_offset == rep->hdr_sz)) {
487 entry->mem_obj->write(StoreIOBuffer(len, copyInto.offset, copyInto.data));
488 }
489 }
490 }
491
492 callback(len);
493 }
494
495 void
496 store_client::fail()
497 {
498 object_ok = false;
499 /* synchronous open failures callback from the store,
500 * before startSwapin detects the failure.
501 * TODO: fix this inconsistent behaviour - probably by
502 * having storeSwapInStart become a callback functions,
503 * not synchronous
504 */
505
506 if (_callback.pending())
507 callback(0, true);
508 }
509
510 static void
511 storeClientReadHeader(void *data, const char *buf, ssize_t len, StoreIOState::Pointer)
512 {
513 store_client *sc = (store_client *)data;
514 sc->readHeader(buf, len);
515 }
516
517 static void
518 storeClientReadBody(void *data, const char *buf, ssize_t len, StoreIOState::Pointer)
519 {
520 store_client *sc = (store_client *)data;
521 sc->readBody(buf, len);
522 }
523
524 bool
525 store_client::unpackHeader(char const *buf, ssize_t len)
526 {
527 int xerrno = errno; // FIXME: where does errno come from?
528 debugs(90, 3, "store_client::unpackHeader: len " << len << "");
529
530 if (len < 0) {
531 debugs(90, 3, "WARNING: unpack error: " << xstrerr(xerrno));
532 return false;
533 }
534
535 int swap_hdr_sz = 0;
536 StoreMetaUnpacker aBuilder(buf, len, &swap_hdr_sz);
537
538 if (!aBuilder.isBufferSane()) {
539 /* oops, bad disk file? */
540 debugs(90, DBG_IMPORTANT, "WARNING: swapfile header inconsistent with available data");
541 return false;
542 }
543
544 tlv *tlv_list = aBuilder.createStoreMeta ();
545
546 if (tlv_list == NULL) {
547 debugs(90, DBG_IMPORTANT, "WARNING: failed to unpack meta data");
548 return false;
549 }
550
551 /*
552 * Check the meta data and make sure we got the right object.
553 */
554 for (tlv *t = tlv_list; t; t = t->next) {
555 if (!t->checkConsistency(entry)) {
556 storeSwapTLVFree(tlv_list);
557 return false;
558 }
559 }
560
561 storeSwapTLVFree(tlv_list);
562
563 assert(swap_hdr_sz >= 0);
564 entry->mem_obj->swap_hdr_sz = swap_hdr_sz;
565 if (entry->swap_file_sz > 0) { // collapsed hits may not know swap_file_sz
566 assert(entry->swap_file_sz >= static_cast<uint64_t>(swap_hdr_sz));
567 entry->mem_obj->object_sz = entry->swap_file_sz - swap_hdr_sz;
568 }
569 debugs(90, 5, "store_client::unpackHeader: swap_file_sz=" <<
570 entry->swap_file_sz << "( " << swap_hdr_sz << " + " <<
571 entry->mem_obj->object_sz << ")");
572 return true;
573 }
574
575 void
576 store_client::readHeader(char const *buf, ssize_t len)
577 {
578 MemObject *const mem = entry->mem_obj;
579
580 assert(flags.disk_io_pending);
581 flags.disk_io_pending = false;
582 assert(_callback.pending());
583
584 // abort if we fail()'d earlier
585 if (!object_ok)
586 return;
587
588 if (!unpackHeader(buf, len)) {
589 fail();
590 return;
591 }
592
593 /*
594 * If our last read got some data the client wants, then give
595 * it to them, otherwise schedule another read.
596 */
597 size_t body_sz = len - mem->swap_hdr_sz;
598
599 if (copyInto.offset < static_cast<int64_t>(body_sz)) {
600 /*
601 * we have (part of) what they want
602 */
603 size_t copy_sz = min(copyInto.length, body_sz);
604 debugs(90, 3, "storeClientReadHeader: copying " << copy_sz << " bytes of body");
605 memmove(copyInto.data, copyInto.data + mem->swap_hdr_sz, copy_sz);
606
607 readBody(copyInto.data, copy_sz);
608
609 return;
610 }
611
612 /*
613 * we don't have what the client wants, but at least we now
614 * know the swap header size.
615 */
616 fileRead();
617 }
618
619 int
620 storeClientCopyPending(store_client * sc, StoreEntry * e, void *data)
621 {
622 #if STORE_CLIENT_LIST_DEBUG
623 assert(sc == storeClientListSearch(e->mem_obj, data));
624 #endif
625 #ifndef SILLY_CODE
626
627 assert(sc);
628 #endif
629
630 assert(sc->entry == e);
631 #if SILLY_CODE
632
633 if (sc == NULL)
634 return 0;
635
636 #endif
637
638 if (!sc->_callback.pending())
639 return 0;
640
641 return 1;
642 }
643
644 /*
645 * This routine hasn't been optimised to take advantage of the
646 * passed sc. Yet.
647 */
648 int
649 storeUnregister(store_client * sc, StoreEntry * e, void *data)
650 {
651 MemObject *mem = e->mem_obj;
652 #if STORE_CLIENT_LIST_DEBUG
653
654 assert(sc == storeClientListSearch(e->mem_obj, data));
655 #endif
656
657 if (mem == NULL)
658 return 0;
659
660 debugs(90, 3, "storeUnregister: called for '" << e->getMD5Text() << "'");
661
662 if (sc == NULL) {
663 debugs(90, 3, "storeUnregister: No matching client for '" << e->getMD5Text() << "'");
664 return 0;
665 }
666
667 if (mem->clientCount() == 0) {
668 debugs(90, 3, "storeUnregister: Consistency failure - store client being unregistered is not in the mem object's list for '" << e->getMD5Text() << "'");
669 return 0;
670 }
671
672 dlinkDelete(&sc->node, &mem->clients);
673 -- mem->nclients;
674
675 if (e->store_status == STORE_OK && e->swap_status != SWAPOUT_DONE)
676 e->swapOut();
677
678 if (sc->swapin_sio != NULL) {
679 storeClose(sc->swapin_sio, StoreIOState::readerDone);
680 sc->swapin_sio = NULL;
681 ++statCounter.swap.ins;
682 }
683
684 if (sc->_callback.pending()) {
685 /* callback with ssize = -1 to indicate unexpected termination */
686 debugs(90, 3, "store_client for " << *e << " has a callback");
687 sc->fail();
688 }
689
690 #if STORE_CLIENT_LIST_DEBUG
691 cbdataReferenceDone(sc->owner);
692
693 #endif
694
695 delete sc;
696
697 assert(e->locked());
698 // An entry locked by others may be unlocked (and destructed) by others, so
699 // we must lock again to safely dereference e after CheckQuickAbortIsReasonable().
700 e->lock("storeUnregister");
701
702 if (CheckQuickAbortIsReasonable(e))
703 e->abort();
704 else
705 mem->kickReads();
706
707 #if USE_ADAPTATION
708 e->kickProducer();
709 #endif
710
711 e->unlock("storeUnregister");
712 return 1;
713 }
714
715 /* Call handlers waiting for data to be appended to E. */
716 void
717 StoreEntry::invokeHandlers()
718 {
719 /* Commit what we can to disk, if appropriate */
720 swapOut();
721 int i = 0;
722 store_client *sc;
723 dlink_node *nx = NULL;
724 dlink_node *node;
725
726 PROF_start(InvokeHandlers);
727
728 debugs(90, 3, "InvokeHandlers: " << getMD5Text() );
729 /* walk the entire list looking for valid callbacks */
730
731 for (node = mem_obj->clients.head; node; node = nx) {
732 sc = (store_client *)node->data;
733 nx = node->next;
734 debugs(90, 3, "StoreEntry::InvokeHandlers: checking client #" << i );
735 ++i;
736
737 if (!sc->_callback.pending())
738 continue;
739
740 if (sc->flags.disk_io_pending)
741 continue;
742
743 storeClientCopy2(this, sc);
744 }
745 PROF_stop(InvokeHandlers);
746 }
747
748 // Does not account for remote readers/clients.
749 int
750 storePendingNClients(const StoreEntry * e)
751 {
752 MemObject *mem = e->mem_obj;
753 int npend = NULL == mem ? 0 : mem->nclients;
754 debugs(90, 3, "storePendingNClients: returning " << npend);
755 return npend;
756 }
757
758 /* return true if the request should be aborted */
759 static bool
760 CheckQuickAbortIsReasonable(StoreEntry * entry)
761 {
762 assert(entry);
763 debugs(90, 3, "entry=" << *entry);
764
765 if (storePendingNClients(entry) > 0) {
766 debugs(90, 3, "quick-abort? NO storePendingNClients() > 0");
767 return false;
768 }
769
770 if (!shutting_down && Store::Root().transientReaders(*entry)) {
771 debugs(90, 3, "quick-abort? NO still have one or more transient readers");
772 return false;
773 }
774
775 if (entry->store_status != STORE_PENDING) {
776 debugs(90, 3, "quick-abort? NO store_status != STORE_PENDING");
777 return false;
778 }
779
780 if (EBIT_TEST(entry->flags, ENTRY_SPECIAL)) {
781 debugs(90, 3, "quick-abort? NO ENTRY_SPECIAL");
782 return false;
783 }
784
785 MemObject * const mem = entry->mem_obj;
786 assert(mem);
787 debugs(90, 3, "mem=" << mem);
788
789 if (mem->request && !mem->request->flags.cachable) {
790 debugs(90, 3, "quick-abort? YES !mem->request->flags.cachable");
791 return true;
792 }
793
794 if (EBIT_TEST(entry->flags, KEY_PRIVATE)) {
795 debugs(90, 3, "quick-abort? YES KEY_PRIVATE");
796 return true;
797 }
798
799 int64_t expectlen = entry->getReply()->content_length + entry->getReply()->hdr_sz;
800
801 if (expectlen < 0) {
802 /* expectlen is < 0 if *no* information about the object has been received */
803 debugs(90, 3, "quick-abort? YES no object data received yet");
804 return true;
805 }
806
807 int64_t curlen = mem->endOffset();
808
809 if (Config.quickAbort.min < 0) {
810 debugs(90, 3, "quick-abort? NO disabled");
811 return false;
812 }
813
814 if (mem->request && mem->request->range && mem->request->getRangeOffsetLimit() < 0) {
815 /* Don't abort if the admin has configured range_ofset -1 to download fully for caching. */
816 debugs(90, 3, "quick-abort? NO admin configured range replies to full-download");
817 return false;
818 }
819
820 if (curlen > expectlen) {
821 debugs(90, 3, "quick-abort? YES bad content length (" << curlen << " of " << expectlen << " bytes received)");
822 return true;
823 }
824
825 if ((expectlen - curlen) < (Config.quickAbort.min << 10)) {
826 debugs(90, 3, "quick-abort? NO only a little more object left to receive");
827 return false;
828 }
829
830 if ((expectlen - curlen) > (Config.quickAbort.max << 10)) {
831 debugs(90, 3, "quick-abort? YES too much left to go");
832 return true;
833 }
834
835 if (expectlen < 100) {
836 debugs(90, 3, "quick-abort? NO avoid FPE");
837 return false;
838 }
839
840 if ((curlen / (expectlen / 100)) > (Config.quickAbort.pct)) {
841 debugs(90, 3, "quick-abort? NO past point of no return");
842 return false;
843 }
844
845 debugs(90, 3, "quick-abort? YES default");
846 return true;
847 }
848
849 void
850 store_client::dumpStats(MemBuf * output, int clientNumber) const
851 {
852 if (_callback.pending())
853 return;
854
855 output->appendf("\tClient #%d, %p\n", clientNumber, _callback.callback_data);
856 output->appendf("\t\tcopy_offset: %" PRId64 "\n", copyInto.offset);
857 output->appendf("\t\tcopy_size: %" PRIuSIZE "\n", copyInto.length);
858 output->append("\t\tflags:", 8);
859
860 if (flags.disk_io_pending)
861 output->append(" disk_io_pending", 16);
862
863 if (flags.store_copying)
864 output->append(" store_copying", 14);
865
866 if (flags.copy_event_pending)
867 output->append(" copy_event_pending", 19);
868
869 output->append("\n",1);
870 }
871
872 bool
873 store_client::Callback::pending() const
874 {
875 return callback_handler && callback_data;
876 }
877
878 store_client::Callback::Callback(STCB *function, void *data) : callback_handler(function), callback_data (data) {}
879
880 #if USE_DELAY_POOLS
881 void
882 store_client::setDelayId(DelayId delay_id)
883 {
884 delayId = delay_id;
885 }
886 #endif
887