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