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