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