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