]> git.ipfire.org Git - thirdparty/squid.git/blame - src/store_client.cc
Bootstrapped
[thirdparty/squid.git] / src / store_client.cc
CommitLineData
9cef6668 1
2/*
1d5161bd 3 * $Id: store_client.cc,v 1.128 2003/06/24 12:42:27 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
c8be6d7b 47
48CBDATA_TYPE(store_client);
f09f5b26 49
e3ef2b09 50/*
51 * NOTE: 'Header' refers to the swapfile metadata header.
528b2c61 52 * 'OBJHeader' refers to the object header, with cannonical
53 * processed object headers (which may derive from FTP/HTTP etc
54 * upstream protocols
e3ef2b09 55 * 'Body' refers to the swapfile body, which is the full
56 * HTTP reply (including HTTP headers and body).
57 */
2391a162 58static STRCB storeClientReadBody;
59static STRCB storeClientReadHeader;
f09f5b26 60static void storeClientCopy2(StoreEntry * e, store_client * sc);
d6f51e3c 61static EVH storeClientCopyEvent;
77b32a34 62static int CheckQuickAbort2(StoreEntry * entry);
63static void CheckQuickAbort(StoreEntry * entry);
f09f5b26 64
528b2c61 65MemPool *store_client::pool = NULL;
66
67void *
68store_client::operator new (size_t byteCount)
69{
70 /* derived classes with different sizes must implement their own new */
71 assert (byteCount == sizeof (store_client));
72 CBDATA_INIT_TYPE(store_client);
73 return cbdataAlloc(store_client);
74}
75
76void
77store_client::operator delete (void *address)
78{
101a3d6f 79 cbdataFree (address);
528b2c61 80}
81
82bool
83store_client::memReaderHasLowerOffset(off_t anOffset) const
84{
85 return getType() == STORE_MEM_CLIENT && copyInto.offset < anOffset;
86}
87
88int
89store_client::getType() const
90{
91 return type;
92}
93
06d2839d 94#if STORE_CLIENT_LIST_DEBUG
fa80a8ef 95static store_client *
f09f5b26 96storeClientListSearch(const MemObject * mem, void *data)
97{
06d2839d 98 dlink_node *node;
99 store_client *sc = NULL;
62e76326 100
06d2839d 101 for (node = mem->clients.head; node; node = node->next) {
62e76326 102 sc = node->data;
103
104 if (sc->owner == data)
105 return sc;
f09f5b26 106 }
62e76326 107
06d2839d 108 return NULL;
f09f5b26 109}
c8be6d7b 110
111int
112storeClientIsThisAClient(store_client * sc, void *someClient)
113{
114 return sc->owner == someClient;
115}
62e76326 116
06d2839d 117#endif
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;
154 assert(callbackPending());
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
528b2c61 175 if (!sc->callbackPending())
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
528b2c61 240 assert(!callbackPending());
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());
528b2c61 316 assert(sc->callbackPending());
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
528b2c61 370 if (STORE_DISK_CLIENT == getType() && NULL == swapin_sio.getRaw()) {
62e76326 371 debug(90, 3)("store_client::doCopy: Need to open swap in file\n");
372 /* gotta open the swapin file */
373
374 if (storeTooManyDiskFilesOpen()) {
375 /* yuck -- this causes a TCP_SWAPFAIL_MISS on the client side */
376 fail();
377 flags.store_copying = 0;
378 return;
379 } else if (!flags.disk_io_pending) {
380 /* Don't set store_io_pending here */
381 storeSwapInStart(this);
382
383 if (NULL == swapin_sio.getRaw()) {
384 fail();
385 flags.store_copying = 0;
386 return;
387 }
388
389 /*
390 * If the open succeeds we either copy from memory, or
391 * schedule a disk read in the next block.
392 */
393 } else {
394 debug (90, 1)("WARNING: Averted multiple fd operation (1)\n");
395 flags.store_copying = 0;
396 return;
397 }
f09f5b26 398 }
62e76326 399
528b2c61 400 if (copyInto.offset >= mem->inmem_lo && copyInto.offset < mem->endOffset()) {
62e76326 401 /* What the client wants is in memory */
402 /* Old style */
403 debug(90, 3)("store_client::doCopy: Copying normal from memory\n");
404 size_t sz = mem->data_hdr.copy(copyInto.offset, copyInto.data,
405 copyInto.length);
406 callback(sz);
407 flags.store_copying = 0;
408 return;
cfac48c2 409 }
62e76326 410
cd748f27 411 /* What the client wants is not in memory. Schedule a disk read */
528b2c61 412 assert(STORE_DISK_CLIENT == getType());
62e76326 413
528b2c61 414 assert(!flags.disk_io_pending);
62e76326 415
2f44bd34 416 debug(90, 3)("store_client::doCopy: reading from STORE\n");
62e76326 417
528b2c61 418 fileRead();
62e76326 419
528b2c61 420 flags.store_copying = 0;
f09f5b26 421}
422
528b2c61 423void
424store_client::fileRead()
f09f5b26 425{
528b2c61 426 MemObject *mem = entry->mem_obj;
427
428 assert(callbackPending());
429 assert(!flags.disk_io_pending);
430 flags.disk_io_pending = 1;
62e76326 431
528b2c61 432 if (mem->swap_hdr_sz != 0)
62e76326 433 if (entry->swap_status == SWAPOUT_WRITING)
434 assert(mem->swapout.sio->offset() > copyInto.offset + (off_t)mem->swap_hdr_sz);
435
528b2c61 436 storeRead(swapin_sio,
62e76326 437 copyInto.data,
438 copyInto.length,
439 copyInto.offset + mem->swap_hdr_sz,
440 mem->swap_hdr_sz == 0 ? storeClientReadHeader
441 : storeClientReadBody,
442 this);
f09f5b26 443}
444
445static void
5bd1abac 446storeClientReadBody(void *data, const char *buf, ssize_t len)
f09f5b26 447{
e6ccf245 448 store_client *sc = (store_client *)data;
27002b34 449 assert(sc->flags.disk_io_pending);
f115fadd 450 sc->flags.disk_io_pending = 0;
528b2c61 451 assert(sc->callbackPending());
2f44bd34 452 debug(90, 3)("storeClientReadBody: len %d", (int) len);
62e76326 453
528b2c61 454 if (sc->copyInto.offset == 0 && len > 0 && sc->entry->getReply()->sline.status == 0)
62e76326 455 /* Our structure ! */
456 if (!httpReplyParse((HttpReply *)sc->entry->getReply(), sc->copyInto.data, headersEnd(sc->copyInto.data, len))) {
457 debug (90,0)("Could not parse headers from on disk object\n");
458 }
459
528b2c61 460 sc->callback(len);
461}
462
463void
62e76326 464store_client::fail()
528b2c61 465{
466 object_ok = false;
467 callback(0, true);
f09f5b26 468}
469
e3ef2b09 470static void
5bd1abac 471storeClientReadHeader(void *data, const char *buf, ssize_t len)
e3ef2b09 472{
e6ccf245 473 store_client *sc = (store_client *)data;
528b2c61 474 sc->readHeader(buf, len);
475}
476
477void
478store_client::unpackHeader(char const *buf, ssize_t len)
479{
2f44bd34 480 debug(90, 3)("store_client::unpackHeader: len %d", (int) len);
62e76326 481
e3ef2b09 482 if (len < 0) {
62e76326 483 debug(90, 3)("store_client::unpackHeader: %s", xstrerror());
484 fail();
485 return;
e3ef2b09 486 }
62e76326 487
528b2c61 488 int swap_hdr_sz = 0;
489 StoreMetaUnpacker aBuilder(buf, len, &swap_hdr_sz);
62e76326 490
528b2c61 491 if (!aBuilder.isBufferSane()) {
62e76326 492 /* oops, bad disk file? */
493 debug(90, 1) ("WARNING: swapfile header inconsistent with available data\n");
494 fail();
495 return;
9bc73deb 496 }
62e76326 497
528b2c61 498 tlv *tlv_list = aBuilder.createStoreMeta ();
62e76326 499
e3ef2b09 500 if (tlv_list == NULL) {
62e76326 501 debug(90, 1) ("WARNING: failed to unpack meta data\n");
502 fail();
503 return;
e3ef2b09 504 }
62e76326 505
e3ef2b09 506 /*
7e3ce7b9 507 * Check the meta data and make sure we got the right object.
e3ef2b09 508 */
528b2c61 509 for (tlv *t = tlv_list; t; t = t->next) {
62e76326 510 if (!t->checkConsistency(entry)) {
511 storeSwapTLVFree(tlv_list);
512 fail();
513 return;
514 }
7e3ce7b9 515 }
62e76326 516
07304bf9 517 storeSwapTLVFree(tlv_list);
528b2c61 518
519 entry->mem_obj->swap_hdr_sz = swap_hdr_sz;
520 entry->mem_obj->object_sz = entry->swap_file_sz - swap_hdr_sz;
521
522}
523
524void
525store_client::readHeader(char const *buf, ssize_t len)
526{
527 MemObject *const mem = entry->mem_obj;
62e76326 528
528b2c61 529 assert(flags.disk_io_pending);
530 flags.disk_io_pending = 0;
531 assert(callbackPending());
532
533 unpackHeader (buf, len);
62e76326 534
528b2c61 535 if (!object_ok)
62e76326 536 return;
537
e3ef2b09 538 /*
539 * If our last read got some data the client wants, then give
540 * it to them, otherwise schedule another read.
541 */
528b2c61 542 size_t body_sz = len - mem->swap_hdr_sz;
62e76326 543
528b2c61 544 if (static_cast<size_t>(copyInto.offset) < body_sz) {
62e76326 545 /*
546 * we have (part of) what they want
547 */
548 size_t copy_sz = XMIN(copyInto.length, body_sz)
549 ;
550 debug(90, 3) ("storeClientReadHeader: copying %d bytes of body\n",
551 (int) copy_sz);
552 xmemmove(copyInto.data, copyInto.data + mem->swap_hdr_sz, copy_sz);
553
554 if (copyInto.offset == 0 && len > 0 && entry->getReply()->sline.status == 0)
555 /* Our structure ! */
556 if (!httpReplyParse((HttpReply *)entry->getReply(), copyInto.data,
557 headersEnd(copyInto.data, copy_sz))) {
558 debug (90,0)("could not parse headers from on disk structure!\n");
559 }
528b2c61 560
561 callback(copy_sz);
62e76326 562 return;
e3ef2b09 563 }
62e76326 564
e3ef2b09 565 /*
566 * we don't have what the client wants, but at least we now
567 * know the swap header size.
568 */
528b2c61 569 fileRead();
e3ef2b09 570}
571
f09f5b26 572int
a4b8110e 573storeClientCopyPending(store_client * sc, StoreEntry * e, void *data)
f09f5b26 574{
06d2839d 575#if STORE_CLIENT_LIST_DEBUG
576 assert(sc == storeClientListSearch(e->mem_obj, data));
edce4d98 577#endif
578#ifndef SILLY_CODE
62e76326 579
edce4d98 580 assert(sc);
06d2839d 581#endif
62e76326 582
06d2839d 583 assert(sc->entry == e);
edce4d98 584#if SILLY_CODE
62e76326 585
f09f5b26 586 if (sc == NULL)
62e76326 587 return 0;
588
edce4d98 589#endif
62e76326 590
528b2c61 591 if (!sc->callbackPending())
62e76326 592 return 0;
593
f09f5b26 594 return 1;
595}
596
06d2839d 597/*
598 * This routine hasn't been optimised to take advantage of the
599 * passed sc. Yet.
600 */
f09f5b26 601int
a4b8110e 602storeUnregister(store_client * sc, StoreEntry * e, void *data)
f09f5b26 603{
604 MemObject *mem = e->mem_obj;
06d2839d 605#if STORE_CLIENT_LIST_DEBUG
62e76326 606
06d2839d 607 assert(sc == storeClientListSearch(e->mem_obj, data));
608#endif
62e76326 609
f09f5b26 610 if (mem == NULL)
62e76326 611 return 0;
612
2f44bd34 613 debug(90, 3) ("storeUnregister: called for '%s'\n", e->getMD5Text());
62e76326 614
2f44bd34 615 if (sc == NULL) {
62e76326 616 debug(90, 3) ("storeUnregister: No matching client for '%s'\n", e->getMD5Text());
617 return 0;
2f44bd34 618 }
62e76326 619
0e3f3e0d 620 if (mem->clientCount() == 0) {
62e76326 621 debug(90, 3) ("storeUnregister: Consistency failure - store client being unregistered is not in the mem object's list for '%s'\n", e->getMD5Text());
622 return 0;
2f44bd34 623 }
62e76326 624
0e3f3e0d 625 if (mem->clientIsFirst(sc)) {
62e76326 626 /*
627 * If we are unregistering the _first_ client for this
628 * entry, then we have to reset the client FD to -1.
629 */
630 mem->fd = -1;
4c454c5c 631 }
62e76326 632
06d2839d 633 dlinkDelete(&sc->node, &mem->clients);
f09f5b26 634 mem->nclients--;
62e76326 635
f09f5b26 636 if (e->store_status == STORE_OK && e->swap_status != SWAPOUT_DONE)
62e76326 637 storeSwapOut(e);
638
d3b3ab85 639 if (sc->swapin_sio.getRaw()) {
62e76326 640 storeClose(sc->swapin_sio);
641 sc->swapin_sio = NULL;
642 statCounter.swap.ins++;
eb824054 643 }
62e76326 644
645 if (sc->callbackPending()) {
646 /* callback with ssize = -1 to indicate unexpected termination */
647 debug(90, 3) ("storeUnregister: store_client for %s has a callback\n",
648 mem->url);
649 sc->fail();
f09f5b26 650 }
62e76326 651
fa80a8ef 652#if STORE_CLIENT_LIST_DEBUG
653 cbdataReferenceDone(sc->owner);
62e76326 654
fa80a8ef 655#endif
62e76326 656
528b2c61 657 delete sc;
62e76326 658
77b32a34 659 assert(e->lock_count > 0);
62e76326 660
77b32a34 661 if (mem->nclients == 0)
62e76326 662 CheckQuickAbort(e);
a46d2c0e 663 else
664 mem->kickReads();
62e76326 665
f09f5b26 666 return 1;
667}
668
669off_t
670storeLowestMemReaderOffset(const StoreEntry * entry)
671{
528b2c61 672 return entry->mem_obj->lowestMemReaderOffset();
f09f5b26 673}
674
675/* Call handlers waiting for data to be appended to E. */
676void
677InvokeHandlers(StoreEntry * e)
678{
528b2c61 679 /* Commit what we can to disk, if appropriate */
680 storeSwapOut (e);
f09f5b26 681 int i = 0;
682 MemObject *mem = e->mem_obj;
683 store_client *sc;
06d2839d 684 dlink_node *nx = NULL;
685 dlink_node *node;
686
2f44bd34 687 debug(90, 3) ("InvokeHandlers: %s\n", e->getMD5Text());
f09f5b26 688 /* walk the entire list looking for valid callbacks */
62e76326 689
06d2839d 690 for (node = mem->clients.head; node; node = nx) {
62e76326 691 sc = (store_client *)node->data;
692 nx = node->next;
693 debug(90, 3) ("InvokeHandlers: checking client #%d\n", i++);
694
695 if (!sc->callbackPending())
696 continue;
697
698 if (sc->flags.disk_io_pending)
699 continue;
700
701 storeClientCopy2(e, sc);
f09f5b26 702 }
703}
704
705int
706storePendingNClients(const StoreEntry * e)
707{
f09f5b26 708 MemObject *mem = e->mem_obj;
36547bcf 709 int npend = NULL == mem ? 0 : mem->nclients;
2f44bd34 710 debug(90, 3) ("storePendingNClients: returning %d\n", npend);
f09f5b26 711 return npend;
712}
77b32a34 713
714/* return 1 if the request should be aborted */
715static int
716CheckQuickAbort2(StoreEntry * entry)
717{
e6ccf245 718 size_t curlen;
719 size_t minlen;
720 size_t expectlen;
528b2c61 721 MemObject * const mem = entry->mem_obj;
77b32a34 722 assert(mem);
2f44bd34 723 debug(90, 3) ("CheckQuickAbort2: entry=%p, mem=%p\n", entry, mem);
62e76326 724
0e5bd28f 725 if (mem->request && !mem->request->flags.cachable) {
62e76326 726 debug(90, 3) ("CheckQuickAbort2: YES !mem->request->flags.cachable\n");
727 return 1;
77b32a34 728 }
62e76326 729
77b32a34 730 if (EBIT_TEST(entry->flags, KEY_PRIVATE)) {
62e76326 731 debug(90, 3) ("CheckQuickAbort2: YES KEY_PRIVATE\n");
732 return 1;
77b32a34 733 }
62e76326 734
528b2c61 735 expectlen = entry->getReply()->content_length + entry->getReply()->hdr_sz;
0e3f3e0d 736
737 if (expectlen < 0)
738 /* expectlen is < 0 if *no* information about the object has been recieved */
739 return 1;
740
528b2c61 741 curlen = (size_t) mem->endOffset ();
0e3f3e0d 742
e6ccf245 743 minlen = (size_t) Config.quickAbort.min << 10;
62e76326 744
77b32a34 745 if (minlen < 0) {
62e76326 746 debug(90, 3) ("CheckQuickAbort2: NO disabled\n");
747 return 0;
77b32a34 748 }
62e76326 749
77b32a34 750 if (curlen > expectlen) {
62e76326 751 debug(90, 3) ("CheckQuickAbort2: YES bad content length\n");
752 return 1;
77b32a34 753 }
62e76326 754
77b32a34 755 if ((expectlen - curlen) < minlen) {
62e76326 756 debug(90, 3) ("CheckQuickAbort2: NO only little more left\n");
757 return 0;
77b32a34 758 }
62e76326 759
77b32a34 760 if ((expectlen - curlen) > (Config.quickAbort.max << 10)) {
62e76326 761 debug(90, 3) ("CheckQuickAbort2: YES too much left to go\n");
762 return 1;
77b32a34 763 }
62e76326 764
77b32a34 765 if (expectlen < 100) {
62e76326 766 debug(90, 3) ("CheckQuickAbort2: NO avoid FPE\n");
767 return 0;
77b32a34 768 }
62e76326 769
e6ccf245 770 if ((curlen / (expectlen / 100)) > (size_t)Config.quickAbort.pct) {
62e76326 771 debug(90, 3) ("CheckQuickAbort2: NO past point of no return\n");
772 return 0;
77b32a34 773 }
62e76326 774
2f44bd34 775 debug(90, 3) ("CheckQuickAbort2: YES default, returning 1\n");
77b32a34 776 return 1;
777}
778
779static void
780CheckQuickAbort(StoreEntry * entry)
781{
528b2c61 782 assert (entry);
62e76326 783
77b32a34 784 if (storePendingNClients(entry) > 0)
62e76326 785 return;
786
77b32a34 787 if (entry->store_status != STORE_PENDING)
62e76326 788 return;
789
986ebffc 790 if (EBIT_TEST(entry->flags, ENTRY_SPECIAL))
62e76326 791 return;
792
77b32a34 793 if (CheckQuickAbort2(entry) == 0)
62e76326 794 return;
795
7197b20d 796 storeAbort(entry);
77b32a34 797}
c8be6d7b 798
799void
528b2c61 800store_client::dumpStats(StoreEntry * output, int clientNumber) const
c8be6d7b 801{
528b2c61 802 if (callbackPending())
62e76326 803 return;
804
528b2c61 805 storeAppendPrintf(output, "\tClient #%d, %p\n", clientNumber, _callback.callback_data);
62e76326 806
c8be6d7b 807 storeAppendPrintf(output, "\t\tcopy_offset: %lu\n",
62e76326 808 (unsigned long) copyInto.offset);
809
c8be6d7b 810 storeAppendPrintf(output, "\t\tcopy_size: %d\n",
62e76326 811 (int) copyInto.length);
812
c8be6d7b 813 storeAppendPrintf(output, "\t\tflags:");
62e76326 814
528b2c61 815 if (flags.disk_io_pending)
62e76326 816 storeAppendPrintf(output, " disk_io_pending");
817
528b2c61 818 if (flags.store_copying)
62e76326 819 storeAppendPrintf(output, " store_copying");
820
528b2c61 821 if (flags.copy_event_pending)
62e76326 822 storeAppendPrintf(output, " copy_event_pending");
823
c8be6d7b 824 storeAppendPrintf(output, "\n");
528b2c61 825}
c8be6d7b 826
528b2c61 827bool
828store_client::callbackPending() const
829{
830 return _callback.callback_handler && _callback.callback_data;
c8be6d7b 831}
528b2c61 832
833store_client::Callback::Callback(STCB *function, void *data) : callback_handler(function), callback_data (data) {}
b67e2c8c 834
515ec4dc 835#if DELAY_POOLS
b67e2c8c 836void
837store_client::setDelayId(DelayId delay_id)
838{
839 delayId = delay_id;
840}
62e76326 841
515ec4dc 842#endif