]>
Commit | Line | Data |
---|---|---|
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 | 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; | |
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 | 70 | store_client * |
71 | storeClientListSearch(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 | 84 | static store_client_t |
135171fe | 85 | storeClientType(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 | 124 | store_client * |
f09f5b26 | 125 | storeClientListAdd(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 | 155 | static void |
156 | storeClientCallback(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 | 167 | static void |
168 | storeClientCopyEvent(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 */ |
179 | void | |
a4b8110e | 180 | storeClientCopy(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 | */ |
221 | static int | |
222 | storeClientNoMoreToSend(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 | 234 | static void |
235 | storeClientCopy2(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 | ||
265 | static void | |
266 | storeClientCopy3(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 | 330 | static void |
e3ef2b09 | 331 | storeClientFileRead(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 | ||
356 | static void | |
5bd1abac | 357 | storeClientReadBody(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 | 370 | static void |
5bd1abac | 371 | storeClientReadHeader(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 | 483 | int |
a4b8110e | 484 | storeClientCopyPending(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 | 501 | int |
a4b8110e | 502 | storeUnregister(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 | ||
550 | off_t | |
551 | storeLowestMemReaderOffset(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. */ | |
576 | void | |
577 | InvokeHandlers(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 | ||
601 | int | |
602 | storePendingNClients(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 */ | |
611 | static int | |
612 | CheckQuickAbort2(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 | ||
659 | static void | |
660 | CheckQuickAbort(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 | } |