]> git.ipfire.org Git - thirdparty/squid.git/blame - src/store_client.cc
Fix "access_log none" (and "forward_log none")
[thirdparty/squid.git] / src / store_client.cc
CommitLineData
9cef6668 1
2/*
ed19251a 3 * $Id: store_client.cc,v 1.105 2001/10/24 08:19:09 hno Exp $
9cef6668 4 *
5 * DEBUG: section 20 Storage Manager Client-Side Interface
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 *
34 */
35
f09f5b26 36#include "squid.h"
37
e3ef2b09 38/*
39 * NOTE: 'Header' refers to the swapfile metadata header.
40 * 'Body' refers to the swapfile body, which is the full
41 * HTTP reply (including HTTP headers and body).
42 */
2391a162 43static STRCB storeClientReadBody;
44static STRCB storeClientReadHeader;
f09f5b26 45static void storeClientCopy2(StoreEntry * e, store_client * sc);
cfac48c2 46static void storeClientCopy3(StoreEntry * e, store_client * sc);
e3ef2b09 47static void storeClientFileRead(store_client * sc);
d6f51e3c 48static EVH storeClientCopyEvent;
7405a782 49static store_client_t storeClientType(StoreEntry *);
77b32a34 50static int CheckQuickAbort2(StoreEntry * entry);
51static void CheckQuickAbort(StoreEntry * entry);
f09f5b26 52
53/* check if there is any client waiting for this object at all */
54/* return 1 if there is at least one client */
55int
56storeClientWaiting(const StoreEntry * e)
57{
58 MemObject *mem = e->mem_obj;
06d2839d 59 dlink_node *node;
f09f5b26 60 store_client *sc;
06d2839d 61 for (node = mem->clients.head; node; node = node->next) {
de3d0a00 62 sc = node->data;
f09f5b26 63 if (sc->callback_data != NULL)
64 return 1;
65 }
66 return 0;
67}
68
06d2839d 69#if STORE_CLIENT_LIST_DEBUG
f09f5b26 70store_client *
71storeClientListSearch(const MemObject * mem, void *data)
72{
06d2839d 73 dlink_node *node;
74 store_client *sc = NULL;
75 for (node = mem->clients.head; node; node = node->next) {
de3d0a00 76 sc = node->data;
f09f5b26 77 if (sc->callback_data == data)
06d2839d 78 return sc;
f09f5b26 79 }
06d2839d 80 return NULL;
f09f5b26 81}
06d2839d 82#endif
f09f5b26 83
7405a782 84static store_client_t
135171fe 85storeClientType(StoreEntry * e)
7405a782 86{
87 MemObject *mem = e->mem_obj;
7405a782 88 if (mem->inmem_lo)
89 return STORE_DISK_CLIENT;
b7fe0ab0 90 if (EBIT_TEST(e->flags, ENTRY_ABORTED)) {
6a888d16 91 /* I don't think we should be adding clients to aborted entries */
b7fe0ab0 92 debug(20, 1) ("storeClientType: adding to ENTRY_ABORTED entry\n");
7405a782 93 return STORE_MEM_CLIENT;
6a888d16 94 }
95 if (e->store_status == STORE_OK) {
96 if (mem->inmem_lo == 0 && mem->inmem_hi > 0)
97 return STORE_MEM_CLIENT;
98 else
99 return STORE_DISK_CLIENT;
100 }
101 /* here and past, entry is STORE_PENDING */
7405a782 102 /*
103 * If this is the first client, let it be the mem client
104 */
6a888d16 105 else if (mem->nclients == 1)
106 return STORE_MEM_CLIENT;
ce872c10 107 /*
108 * If there is no disk file to open yet, we must make this a
109 * mem client. If we can't open the swapin file before writing
110 * to the client, there is no guarantee that we will be able
7e3ce7b9 111 * to open it later when we really need it.
ce872c10 112 */
113 else if (e->swap_status == SWAPOUT_NONE)
114 return STORE_MEM_CLIENT;
7405a782 115 /*
116 * otherwise, make subsequent clients read from disk so they
117 * can not delay the first, and vice-versa.
118 */
119 else
6a888d16 120 return STORE_DISK_CLIENT;
7405a782 121}
122
f09f5b26 123/* add client with fd to client list */
06d2839d 124store_client *
f09f5b26 125storeClientListAdd(StoreEntry * e, void *data)
126{
127 MemObject *mem = e->mem_obj;
f09f5b26 128 store_client *sc;
129 assert(mem);
06d2839d 130#if STORE_CLIENT_LIST_DEBUG
f09f5b26 131 if (storeClientListSearch(mem, data) != NULL)
a4b8110e 132 assert(1 == 0); /* XXX die! */
06d2839d 133#endif
7e3ce7b9 134 e->refcount++;
f09f5b26 135 mem->nclients++;
72711e31 136 sc = cbdataAlloc(store_client);
7e3ce7b9 137 cbdataLock(data); /* locked while we point to it */
f09f5b26 138 sc->callback_data = data;
139 sc->seen_offset = 0;
140 sc->copy_offset = 0;
f115fadd 141 sc->flags.disk_io_pending = 0;
07304bf9 142 sc->entry = e;
7405a782 143 sc->type = storeClientType(e);
144 if (sc->type == STORE_DISK_CLIENT)
698a99c2 145 /* assert we'll be able to get the data we want */
146 /* maybe we should open swapin_fd here */
cd748f27 147 assert(e->swap_filen > -1 || storeSwapOutAble(e));
06d2839d 148 dlinkAdd(sc, &sc->node, &mem->clients);
6b8e7481 149#if DELAY_POOLS
150 sc->delay_id = 0;
6b8e7481 151#endif
06d2839d 152 return sc;
f09f5b26 153}
154
b04e66e0 155static void
156storeClientCallback(store_client * sc, ssize_t sz)
157{
158 STCB *callback = sc->callback;
159 char *buf = sc->copy_buf;
160 assert(sc->callback);
b04e66e0 161 sc->callback = NULL;
162 sc->copy_buf = NULL;
163 if (cbdataValid(sc->callback_data))
164 callback(sc->callback_data, buf, sz);
165}
166
f115fadd 167static void
168storeClientCopyEvent(void *data)
169{
170 store_client *sc = data;
fe4a33ac 171 debug(20, 3) ("storeClientCopyEvent: Running\n");
67fd69de 172 sc->flags.copy_event_pending = 0;
a2899918 173 if (!sc->callback)
174 return;
f115fadd 175 storeClientCopy2(sc->entry, sc);
f115fadd 176}
177
f09f5b26 178/* copy bytes requested by the client */
179void
a4b8110e 180storeClientCopy(store_client * sc,
06d2839d 181 StoreEntry * e,
f09f5b26 182 off_t seen_offset,
183 off_t copy_offset,
184 size_t size,
185 char *buf,
186 STCB * callback,
187 void *data)
188{
b7fe0ab0 189 assert(!EBIT_TEST(e->flags, ENTRY_ABORTED));
f09f5b26 190 debug(20, 3) ("storeClientCopy: %s, seen %d, want %d, size %d, cb %p, cbdata %p\n",
186477c1 191 storeKeyText(e->hash.key),
f09f5b26 192 (int) seen_offset,
193 (int) copy_offset,
194 (int) size,
195 callback,
196 data);
f09f5b26 197 assert(sc != NULL);
06d2839d 198#if STORE_CLIENT_LIST_DEBUG
199 assert(sc == storeClientListSearch(e->mem_obj, data));
200#endif
f09f5b26 201 assert(sc->callback == NULL);
06d2839d 202 assert(sc->entry == e);
f09f5b26 203 sc->copy_offset = copy_offset;
204 sc->seen_offset = seen_offset;
205 sc->callback = callback;
206 sc->copy_buf = buf;
207 sc->copy_size = size;
208 sc->copy_offset = copy_offset;
67fd69de 209 storeClientCopy2(e, sc);
f09f5b26 210}
211
07304bf9 212/*
213 * This function is used below to decide if we have any more data to
0bb129ee 214 * send to the client. If the store_status is STORE_PENDING, then we
b7fe0ab0 215 * do have more data to send. If its STORE_OK, then
0bb129ee 216 * we continue checking. If the object length is negative, then we
217 * don't know the real length and must open the swap file to find out.
218 * If the length is >= 0, then we compare it to the requested copy
219 * offset.
07304bf9 220 */
221static int
222storeClientNoMoreToSend(StoreEntry * e, store_client * sc)
223{
224 ssize_t len;
0bb129ee 225 if (e->store_status == STORE_PENDING)
07304bf9 226 return 0;
227 if ((len = objectLen(e)) < 0)
228 return 0;
229 if (sc->copy_offset < len)
230 return 0;
231 return 1;
232}
233
f09f5b26 234static void
235storeClientCopy2(StoreEntry * e, store_client * sc)
236{
67fd69de 237 if (sc->flags.copy_event_pending)
238 return;
db1cd23c 239 if (EBIT_TEST(e->flags, ENTRY_FWD_HDR_WAIT)) {
240 debug(20, 5) ("storeClientCopy2: returning because ENTRY_FWD_HDR_WAIT set\n");
7e3e1d01 241 return;
db1cd23c 242 }
67fd69de 243 if (sc->flags.store_copying) {
244 sc->flags.copy_event_pending = 1;
3d36b8ab 245 debug(20, 3) ("storeClientCopy2: Queueing storeClientCopyEvent()\n");
c43f5247 246 eventAdd("storeClientCopyEvent", storeClientCopyEvent, sc, 0.0, 0);
67fd69de 247 return;
248 }
a455c097 249 cbdataLock(sc); /* ick, prevent sc from getting freed */
67fd69de 250 sc->flags.store_copying = 1;
186477c1 251 debug(20, 3) ("storeClientCopy2: %s\n", storeKeyText(e->hash.key));
b04e66e0 252 assert(sc->callback != NULL);
0bb129ee 253 /*
b7fe0ab0 254 * We used to check for ENTRY_ABORTED here. But there were some
0bb129ee 255 * problems. For example, we might have a slow client (or two) and
256 * the server-side is reading far ahead and swapping to disk. Even
257 * if the server-side aborts, we want to give the client(s)
258 * everything we got before the abort condition occurred.
259 */
cfac48c2 260 storeClientCopy3(e, sc);
261 sc->flags.store_copying = 0;
262 cbdataUnlock(sc); /* ick, allow sc to be freed */
263}
264
265static void
266storeClientCopy3(StoreEntry * e, store_client * sc)
267{
cfac48c2 268 MemObject *mem = e->mem_obj;
269 size_t sz;
cd748f27 270
0bb129ee 271 if (storeClientNoMoreToSend(e, sc)) {
f09f5b26 272 /* There is no more to send! */
b04e66e0 273 storeClientCallback(sc, 0);
cfac48c2 274 return;
275 }
276 if (e->store_status == STORE_PENDING && sc->seen_offset >= mem->inmem_hi) {
f09f5b26 277 /* client has already seen this, wait for more */
cfac48c2 278 debug(20, 3) ("storeClientCopy3: Waiting for more\n");
279 return;
280 }
281 /*
282 * Slight weirdness here. We open a swapin file for any
283 * STORE_DISK_CLIENT, even if we can copy the requested chunk
284 * from memory in the next block. We must try to open the
285 * swapin file before sending any data to the client side. If
286 * we postpone the open, and then can not open the file later
287 * on, the client loses big time. Its transfer just gets cut
288 * off. Better to open it early (while the client side handler
289 * is clientCacheHit) so that we can fall back to a cache miss
290 * if needed.
291 */
292 if (STORE_DISK_CLIENT == sc->type && NULL == sc->swapin_sio) {
293 debug(20, 3) ("storeClientCopy3: Need to open swap in file\n");
f09f5b26 294 /* gotta open the swapin file */
e11322b2 295 if (storeTooManyDiskFilesOpen()) {
43f3238f 296 /* yuck -- this causes a TCP_SWAPFAIL_MISS on the client side */
b04e66e0 297 storeClientCallback(sc, -1);
298 return;
d20b1cd0 299 } else if (!sc->flags.disk_io_pending) {
bcfda318 300 /* Don't set store_io_pending here */
7e3ce7b9 301 storeSwapInStart(sc);
302 if (NULL == sc->swapin_sio) {
b04e66e0 303 storeClientCallback(sc, -1);
304 return;
7e3ce7b9 305 }
b04e66e0 306 /*
307 * If the open succeeds we either copy from memory, or
308 * schedule a disk read in the next block.
309 */
7e3ce7b9 310 } else {
f3435962 311 debug(20, 1) ("WARNING: Averted multiple fd operation (1)\n");
cfac48c2 312 return;
f09f5b26 313 }
314 }
cfac48c2 315 if (sc->copy_offset >= mem->inmem_lo && sc->copy_offset < mem->inmem_hi) {
316 /* What the client wants is in memory */
317 debug(20, 3) ("storeClientCopy3: Copying from memory\n");
318 sz = stmemCopy(&mem->data_hdr,
a4b8110e 319 sc->copy_offset, sc->copy_buf, sc->copy_size);
320 storeClientCallback(sc, sz);
321 return;
cfac48c2 322 }
cd748f27 323 /* What the client wants is not in memory. Schedule a disk read */
cfac48c2 324 assert(STORE_DISK_CLIENT == sc->type);
a928befc 325 assert(!sc->flags.disk_io_pending);
cfac48c2 326 debug(20, 3) ("storeClientCopy3: reading from STORE\n");
cfac48c2 327 storeClientFileRead(sc);
f09f5b26 328}
329
f09f5b26 330static void
e3ef2b09 331storeClientFileRead(store_client * sc)
f09f5b26 332{
07304bf9 333 MemObject *mem = sc->entry->mem_obj;
f09f5b26 334 assert(sc->callback != NULL);
b04e66e0 335 assert(!sc->flags.disk_io_pending);
336 sc->flags.disk_io_pending = 1;
dee51794 337 if (mem->swap_hdr_sz == 0) {
2391a162 338 storeRead(sc->swapin_sio,
d8b9a541 339 sc->copy_buf,
340 sc->copy_size,
e3ef2b09 341 0,
342 storeClientReadHeader,
343 sc);
9d66d521 344 } else {
3157c72f 345 if (sc->entry->swap_status == SWAPOUT_WRITING)
2391a162 346 assert(storeOffset(mem->swapout.sio) > sc->copy_offset + mem->swap_hdr_sz);
347 storeRead(sc->swapin_sio,
e3ef2b09 348 sc->copy_buf,
349 sc->copy_size,
350 sc->copy_offset + mem->swap_hdr_sz,
351 storeClientReadBody,
352 sc);
3157c72f 353 }
f09f5b26 354}
355
356static void
5bd1abac 357storeClientReadBody(void *data, const char *buf, ssize_t len)
f09f5b26 358{
359 store_client *sc = data;
07304bf9 360 MemObject *mem = sc->entry->mem_obj;
27002b34 361 assert(sc->flags.disk_io_pending);
f115fadd 362 sc->flags.disk_io_pending = 0;
f09f5b26 363 assert(sc->callback != NULL);
ed19251a 364 debug(20, 3) ("storeClientReadBody: len %d\n", (int) len);
cb69b4c7 365 if (sc->copy_offset == 0 && len > 0 && mem->reply->sline.status == 0)
9bc73deb 366 httpReplyParse(mem->reply, sc->copy_buf, headersEnd(sc->copy_buf, len));
b04e66e0 367 storeClientCallback(sc, len);
f09f5b26 368}
369
e3ef2b09 370static void
5bd1abac 371storeClientReadHeader(void *data, const char *buf, ssize_t len)
e3ef2b09 372{
e4cc2fdf 373 static int md5_mismatches = 0;
e3ef2b09 374 store_client *sc = data;
07304bf9 375 StoreEntry *e = sc->entry;
376 MemObject *mem = e->mem_obj;
e3ef2b09 377 int swap_hdr_sz = 0;
378 size_t body_sz;
379 size_t copy_sz;
380 tlv *tlv_list;
7e3ce7b9 381 tlv *t;
382 int swap_object_ok = 1;
27002b34 383 assert(sc->flags.disk_io_pending);
f115fadd 384 sc->flags.disk_io_pending = 0;
e3ef2b09 385 assert(sc->callback != NULL);
ed19251a 386 debug(20, 3) ("storeClientReadHeader: len %d\n", (int) len);
e3ef2b09 387 if (len < 0) {
2391a162 388 debug(20, 3) ("storeClientReadHeader: %s\n", xstrerror());
b04e66e0 389 storeClientCallback(sc, len);
e3ef2b09 390 return;
391 }
392 tlv_list = storeSwapMetaUnpack(buf, &swap_hdr_sz);
9bc73deb 393 if (swap_hdr_sz > len) {
394 /* oops, bad disk file? */
efd900cb 395 debug(20, 1) ("WARNING: swapfile header too small\n");
b04e66e0 396 storeClientCallback(sc, -1);
9bc73deb 397 return;
398 }
e3ef2b09 399 if (tlv_list == NULL) {
cd748f27 400 debug(20, 1) ("WARNING: failed to unpack meta data\n");
b04e66e0 401 storeClientCallback(sc, -1);
e3ef2b09 402 return;
403 }
404 /*
7e3ce7b9 405 * Check the meta data and make sure we got the right object.
e3ef2b09 406 */
0f4fc447 407 for (t = tlv_list; t && swap_object_ok; t = t->next) {
7e3ce7b9 408 switch (t->type) {
409 case STORE_META_KEY:
410 assert(t->length == MD5_DIGEST_CHARS);
e7407eb8 411 if (!EBIT_TEST(e->flags, KEY_PRIVATE) &&
186477c1 412 memcmp(t->value, e->hash.key, MD5_DIGEST_CHARS)) {
e4cc2fdf 413 debug(20, 2) ("storeClientReadHeader: swapin MD5 mismatch\n");
414 debug(20, 2) ("\t%s\n", storeKeyText(t->value));
186477c1 415 debug(20, 2) ("\t%s\n", storeKeyText(e->hash.key));
e4cc2fdf 416 if (isPowTen(++md5_mismatches))
417 debug(20, 1) ("WARNING: %d swapin MD5 mismatches\n",
418 md5_mismatches);
e7407eb8 419 swap_object_ok = 0;
b04e66e0 420 }
7e3ce7b9 421 break;
422 case STORE_META_URL:
423 if (NULL == mem->url)
424 (void) 0; /* can't check */
425 else if (0 == strcasecmp(mem->url, t->value))
426 (void) 0; /* a match! */
427 else {
428 debug(20, 1) ("storeClientReadHeader: URL mismatch\n");
84f2d773 429 debug(20, 1) ("\t{%s} != {%s}\n", (char *) t->value, mem->url);
7e3ce7b9 430 swap_object_ok = 0;
431 break;
432 }
433 break;
434 case STORE_META_STD:
435 break;
f66a9ef4 436 case STORE_META_VARY_HEADERS:
437 if (mem->vary_headers) {
438 if (strcmp(mem->vary_headers, t->value) != 0)
439 swap_object_ok = 0;
440 } else {
441 /* Assume the object is OK.. remember the vary request headers */
442 mem->vary_headers = xstrdup(t->value);
443 }
444 break;
7e3ce7b9 445 default:
446 debug(20, 1) ("WARNING: got unused STORE_META type %d\n", t->type);
447 break;
448 }
449 }
07304bf9 450 storeSwapTLVFree(tlv_list);
7e3ce7b9 451 if (!swap_object_ok) {
b04e66e0 452 storeClientCallback(sc, -1);
7e3ce7b9 453 return;
454 }
e3ef2b09 455 mem->swap_hdr_sz = swap_hdr_sz;
07304bf9 456 mem->object_sz = e->swap_file_sz - swap_hdr_sz;
e3ef2b09 457 /*
458 * If our last read got some data the client wants, then give
459 * it to them, otherwise schedule another read.
460 */
461 body_sz = len - swap_hdr_sz;
462 if (sc->copy_offset < body_sz) {
463 /*
25535cbe 464 * we have (part of) what they want
e3ef2b09 465 */
466 copy_sz = XMIN(sc->copy_size, body_sz);
25535cbe 467 debug(20, 3) ("storeClientReadHeader: copying %d bytes of body\n",
ed19251a 468 (int) copy_sz);
d8b9a541 469 xmemmove(sc->copy_buf, sc->copy_buf + swap_hdr_sz, copy_sz);
cb69b4c7 470 if (sc->copy_offset == 0 && len > 0 && mem->reply->sline.status == 0)
9bc73deb 471 httpReplyParse(mem->reply, sc->copy_buf,
472 headersEnd(sc->copy_buf, copy_sz));
b04e66e0 473 storeClientCallback(sc, copy_sz);
e3ef2b09 474 return;
475 }
476 /*
477 * we don't have what the client wants, but at least we now
478 * know the swap header size.
479 */
e3ef2b09 480 storeClientFileRead(sc);
481}
482
f09f5b26 483int
a4b8110e 484storeClientCopyPending(store_client * sc, StoreEntry * e, void *data)
f09f5b26 485{
06d2839d 486#if STORE_CLIENT_LIST_DEBUG
487 assert(sc == storeClientListSearch(e->mem_obj, data));
488#endif
489 assert(sc->entry == e);
f09f5b26 490 if (sc == NULL)
491 return 0;
492 if (sc->callback == NULL)
493 return 0;
494 return 1;
495}
496
06d2839d 497/*
498 * This routine hasn't been optimised to take advantage of the
499 * passed sc. Yet.
500 */
f09f5b26 501int
a4b8110e 502storeUnregister(store_client * sc, StoreEntry * e, void *data)
f09f5b26 503{
504 MemObject *mem = e->mem_obj;
06d2839d 505#if STORE_CLIENT_LIST_DEBUG
506 assert(sc == storeClientListSearch(e->mem_obj, data));
507#endif
f09f5b26 508 if (mem == NULL)
a4b8110e 509 return 0;
186477c1 510 debug(20, 3) ("storeUnregister: called for '%s'\n", storeKeyText(e->hash.key));
f09f5b26 511 if (sc == NULL)
a4b8110e 512 return 0;
06d2839d 513 if (mem->clients.head == NULL)
a4b8110e 514 return 0;
de3d0a00 515 if (sc == mem->clients.head->data) {
a4b8110e 516 /*
517 * If we are unregistering the _first_ client for this
518 * entry, then we have to reset the client FD to -1.
519 */
520 mem->fd = -1;
4c454c5c 521 }
06d2839d 522 dlinkDelete(&sc->node, &mem->clients);
f09f5b26 523 mem->nclients--;
f09f5b26 524 if (e->store_status == STORE_OK && e->swap_status != SWAPOUT_DONE)
a4b8110e 525 storeSwapOut(e);
eb824054 526 if (sc->swapin_sio) {
a4b8110e 527 storeClose(sc->swapin_sio);
528 cbdataUnlock(sc->swapin_sio);
529 sc->swapin_sio = NULL;
83704487 530 statCounter.swap.ins++;
eb824054 531 }
b04e66e0 532 if (NULL != sc->callback) {
a4b8110e 533 /* callback with ssize = -1 to indicate unexpected termination */
534 debug(20, 3) ("storeUnregister: store_client for %s has a callback\n",
535 mem->url);
536 storeClientCallback(sc, -1);
f09f5b26 537 }
b6a2f15e 538#if DELAY_POOLS
539 delayUnregisterDelayIdPtr(&sc->delay_id);
540#endif
a4b8110e 541 cbdataUnlock(sc->callback_data); /* we're done with it now */
a928befc 542 /*assert(!sc->flags.disk_io_pending); */
f09f5b26 543 cbdataFree(sc);
77b32a34 544 assert(e->lock_count > 0);
545 if (mem->nclients == 0)
a4b8110e 546 CheckQuickAbort(e);
f09f5b26 547 return 1;
548}
549
550off_t
551storeLowestMemReaderOffset(const StoreEntry * entry)
552{
553 const MemObject *mem = entry->mem_obj;
50a49a6f 554 off_t lowest = mem->inmem_hi + 1;
f09f5b26 555 store_client *sc;
06d2839d 556 dlink_node *nx = NULL;
557 dlink_node *node;
558
559 for (node = mem->clients.head; node; node = nx) {
de3d0a00 560 sc = node->data;
06d2839d 561 nx = node->next;
f09f5b26 562 if (sc->callback_data == NULL) /* open slot */
563 continue;
cd748f27 564 if (sc->type != STORE_MEM_CLIENT)
565 continue;
cfac48c2 566 if (sc->type == STORE_DISK_CLIENT)
567 if (NULL != sc->swapin_sio)
568 continue;
f09f5b26 569 if (sc->copy_offset < lowest)
570 lowest = sc->copy_offset;
571 }
572 return lowest;
573}
574
575/* Call handlers waiting for data to be appended to E. */
576void
577InvokeHandlers(StoreEntry * e)
578{
579 int i = 0;
580 MemObject *mem = e->mem_obj;
581 store_client *sc;
06d2839d 582 dlink_node *nx = NULL;
583 dlink_node *node;
584
186477c1 585 debug(20, 3) ("InvokeHandlers: %s\n", storeKeyText(e->hash.key));
f09f5b26 586 /* walk the entire list looking for valid callbacks */
06d2839d 587 for (node = mem->clients.head; node; node = nx) {
de3d0a00 588 sc = node->data;
06d2839d 589 nx = node->next;
f09f5b26 590 debug(20, 3) ("InvokeHandlers: checking client #%d\n", i++);
591 if (sc->callback_data == NULL)
592 continue;
593 if (sc->callback == NULL)
594 continue;
a928befc 595 if (sc->flags.disk_io_pending)
596 continue;
f09f5b26 597 storeClientCopy2(e, sc);
598 }
599}
600
601int
602storePendingNClients(const StoreEntry * e)
603{
f09f5b26 604 MemObject *mem = e->mem_obj;
36547bcf 605 int npend = NULL == mem ? 0 : mem->nclients;
1afe05c5 606 debug(20, 3) ("storePendingNClients: returning %d\n", npend);
f09f5b26 607 return npend;
608}
77b32a34 609
610/* return 1 if the request should be aborted */
611static int
612CheckQuickAbort2(StoreEntry * entry)
613{
614 int curlen;
615 int minlen;
616 int expectlen;
617 MemObject *mem = entry->mem_obj;
618 assert(mem);
619 debug(20, 3) ("CheckQuickAbort2: entry=%p, mem=%p\n", entry, mem);
0e5bd28f 620 if (mem->request && !mem->request->flags.cachable) {
6946d7e1 621 debug(20, 3) ("CheckQuickAbort2: YES !mem->request->flags.cachable\n");
77b32a34 622 return 1;
623 }
624 if (EBIT_TEST(entry->flags, KEY_PRIVATE)) {
6946d7e1 625 debug(20, 3) ("CheckQuickAbort2: YES KEY_PRIVATE\n");
77b32a34 626 return 1;
627 }
b6a2f15e 628 expectlen = mem->reply->content_length + mem->reply->hdr_sz;
77b32a34 629 curlen = (int) mem->inmem_hi;
630 minlen = (int) Config.quickAbort.min << 10;
631 if (minlen < 0) {
632 debug(20, 3) ("CheckQuickAbort2: NO disabled\n");
633 return 0;
634 }
635 if (curlen > expectlen) {
6946d7e1 636 debug(20, 3) ("CheckQuickAbort2: YES bad content length\n");
77b32a34 637 return 1;
638 }
639 if ((expectlen - curlen) < minlen) {
640 debug(20, 3) ("CheckQuickAbort2: NO only little more left\n");
641 return 0;
642 }
643 if ((expectlen - curlen) > (Config.quickAbort.max << 10)) {
6946d7e1 644 debug(20, 3) ("CheckQuickAbort2: YES too much left to go\n");
77b32a34 645 return 1;
646 }
647 if (expectlen < 100) {
648 debug(20, 3) ("CheckQuickAbort2: NO avoid FPE\n");
649 return 0;
650 }
651 if ((curlen / (expectlen / 100)) > Config.quickAbort.pct) {
652 debug(20, 3) ("CheckQuickAbort2: NO past point of no return\n");
653 return 0;
654 }
6946d7e1 655 debug(20, 3) ("CheckQuickAbort2: YES default, returning 1\n");
77b32a34 656 return 1;
657}
658
659static void
660CheckQuickAbort(StoreEntry * entry)
661{
662 if (entry == NULL)
663 return;
664 if (storePendingNClients(entry) > 0)
665 return;
666 if (entry->store_status != STORE_PENDING)
667 return;
986ebffc 668 if (EBIT_TEST(entry->flags, ENTRY_SPECIAL))
669 return;
77b32a34 670 if (CheckQuickAbort2(entry) == 0)
671 return;
83704487 672 statCounter.aborted_requests++;
7197b20d 673 storeAbort(entry);
77b32a34 674}