]>
Commit | Line | Data |
---|---|---|
9cef6668 | 1 | |
2 | /* | |
cd748f27 | 3 | * $Id: store_client.cc,v 1.87 2000/05/03 17:15:43 adrian 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 */ | |
cd748f27 | 141 | assert(e->swap_filen > -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); | |
b04e66e0 | 155 | sc->callback = NULL; |
156 | sc->copy_buf = NULL; | |
157 | if (cbdataValid(sc->callback_data)) | |
158 | callback(sc->callback_data, buf, sz); | |
159 | } | |
160 | ||
f115fadd | 161 | static void |
162 | storeClientCopyEvent(void *data) | |
163 | { | |
164 | store_client *sc = data; | |
fe4a33ac | 165 | debug(20, 3) ("storeClientCopyEvent: Running\n"); |
67fd69de | 166 | sc->flags.copy_event_pending = 0; |
a2899918 | 167 | if (!sc->callback) |
168 | return; | |
f115fadd | 169 | storeClientCopy2(sc->entry, sc); |
f115fadd | 170 | } |
171 | ||
f09f5b26 | 172 | /* copy bytes requested by the client */ |
173 | void | |
174 | storeClientCopy(StoreEntry * e, | |
175 | off_t seen_offset, | |
176 | off_t copy_offset, | |
177 | size_t size, | |
178 | char *buf, | |
179 | STCB * callback, | |
180 | void *data) | |
181 | { | |
182 | store_client *sc; | |
b7fe0ab0 | 183 | assert(!EBIT_TEST(e->flags, ENTRY_ABORTED)); |
f09f5b26 | 184 | debug(20, 3) ("storeClientCopy: %s, seen %d, want %d, size %d, cb %p, cbdata %p\n", |
185 | storeKeyText(e->key), | |
186 | (int) seen_offset, | |
187 | (int) copy_offset, | |
188 | (int) size, | |
189 | callback, | |
190 | data); | |
191 | sc = storeClientListSearch(e->mem_obj, data); | |
192 | assert(sc != NULL); | |
193 | assert(sc->callback == NULL); | |
194 | sc->copy_offset = copy_offset; | |
195 | sc->seen_offset = seen_offset; | |
196 | sc->callback = callback; | |
197 | sc->copy_buf = buf; | |
198 | sc->copy_size = size; | |
199 | sc->copy_offset = copy_offset; | |
67fd69de | 200 | storeClientCopy2(e, sc); |
f09f5b26 | 201 | } |
202 | ||
07304bf9 | 203 | /* |
204 | * This function is used below to decide if we have any more data to | |
0bb129ee | 205 | * send to the client. If the store_status is STORE_PENDING, then we |
b7fe0ab0 | 206 | * do have more data to send. If its STORE_OK, then |
0bb129ee | 207 | * we continue checking. If the object length is negative, then we |
208 | * don't know the real length and must open the swap file to find out. | |
209 | * If the length is >= 0, then we compare it to the requested copy | |
210 | * offset. | |
07304bf9 | 211 | */ |
212 | static int | |
213 | storeClientNoMoreToSend(StoreEntry * e, store_client * sc) | |
214 | { | |
215 | ssize_t len; | |
0bb129ee | 216 | if (e->store_status == STORE_PENDING) |
07304bf9 | 217 | return 0; |
218 | if ((len = objectLen(e)) < 0) | |
219 | return 0; | |
220 | if (sc->copy_offset < len) | |
221 | return 0; | |
222 | return 1; | |
223 | } | |
224 | ||
f09f5b26 | 225 | static void |
226 | storeClientCopy2(StoreEntry * e, store_client * sc) | |
227 | { | |
67fd69de | 228 | if (sc->flags.copy_event_pending) |
229 | return; | |
db1cd23c | 230 | if (EBIT_TEST(e->flags, ENTRY_FWD_HDR_WAIT)) { |
231 | debug(20, 5) ("storeClientCopy2: returning because ENTRY_FWD_HDR_WAIT set\n"); | |
7e3e1d01 | 232 | return; |
db1cd23c | 233 | } |
67fd69de | 234 | if (sc->flags.store_copying) { |
235 | sc->flags.copy_event_pending = 1; | |
3d36b8ab | 236 | debug(20, 3) ("storeClientCopy2: Queueing storeClientCopyEvent()\n"); |
c43f5247 | 237 | eventAdd("storeClientCopyEvent", storeClientCopyEvent, sc, 0.0, 0); |
67fd69de | 238 | return; |
239 | } | |
a455c097 | 240 | cbdataLock(sc); /* ick, prevent sc from getting freed */ |
67fd69de | 241 | sc->flags.store_copying = 1; |
f09f5b26 | 242 | debug(20, 3) ("storeClientCopy2: %s\n", storeKeyText(e->key)); |
b04e66e0 | 243 | assert(sc->callback != NULL); |
0bb129ee | 244 | /* |
b7fe0ab0 | 245 | * We used to check for ENTRY_ABORTED here. But there were some |
0bb129ee | 246 | * problems. For example, we might have a slow client (or two) and |
247 | * the server-side is reading far ahead and swapping to disk. Even | |
248 | * if the server-side aborts, we want to give the client(s) | |
249 | * everything we got before the abort condition occurred. | |
250 | */ | |
cfac48c2 | 251 | storeClientCopy3(e, sc); |
252 | sc->flags.store_copying = 0; | |
253 | cbdataUnlock(sc); /* ick, allow sc to be freed */ | |
254 | } | |
255 | ||
256 | static void | |
257 | storeClientCopy3(StoreEntry * e, store_client * sc) | |
258 | { | |
cfac48c2 | 259 | MemObject *mem = e->mem_obj; |
260 | size_t sz; | |
cd748f27 | 261 | |
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, | |
cd748f27 | 310 | sc->copy_offset, sc->copy_buf, sc->copy_size); |
311 | storeClientCallback(sc, sz); | |
312 | return; | |
cfac48c2 | 313 | } |
cd748f27 | 314 | /* What the client wants is not in memory. Schedule a disk read */ |
cfac48c2 | 315 | assert(STORE_DISK_CLIENT == sc->type); |
a928befc | 316 | assert(!sc->flags.disk_io_pending); |
cfac48c2 | 317 | debug(20, 3) ("storeClientCopy3: reading from STORE\n"); |
cfac48c2 | 318 | storeClientFileRead(sc); |
f09f5b26 | 319 | } |
320 | ||
f09f5b26 | 321 | static void |
e3ef2b09 | 322 | storeClientFileRead(store_client * sc) |
f09f5b26 | 323 | { |
07304bf9 | 324 | MemObject *mem = sc->entry->mem_obj; |
f09f5b26 | 325 | assert(sc->callback != NULL); |
b04e66e0 | 326 | assert(!sc->flags.disk_io_pending); |
327 | sc->flags.disk_io_pending = 1; | |
dee51794 | 328 | if (mem->swap_hdr_sz == 0) { |
2391a162 | 329 | storeRead(sc->swapin_sio, |
d8b9a541 | 330 | sc->copy_buf, |
331 | sc->copy_size, | |
e3ef2b09 | 332 | 0, |
333 | storeClientReadHeader, | |
334 | sc); | |
9d66d521 | 335 | } else { |
3157c72f | 336 | if (sc->entry->swap_status == SWAPOUT_WRITING) |
2391a162 | 337 | assert(storeOffset(mem->swapout.sio) > sc->copy_offset + mem->swap_hdr_sz); |
338 | storeRead(sc->swapin_sio, | |
e3ef2b09 | 339 | sc->copy_buf, |
340 | sc->copy_size, | |
341 | sc->copy_offset + mem->swap_hdr_sz, | |
342 | storeClientReadBody, | |
343 | sc); | |
3157c72f | 344 | } |
f09f5b26 | 345 | } |
346 | ||
347 | static void | |
5bd1abac | 348 | storeClientReadBody(void *data, const char *buf, ssize_t len) |
f09f5b26 | 349 | { |
350 | store_client *sc = data; | |
07304bf9 | 351 | MemObject *mem = sc->entry->mem_obj; |
27002b34 | 352 | assert(sc->flags.disk_io_pending); |
f115fadd | 353 | sc->flags.disk_io_pending = 0; |
f09f5b26 | 354 | assert(sc->callback != NULL); |
2391a162 | 355 | debug(20, 3) ("storeClientReadBody: len %d\n", len); |
cb69b4c7 | 356 | if (sc->copy_offset == 0 && len > 0 && mem->reply->sline.status == 0) |
9bc73deb | 357 | httpReplyParse(mem->reply, sc->copy_buf, headersEnd(sc->copy_buf, len)); |
b04e66e0 | 358 | storeClientCallback(sc, len); |
f09f5b26 | 359 | } |
360 | ||
e3ef2b09 | 361 | static void |
5bd1abac | 362 | storeClientReadHeader(void *data, const char *buf, ssize_t len) |
e3ef2b09 | 363 | { |
e3ef2b09 | 364 | store_client *sc = data; |
07304bf9 | 365 | StoreEntry *e = sc->entry; |
366 | MemObject *mem = e->mem_obj; | |
e3ef2b09 | 367 | int swap_hdr_sz = 0; |
368 | size_t body_sz; | |
369 | size_t copy_sz; | |
370 | tlv *tlv_list; | |
7e3ce7b9 | 371 | tlv *t; |
372 | int swap_object_ok = 1; | |
27002b34 | 373 | assert(sc->flags.disk_io_pending); |
f115fadd | 374 | sc->flags.disk_io_pending = 0; |
e3ef2b09 | 375 | assert(sc->callback != NULL); |
2391a162 | 376 | debug(20, 3) ("storeClientReadHeader: len %d\n", len); |
e3ef2b09 | 377 | if (len < 0) { |
2391a162 | 378 | debug(20, 3) ("storeClientReadHeader: %s\n", xstrerror()); |
b04e66e0 | 379 | storeClientCallback(sc, len); |
e3ef2b09 | 380 | return; |
381 | } | |
382 | tlv_list = storeSwapMetaUnpack(buf, &swap_hdr_sz); | |
9bc73deb | 383 | if (swap_hdr_sz > len) { |
384 | /* oops, bad disk file? */ | |
efd900cb | 385 | debug(20, 1) ("WARNING: swapfile header too small\n"); |
b04e66e0 | 386 | storeClientCallback(sc, -1); |
9bc73deb | 387 | return; |
388 | } | |
e3ef2b09 | 389 | if (tlv_list == NULL) { |
cd748f27 | 390 | debug(20, 1) ("WARNING: failed to unpack meta data\n"); |
b04e66e0 | 391 | storeClientCallback(sc, -1); |
e3ef2b09 | 392 | return; |
393 | } | |
394 | /* | |
7e3ce7b9 | 395 | * Check the meta data and make sure we got the right object. |
e3ef2b09 | 396 | */ |
7e3ce7b9 | 397 | for (t = tlv_list; t; t = t->next) { |
398 | switch (t->type) { | |
399 | case STORE_META_KEY: | |
400 | assert(t->length == MD5_DIGEST_CHARS); | |
b04e66e0 | 401 | if (memcmp(t->value, e->key, MD5_DIGEST_CHARS)) { |
7e3ce7b9 | 402 | debug(20, 1) ("WARNING: swapin MD5 mismatch\n"); |
b04e66e0 | 403 | debug(20, 1) ("\t%s\n", storeKeyText(t->value)); |
404 | debug(20, 1) ("\t%s\n", storeKeyText(e->key)); | |
405 | } | |
7e3ce7b9 | 406 | break; |
407 | case STORE_META_URL: | |
408 | if (NULL == mem->url) | |
409 | (void) 0; /* can't check */ | |
410 | else if (0 == strcasecmp(mem->url, t->value)) | |
411 | (void) 0; /* a match! */ | |
412 | else { | |
413 | debug(20, 1) ("storeClientReadHeader: URL mismatch\n"); | |
414 | debug(20, 1) ("\t{%s} != {%s}\n", t->value, mem->url); | |
415 | swap_object_ok = 0; | |
416 | break; | |
417 | } | |
418 | break; | |
419 | case STORE_META_STD: | |
420 | break; | |
421 | default: | |
422 | debug(20, 1) ("WARNING: got unused STORE_META type %d\n", t->type); | |
423 | break; | |
424 | } | |
425 | } | |
07304bf9 | 426 | storeSwapTLVFree(tlv_list); |
7e3ce7b9 | 427 | if (!swap_object_ok) { |
b04e66e0 | 428 | storeClientCallback(sc, -1); |
7e3ce7b9 | 429 | return; |
430 | } | |
e3ef2b09 | 431 | mem->swap_hdr_sz = swap_hdr_sz; |
07304bf9 | 432 | mem->object_sz = e->swap_file_sz - swap_hdr_sz; |
e3ef2b09 | 433 | /* |
434 | * If our last read got some data the client wants, then give | |
435 | * it to them, otherwise schedule another read. | |
436 | */ | |
437 | body_sz = len - swap_hdr_sz; | |
438 | if (sc->copy_offset < body_sz) { | |
439 | /* | |
25535cbe | 440 | * we have (part of) what they want |
e3ef2b09 | 441 | */ |
442 | copy_sz = XMIN(sc->copy_size, body_sz); | |
25535cbe | 443 | debug(20, 3) ("storeClientReadHeader: copying %d bytes of body\n", |
444 | copy_sz); | |
d8b9a541 | 445 | xmemmove(sc->copy_buf, sc->copy_buf + swap_hdr_sz, copy_sz); |
cb69b4c7 | 446 | if (sc->copy_offset == 0 && len > 0 && mem->reply->sline.status == 0) |
9bc73deb | 447 | httpReplyParse(mem->reply, sc->copy_buf, |
448 | headersEnd(sc->copy_buf, copy_sz)); | |
b04e66e0 | 449 | storeClientCallback(sc, copy_sz); |
e3ef2b09 | 450 | return; |
451 | } | |
452 | /* | |
453 | * we don't have what the client wants, but at least we now | |
454 | * know the swap header size. | |
455 | */ | |
e3ef2b09 | 456 | storeClientFileRead(sc); |
457 | } | |
458 | ||
f09f5b26 | 459 | int |
460 | storeClientCopyPending(StoreEntry * e, void *data) | |
461 | { | |
462 | /* return 1 if there is a callback registered for this client */ | |
463 | store_client *sc = storeClientListSearch(e->mem_obj, data); | |
464 | if (sc == NULL) | |
465 | return 0; | |
466 | if (sc->callback == NULL) | |
467 | return 0; | |
468 | return 1; | |
469 | } | |
470 | ||
471 | int | |
472 | storeUnregister(StoreEntry * e, void *data) | |
473 | { | |
474 | MemObject *mem = e->mem_obj; | |
475 | store_client *sc; | |
476 | store_client **S; | |
f09f5b26 | 477 | if (mem == NULL) |
478 | return 0; | |
479 | debug(20, 3) ("storeUnregister: called for '%s'\n", storeKeyText(e->key)); | |
480 | for (S = &mem->clients; (sc = *S) != NULL; S = &(*S)->next) { | |
481 | if (sc->callback_data == data) | |
482 | break; | |
483 | } | |
484 | if (sc == NULL) | |
485 | return 0; | |
4c454c5c | 486 | if (sc == mem->clients) { |
487 | /* | |
488 | * If we are unregistering the _first_ client for this | |
489 | * entry, then we have to reset the client FD to -1. | |
490 | */ | |
491 | mem->fd = -1; | |
492 | } | |
f09f5b26 | 493 | *S = sc->next; |
494 | mem->nclients--; | |
f09f5b26 | 495 | if (e->store_status == STORE_OK && e->swap_status != SWAPOUT_DONE) |
2391a162 | 496 | storeSwapOut(e); |
eb824054 | 497 | if (sc->swapin_sio) { |
2391a162 | 498 | storeClose(sc->swapin_sio); |
cfac48c2 | 499 | cbdataUnlock(sc->swapin_sio); |
eb824054 | 500 | sc->swapin_sio = NULL; |
501 | } | |
b04e66e0 | 502 | if (NULL != sc->callback) { |
f09f5b26 | 503 | /* callback with ssize = -1 to indicate unexpected termination */ |
504 | debug(20, 3) ("storeUnregister: store_client for %s has a callback\n", | |
505 | mem->url); | |
b04e66e0 | 506 | storeClientCallback(sc, -1); |
f09f5b26 | 507 | } |
b6a2f15e | 508 | #if DELAY_POOLS |
509 | delayUnregisterDelayIdPtr(&sc->delay_id); | |
510 | #endif | |
7e3ce7b9 | 511 | cbdataUnlock(sc->callback_data); /* we're done with it now */ |
a928befc | 512 | /*assert(!sc->flags.disk_io_pending); */ |
f09f5b26 | 513 | cbdataFree(sc); |
77b32a34 | 514 | assert(e->lock_count > 0); |
515 | if (mem->nclients == 0) | |
516 | CheckQuickAbort(e); | |
f09f5b26 | 517 | return 1; |
518 | } | |
519 | ||
520 | off_t | |
521 | storeLowestMemReaderOffset(const StoreEntry * entry) | |
522 | { | |
523 | const MemObject *mem = entry->mem_obj; | |
50a49a6f | 524 | off_t lowest = mem->inmem_hi + 1; |
f09f5b26 | 525 | store_client *sc; |
526 | store_client *nx = NULL; | |
527 | for (sc = mem->clients; sc; sc = nx) { | |
528 | nx = sc->next; | |
529 | if (sc->callback_data == NULL) /* open slot */ | |
530 | continue; | |
cd748f27 | 531 | if (sc->type != STORE_MEM_CLIENT) |
532 | continue; | |
cfac48c2 | 533 | if (sc->type == STORE_DISK_CLIENT) |
534 | if (NULL != sc->swapin_sio) | |
535 | continue; | |
f09f5b26 | 536 | if (sc->copy_offset < lowest) |
537 | lowest = sc->copy_offset; | |
538 | } | |
539 | return lowest; | |
540 | } | |
541 | ||
542 | /* Call handlers waiting for data to be appended to E. */ | |
543 | void | |
544 | InvokeHandlers(StoreEntry * e) | |
545 | { | |
546 | int i = 0; | |
547 | MemObject *mem = e->mem_obj; | |
548 | store_client *sc; | |
549 | store_client *nx = NULL; | |
550 | assert(mem->clients != NULL || mem->nclients == 0); | |
551 | debug(20, 3) ("InvokeHandlers: %s\n", storeKeyText(e->key)); | |
552 | /* walk the entire list looking for valid callbacks */ | |
553 | for (sc = mem->clients; sc; sc = nx) { | |
554 | nx = sc->next; | |
555 | debug(20, 3) ("InvokeHandlers: checking client #%d\n", i++); | |
556 | if (sc->callback_data == NULL) | |
557 | continue; | |
558 | if (sc->callback == NULL) | |
559 | continue; | |
a928befc | 560 | if (sc->flags.disk_io_pending) |
561 | continue; | |
f09f5b26 | 562 | storeClientCopy2(e, sc); |
563 | } | |
564 | } | |
565 | ||
566 | int | |
567 | storePendingNClients(const StoreEntry * e) | |
568 | { | |
f09f5b26 | 569 | MemObject *mem = e->mem_obj; |
36547bcf | 570 | int npend = NULL == mem ? 0 : mem->nclients; |
1afe05c5 | 571 | debug(20, 3) ("storePendingNClients: returning %d\n", npend); |
f09f5b26 | 572 | return npend; |
573 | } | |
77b32a34 | 574 | |
575 | /* return 1 if the request should be aborted */ | |
576 | static int | |
577 | CheckQuickAbort2(StoreEntry * entry) | |
578 | { | |
579 | int curlen; | |
580 | int minlen; | |
581 | int expectlen; | |
582 | MemObject *mem = entry->mem_obj; | |
583 | assert(mem); | |
584 | debug(20, 3) ("CheckQuickAbort2: entry=%p, mem=%p\n", entry, mem); | |
0e5bd28f | 585 | if (mem->request && !mem->request->flags.cachable) { |
6946d7e1 | 586 | debug(20, 3) ("CheckQuickAbort2: YES !mem->request->flags.cachable\n"); |
77b32a34 | 587 | return 1; |
588 | } | |
589 | if (EBIT_TEST(entry->flags, KEY_PRIVATE)) { | |
6946d7e1 | 590 | debug(20, 3) ("CheckQuickAbort2: YES KEY_PRIVATE\n"); |
77b32a34 | 591 | return 1; |
592 | } | |
b6a2f15e | 593 | expectlen = mem->reply->content_length + mem->reply->hdr_sz; |
77b32a34 | 594 | curlen = (int) mem->inmem_hi; |
595 | minlen = (int) Config.quickAbort.min << 10; | |
596 | if (minlen < 0) { | |
597 | debug(20, 3) ("CheckQuickAbort2: NO disabled\n"); | |
598 | return 0; | |
599 | } | |
600 | if (curlen > expectlen) { | |
6946d7e1 | 601 | debug(20, 3) ("CheckQuickAbort2: YES bad content length\n"); |
77b32a34 | 602 | return 1; |
603 | } | |
604 | if ((expectlen - curlen) < minlen) { | |
605 | debug(20, 3) ("CheckQuickAbort2: NO only little more left\n"); | |
606 | return 0; | |
607 | } | |
608 | if ((expectlen - curlen) > (Config.quickAbort.max << 10)) { | |
6946d7e1 | 609 | debug(20, 3) ("CheckQuickAbort2: YES too much left to go\n"); |
77b32a34 | 610 | return 1; |
611 | } | |
612 | if (expectlen < 100) { | |
613 | debug(20, 3) ("CheckQuickAbort2: NO avoid FPE\n"); | |
614 | return 0; | |
615 | } | |
616 | if ((curlen / (expectlen / 100)) > Config.quickAbort.pct) { | |
617 | debug(20, 3) ("CheckQuickAbort2: NO past point of no return\n"); | |
618 | return 0; | |
619 | } | |
6946d7e1 | 620 | debug(20, 3) ("CheckQuickAbort2: YES default, returning 1\n"); |
77b32a34 | 621 | return 1; |
622 | } | |
623 | ||
624 | static void | |
625 | CheckQuickAbort(StoreEntry * entry) | |
626 | { | |
627 | if (entry == NULL) | |
628 | return; | |
629 | if (storePendingNClients(entry) > 0) | |
630 | return; | |
631 | if (entry->store_status != STORE_PENDING) | |
632 | return; | |
986ebffc | 633 | if (EBIT_TEST(entry->flags, ENTRY_SPECIAL)) |
634 | return; | |
77b32a34 | 635 | if (CheckQuickAbort2(entry) == 0) |
636 | return; | |
637 | Counter.aborted_requests++; | |
7197b20d | 638 | storeAbort(entry); |
77b32a34 | 639 | } |