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