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