]>
Commit | Line | Data |
---|---|---|
9cef6668 | 1 | |
2 | /* | |
1d5161bd | 3 | * $Id: store_client.cc,v 1.128 2003/06/24 12:42:27 robertc Exp $ |
9cef6668 | 4 | * |
2f44bd34 | 5 | * DEBUG: section 90 Storage Manager Client-Side Interface |
9cef6668 | 6 | * AUTHOR: Duane Wessels |
7 | * | |
2b6662ba | 8 | * SQUID Web Proxy Cache http://www.squid-cache.org/ |
9cef6668 | 9 | * ---------------------------------------------------------- |
10 | * | |
2b6662ba | 11 | * Squid is the result of efforts by numerous individuals from |
12 | * the Internet community; see the CONTRIBUTORS file for full | |
13 | * details. Many organizations have provided support for Squid's | |
14 | * development; see the SPONSORS file for full details. Squid is | |
15 | * Copyrighted (C) 2001 by the Regents of the University of | |
16 | * California; see the COPYRIGHT file for full details. Squid | |
17 | * incorporates software developed and/or copyrighted by other | |
18 | * sources; see the CREDITS file for full details. | |
9cef6668 | 19 | * |
20 | * This program is free software; you can redistribute it and/or modify | |
21 | * it under the terms of the GNU General Public License as published by | |
22 | * the Free Software Foundation; either version 2 of the License, or | |
23 | * (at your option) any later version. | |
24 | * | |
25 | * This program is distributed in the hope that it will be useful, | |
26 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
27 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
28 | * GNU General Public License for more details. | |
29 | * | |
30 | * You should have received a copy of the GNU General Public License | |
31 | * along with this program; if not, write to the Free Software | |
32 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. | |
33 | * | |
2f44bd34 | 34 | * Portions copyright (c) 2003 Robert Collins <robertc@squid-cache.org> |
9cef6668 | 35 | */ |
36 | ||
f09f5b26 | 37 | #include "squid.h" |
c8be6d7b | 38 | #include "StoreClient.h" |
e6ccf245 | 39 | #include "Store.h" |
528b2c61 | 40 | #include "HttpReply.h" |
41 | #include "MemObject.h" | |
42 | #include "StoreMeta.h" | |
43 | #include "StoreMetaUnpacker.h" | |
b67e2c8c | 44 | #if DELAY_POOLS |
45 | #include "DelayPools.h" | |
46 | #endif | |
c8be6d7b | 47 | |
48 | CBDATA_TYPE(store_client); | |
f09f5b26 | 49 | |
e3ef2b09 | 50 | /* |
51 | * NOTE: 'Header' refers to the swapfile metadata header. | |
528b2c61 | 52 | * 'OBJHeader' refers to the object header, with cannonical |
53 | * processed object headers (which may derive from FTP/HTTP etc | |
54 | * upstream protocols | |
e3ef2b09 | 55 | * 'Body' refers to the swapfile body, which is the full |
56 | * HTTP reply (including HTTP headers and body). | |
57 | */ | |
2391a162 | 58 | static STRCB storeClientReadBody; |
59 | static STRCB storeClientReadHeader; | |
f09f5b26 | 60 | static void storeClientCopy2(StoreEntry * e, store_client * sc); |
d6f51e3c | 61 | static EVH storeClientCopyEvent; |
77b32a34 | 62 | static int CheckQuickAbort2(StoreEntry * entry); |
63 | static void CheckQuickAbort(StoreEntry * entry); | |
f09f5b26 | 64 | |
528b2c61 | 65 | MemPool *store_client::pool = NULL; |
66 | ||
67 | void * | |
68 | store_client::operator new (size_t byteCount) | |
69 | { | |
70 | /* derived classes with different sizes must implement their own new */ | |
71 | assert (byteCount == sizeof (store_client)); | |
72 | CBDATA_INIT_TYPE(store_client); | |
73 | return cbdataAlloc(store_client); | |
74 | } | |
75 | ||
76 | void | |
77 | store_client::operator delete (void *address) | |
78 | { | |
101a3d6f | 79 | cbdataFree (address); |
528b2c61 | 80 | } |
81 | ||
82 | bool | |
83 | store_client::memReaderHasLowerOffset(off_t anOffset) const | |
84 | { | |
85 | return getType() == STORE_MEM_CLIENT && copyInto.offset < anOffset; | |
86 | } | |
87 | ||
88 | int | |
89 | store_client::getType() const | |
90 | { | |
91 | return type; | |
92 | } | |
93 | ||
06d2839d | 94 | #if STORE_CLIENT_LIST_DEBUG |
fa80a8ef | 95 | static store_client * |
f09f5b26 | 96 | storeClientListSearch(const MemObject * mem, void *data) |
97 | { | |
06d2839d | 98 | dlink_node *node; |
99 | store_client *sc = NULL; | |
62e76326 | 100 | |
06d2839d | 101 | for (node = mem->clients.head; node; node = node->next) { |
62e76326 | 102 | sc = node->data; |
103 | ||
104 | if (sc->owner == data) | |
105 | return sc; | |
f09f5b26 | 106 | } |
62e76326 | 107 | |
06d2839d | 108 | return NULL; |
f09f5b26 | 109 | } |
c8be6d7b | 110 | |
111 | int | |
112 | storeClientIsThisAClient(store_client * sc, void *someClient) | |
113 | { | |
114 | return sc->owner == someClient; | |
115 | } | |
62e76326 | 116 | |
06d2839d | 117 | #endif |
f09f5b26 | 118 | |
119 | /* add client with fd to client list */ | |
06d2839d | 120 | store_client * |
f09f5b26 | 121 | storeClientListAdd(StoreEntry * e, void *data) |
122 | { | |
123 | MemObject *mem = e->mem_obj; | |
f09f5b26 | 124 | store_client *sc; |
125 | assert(mem); | |
06d2839d | 126 | #if STORE_CLIENT_LIST_DEBUG |
62e76326 | 127 | |
f09f5b26 | 128 | if (storeClientListSearch(mem, data) != NULL) |
62e76326 | 129 | /* XXX die! */ |
130 | assert(1 == 0); | |
131 | ||
6b8e7481 | 132 | #endif |
62e76326 | 133 | |
528b2c61 | 134 | sc = new store_client (e); |
62e76326 | 135 | |
528b2c61 | 136 | mem->addClient(sc); |
62e76326 | 137 | |
06d2839d | 138 | return sc; |
f09f5b26 | 139 | } |
140 | ||
528b2c61 | 141 | void |
142 | store_client::callback(ssize_t sz, bool error) | |
b04e66e0 | 143 | { |
528b2c61 | 144 | StoreIOBuffer result (sz, 0 ,copyInto.data); |
62e76326 | 145 | |
c8be6d7b | 146 | if (sz < 0) { |
62e76326 | 147 | result.flags.error = 1; |
148 | result.length = 0; | |
528b2c61 | 149 | } else { |
62e76326 | 150 | result.flags.error = error ? 1 : 0; |
c8be6d7b | 151 | } |
62e76326 | 152 | |
528b2c61 | 153 | result.offset = cmp_offset; |
154 | assert(callbackPending()); | |
155 | cmp_offset = copyInto.offset + sz; | |
156 | STCB *temphandler = _callback.callback_handler; | |
157 | void *cbdata = _callback.callback_data; | |
158 | _callback = Callback(NULL, NULL); | |
159 | copyInto.data = NULL; | |
62e76326 | 160 | |
528b2c61 | 161 | if (cbdataReferenceValid(cbdata)) |
62e76326 | 162 | temphandler(cbdata, result); |
163 | ||
528b2c61 | 164 | cbdataReferenceDone(cbdata); |
b04e66e0 | 165 | } |
166 | ||
f115fadd | 167 | static void |
168 | storeClientCopyEvent(void *data) | |
169 | { | |
e6ccf245 | 170 | store_client *sc = (store_client *)data; |
2f44bd34 | 171 | debug(90, 3)("storeClientCopyEvent: Running\n"); |
528b2c61 | 172 | assert (sc->flags.copy_event_pending); |
67fd69de | 173 | sc->flags.copy_event_pending = 0; |
62e76326 | 174 | |
528b2c61 | 175 | if (!sc->callbackPending()) |
62e76326 | 176 | return; |
177 | ||
f115fadd | 178 | storeClientCopy2(sc->entry, sc); |
f115fadd | 179 | } |
180 | ||
b67e2c8c | 181 | store_client::store_client(StoreEntry *e) : entry (e) |
182 | #if DELAY_POOLS | |
62e76326 | 183 | , delayId() |
b67e2c8c | 184 | #endif |
62e76326 | 185 | , type (e->storeClientType()) |
186 | , object_ok(true) | |
528b2c61 | 187 | { |
188 | cmp_offset = 0; | |
189 | flags.disk_io_pending = 0; | |
190 | entry->refcount++; | |
62e76326 | 191 | |
528b2c61 | 192 | if (getType() == STORE_DISK_CLIENT) |
62e76326 | 193 | /* assert we'll be able to get the data we want */ |
194 | /* maybe we should open swapin_fd here */ | |
195 | assert(entry->swap_filen > -1 || storeSwapOutAble(entry)); | |
196 | ||
528b2c61 | 197 | #if STORE_CLIENT_LIST_DEBUG |
62e76326 | 198 | |
528b2c61 | 199 | owner = cbdataReference(data); |
62e76326 | 200 | |
528b2c61 | 201 | #endif |
202 | } | |
203 | ||
2f44bd34 | 204 | store_client::~store_client() |
62e76326 | 205 | {} |
2f44bd34 | 206 | |
f09f5b26 | 207 | /* copy bytes requested by the client */ |
208 | void | |
a4b8110e | 209 | storeClientCopy(store_client * sc, |
62e76326 | 210 | StoreEntry * e, |
211 | StoreIOBuffer copyInto, | |
212 | STCB * callback, | |
213 | void *data) | |
f09f5b26 | 214 | { |
528b2c61 | 215 | assert (sc != NULL); |
216 | sc->copy(e, copyInto,callback,data); | |
217 | } | |
218 | ||
219 | void | |
220 | store_client::copy(StoreEntry * anEntry, | |
62e76326 | 221 | StoreIOBuffer copyRequest, |
222 | STCB * callback_fn, | |
223 | void *data) | |
528b2c61 | 224 | { |
225 | assert (anEntry == entry); | |
226 | assert (callback_fn); | |
227 | assert (data); | |
228 | assert(!EBIT_TEST(entry->flags, ENTRY_ABORTED)); | |
2f44bd34 | 229 | debug(90, 3)("store_client::copy: %s, from %lu, for length %d, cb %p, cbdata %p", |
62e76326 | 230 | entry->getMD5Text(), |
231 | (unsigned long) copyRequest.offset, | |
232 | (int) copyRequest.length, | |
233 | callback_fn, | |
234 | data); | |
06d2839d | 235 | #if STORE_CLIENT_LIST_DEBUG |
62e76326 | 236 | |
528b2c61 | 237 | assert(this == storeClientListSearch(entry->mem_obj, data)); |
06d2839d | 238 | #endif |
62e76326 | 239 | |
528b2c61 | 240 | assert(!callbackPending()); |
241 | #if ONLYCONTIGUOUSREQUESTS | |
62e76326 | 242 | |
528b2c61 | 243 | assert(cmp_offset == copyRequest.offset); |
244 | #endif | |
245 | /* range requests will skip into the body */ | |
246 | cmp_offset = copyRequest.offset; | |
247 | _callback = Callback (callback_fn, cbdataReference(data)); | |
248 | copyInto.data = copyRequest.data; | |
249 | copyInto.length = copyRequest.length; | |
250 | copyInto.offset = copyRequest.offset; | |
251 | ||
1d5161bd | 252 | static bool copying (false); |
253 | assert (!copying); | |
254 | copying = true; | |
255 | PROF_start(storeClient_kickReads); | |
a46d2c0e | 256 | /* we might be blocking comm reads due to readahead limits |
257 | * now we have a new offset, trigger those reads... | |
258 | */ | |
259 | entry->mem_obj->kickReads(); | |
1d5161bd | 260 | PROF_stop(storeClient_kickReads); |
261 | copying = false; | |
a46d2c0e | 262 | |
528b2c61 | 263 | storeClientCopy2(entry, this); |
f09f5b26 | 264 | } |
265 | ||
07304bf9 | 266 | /* |
267 | * This function is used below to decide if we have any more data to | |
0bb129ee | 268 | * send to the client. If the store_status is STORE_PENDING, then we |
b7fe0ab0 | 269 | * do have more data to send. If its STORE_OK, then |
0bb129ee | 270 | * we continue checking. If the object length is negative, then we |
271 | * don't know the real length and must open the swap file to find out. | |
272 | * If the length is >= 0, then we compare it to the requested copy | |
273 | * offset. | |
07304bf9 | 274 | */ |
275 | static int | |
276 | storeClientNoMoreToSend(StoreEntry * e, store_client * sc) | |
277 | { | |
278 | ssize_t len; | |
62e76326 | 279 | |
0bb129ee | 280 | if (e->store_status == STORE_PENDING) |
62e76326 | 281 | return 0; |
282 | ||
07304bf9 | 283 | if ((len = objectLen(e)) < 0) |
62e76326 | 284 | return 0; |
285 | ||
c8be6d7b | 286 | if (sc->copyInto.offset < len) |
62e76326 | 287 | return 0; |
288 | ||
07304bf9 | 289 | return 1; |
290 | } | |
291 | ||
f09f5b26 | 292 | static void |
293 | storeClientCopy2(StoreEntry * e, store_client * sc) | |
294 | { | |
528b2c61 | 295 | /* reentrancy not allowed - note this could lead to |
296 | * dropped events | |
297 | */ | |
62e76326 | 298 | |
fa80a8ef | 299 | if (sc->flags.copy_event_pending) { |
62e76326 | 300 | return; |
fa80a8ef | 301 | } |
62e76326 | 302 | |
db1cd23c | 303 | if (EBIT_TEST(e->flags, ENTRY_FWD_HDR_WAIT)) { |
62e76326 | 304 | debug(90, 5)("storeClientCopy2: returning because ENTRY_FWD_HDR_WAIT set\n"); |
305 | return; | |
db1cd23c | 306 | } |
62e76326 | 307 | |
67fd69de | 308 | if (sc->flags.store_copying) { |
62e76326 | 309 | sc->flags.copy_event_pending = 1; |
310 | debug(90, 3)("storeClientCopy2: Queueing storeClientCopyEvent()\n"); | |
311 | eventAdd("storeClientCopyEvent", storeClientCopyEvent, sc, 0.0, 0); | |
312 | return; | |
67fd69de | 313 | } |
62e76326 | 314 | |
2f44bd34 | 315 | debug(90, 3)("storeClientCopy2: %s\n", e->getMD5Text()); |
528b2c61 | 316 | assert(sc->callbackPending()); |
0bb129ee | 317 | /* |
b7fe0ab0 | 318 | * We used to check for ENTRY_ABORTED here. But there were some |
0bb129ee | 319 | * problems. For example, we might have a slow client (or two) and |
320 | * the server-side is reading far ahead and swapping to disk. Even | |
321 | * if the server-side aborts, we want to give the client(s) | |
322 | * everything we got before the abort condition occurred. | |
323 | */ | |
528b2c61 | 324 | /* Warning: doCopy may indirectly free itself in callbacks, |
fa80a8ef | 325 | * hence the cbdata reference to keep it active for the duration of |
326 | * this function | |
327 | */ | |
528b2c61 | 328 | cbdataReference(sc); |
329 | assert (sc->flags.store_copying == 0); | |
330 | sc->doCopy(e); | |
331 | assert (sc->flags.store_copying == 0); | |
332 | cbdataReferenceDone(sc); | |
cfac48c2 | 333 | } |
334 | ||
528b2c61 | 335 | void |
336 | store_client::doCopy(StoreEntry *anEntry) | |
cfac48c2 | 337 | { |
528b2c61 | 338 | assert (anEntry == entry); |
339 | flags.store_copying = 1; | |
340 | MemObject *mem = entry->mem_obj; | |
cd748f27 | 341 | |
528b2c61 | 342 | debug(33, 5)("store_client::doCopy: co: %lu, hi: %ld\n", (unsigned long) copyInto.offset, (long int) mem->endOffset()); |
add2192d | 343 | |
528b2c61 | 344 | if (storeClientNoMoreToSend(entry, this)) { |
62e76326 | 345 | /* There is no more to send! */ |
346 | callback(0); | |
347 | flags.store_copying = 0; | |
348 | return; | |
cfac48c2 | 349 | } |
62e76326 | 350 | |
add2192d | 351 | /* Check that we actually have data */ |
528b2c61 | 352 | if (anEntry->store_status == STORE_PENDING && copyInto.offset >= mem->endOffset()) { |
62e76326 | 353 | debug(90, 3)("store_client::doCopy: Waiting for more\n"); |
354 | flags.store_copying = 0; | |
355 | return; | |
cfac48c2 | 356 | } |
62e76326 | 357 | |
cfac48c2 | 358 | /* |
359 | * Slight weirdness here. We open a swapin file for any | |
360 | * STORE_DISK_CLIENT, even if we can copy the requested chunk | |
361 | * from memory in the next block. We must try to open the | |
362 | * swapin file before sending any data to the client side. If | |
363 | * we postpone the open, and then can not open the file later | |
364 | * on, the client loses big time. Its transfer just gets cut | |
365 | * off. Better to open it early (while the client side handler | |
366 | * is clientCacheHit) so that we can fall back to a cache miss | |
367 | * if needed. | |
368 | */ | |
fa80a8ef | 369 | |
528b2c61 | 370 | if (STORE_DISK_CLIENT == getType() && NULL == swapin_sio.getRaw()) { |
62e76326 | 371 | debug(90, 3)("store_client::doCopy: Need to open swap in file\n"); |
372 | /* gotta open the swapin file */ | |
373 | ||
374 | if (storeTooManyDiskFilesOpen()) { | |
375 | /* yuck -- this causes a TCP_SWAPFAIL_MISS on the client side */ | |
376 | fail(); | |
377 | flags.store_copying = 0; | |
378 | return; | |
379 | } else if (!flags.disk_io_pending) { | |
380 | /* Don't set store_io_pending here */ | |
381 | storeSwapInStart(this); | |
382 | ||
383 | if (NULL == swapin_sio.getRaw()) { | |
384 | fail(); | |
385 | flags.store_copying = 0; | |
386 | return; | |
387 | } | |
388 | ||
389 | /* | |
390 | * If the open succeeds we either copy from memory, or | |
391 | * schedule a disk read in the next block. | |
392 | */ | |
393 | } else { | |
394 | debug (90, 1)("WARNING: Averted multiple fd operation (1)\n"); | |
395 | flags.store_copying = 0; | |
396 | return; | |
397 | } | |
f09f5b26 | 398 | } |
62e76326 | 399 | |
528b2c61 | 400 | if (copyInto.offset >= mem->inmem_lo && copyInto.offset < mem->endOffset()) { |
62e76326 | 401 | /* What the client wants is in memory */ |
402 | /* Old style */ | |
403 | debug(90, 3)("store_client::doCopy: Copying normal from memory\n"); | |
404 | size_t sz = mem->data_hdr.copy(copyInto.offset, copyInto.data, | |
405 | copyInto.length); | |
406 | callback(sz); | |
407 | flags.store_copying = 0; | |
408 | return; | |
cfac48c2 | 409 | } |
62e76326 | 410 | |
cd748f27 | 411 | /* What the client wants is not in memory. Schedule a disk read */ |
528b2c61 | 412 | assert(STORE_DISK_CLIENT == getType()); |
62e76326 | 413 | |
528b2c61 | 414 | assert(!flags.disk_io_pending); |
62e76326 | 415 | |
2f44bd34 | 416 | debug(90, 3)("store_client::doCopy: reading from STORE\n"); |
62e76326 | 417 | |
528b2c61 | 418 | fileRead(); |
62e76326 | 419 | |
528b2c61 | 420 | flags.store_copying = 0; |
f09f5b26 | 421 | } |
422 | ||
528b2c61 | 423 | void |
424 | store_client::fileRead() | |
f09f5b26 | 425 | { |
528b2c61 | 426 | MemObject *mem = entry->mem_obj; |
427 | ||
428 | assert(callbackPending()); | |
429 | assert(!flags.disk_io_pending); | |
430 | flags.disk_io_pending = 1; | |
62e76326 | 431 | |
528b2c61 | 432 | if (mem->swap_hdr_sz != 0) |
62e76326 | 433 | if (entry->swap_status == SWAPOUT_WRITING) |
434 | assert(mem->swapout.sio->offset() > copyInto.offset + (off_t)mem->swap_hdr_sz); | |
435 | ||
528b2c61 | 436 | storeRead(swapin_sio, |
62e76326 | 437 | copyInto.data, |
438 | copyInto.length, | |
439 | copyInto.offset + mem->swap_hdr_sz, | |
440 | mem->swap_hdr_sz == 0 ? storeClientReadHeader | |
441 | : storeClientReadBody, | |
442 | this); | |
f09f5b26 | 443 | } |
444 | ||
445 | static void | |
5bd1abac | 446 | storeClientReadBody(void *data, const char *buf, ssize_t len) |
f09f5b26 | 447 | { |
e6ccf245 | 448 | store_client *sc = (store_client *)data; |
27002b34 | 449 | assert(sc->flags.disk_io_pending); |
f115fadd | 450 | sc->flags.disk_io_pending = 0; |
528b2c61 | 451 | assert(sc->callbackPending()); |
2f44bd34 | 452 | debug(90, 3)("storeClientReadBody: len %d", (int) len); |
62e76326 | 453 | |
528b2c61 | 454 | if (sc->copyInto.offset == 0 && len > 0 && sc->entry->getReply()->sline.status == 0) |
62e76326 | 455 | /* Our structure ! */ |
456 | if (!httpReplyParse((HttpReply *)sc->entry->getReply(), sc->copyInto.data, headersEnd(sc->copyInto.data, len))) { | |
457 | debug (90,0)("Could not parse headers from on disk object\n"); | |
458 | } | |
459 | ||
528b2c61 | 460 | sc->callback(len); |
461 | } | |
462 | ||
463 | void | |
62e76326 | 464 | store_client::fail() |
528b2c61 | 465 | { |
466 | object_ok = false; | |
467 | callback(0, true); | |
f09f5b26 | 468 | } |
469 | ||
e3ef2b09 | 470 | static void |
5bd1abac | 471 | storeClientReadHeader(void *data, const char *buf, ssize_t len) |
e3ef2b09 | 472 | { |
e6ccf245 | 473 | store_client *sc = (store_client *)data; |
528b2c61 | 474 | sc->readHeader(buf, len); |
475 | } | |
476 | ||
477 | void | |
478 | store_client::unpackHeader(char const *buf, ssize_t len) | |
479 | { | |
2f44bd34 | 480 | debug(90, 3)("store_client::unpackHeader: len %d", (int) len); |
62e76326 | 481 | |
e3ef2b09 | 482 | if (len < 0) { |
62e76326 | 483 | debug(90, 3)("store_client::unpackHeader: %s", xstrerror()); |
484 | fail(); | |
485 | return; | |
e3ef2b09 | 486 | } |
62e76326 | 487 | |
528b2c61 | 488 | int swap_hdr_sz = 0; |
489 | StoreMetaUnpacker aBuilder(buf, len, &swap_hdr_sz); | |
62e76326 | 490 | |
528b2c61 | 491 | if (!aBuilder.isBufferSane()) { |
62e76326 | 492 | /* oops, bad disk file? */ |
493 | debug(90, 1) ("WARNING: swapfile header inconsistent with available data\n"); | |
494 | fail(); | |
495 | return; | |
9bc73deb | 496 | } |
62e76326 | 497 | |
528b2c61 | 498 | tlv *tlv_list = aBuilder.createStoreMeta (); |
62e76326 | 499 | |
e3ef2b09 | 500 | if (tlv_list == NULL) { |
62e76326 | 501 | debug(90, 1) ("WARNING: failed to unpack meta data\n"); |
502 | fail(); | |
503 | return; | |
e3ef2b09 | 504 | } |
62e76326 | 505 | |
e3ef2b09 | 506 | /* |
7e3ce7b9 | 507 | * Check the meta data and make sure we got the right object. |
e3ef2b09 | 508 | */ |
528b2c61 | 509 | for (tlv *t = tlv_list; t; t = t->next) { |
62e76326 | 510 | if (!t->checkConsistency(entry)) { |
511 | storeSwapTLVFree(tlv_list); | |
512 | fail(); | |
513 | return; | |
514 | } | |
7e3ce7b9 | 515 | } |
62e76326 | 516 | |
07304bf9 | 517 | storeSwapTLVFree(tlv_list); |
528b2c61 | 518 | |
519 | entry->mem_obj->swap_hdr_sz = swap_hdr_sz; | |
520 | entry->mem_obj->object_sz = entry->swap_file_sz - swap_hdr_sz; | |
521 | ||
522 | } | |
523 | ||
524 | void | |
525 | store_client::readHeader(char const *buf, ssize_t len) | |
526 | { | |
527 | MemObject *const mem = entry->mem_obj; | |
62e76326 | 528 | |
528b2c61 | 529 | assert(flags.disk_io_pending); |
530 | flags.disk_io_pending = 0; | |
531 | assert(callbackPending()); | |
532 | ||
533 | unpackHeader (buf, len); | |
62e76326 | 534 | |
528b2c61 | 535 | if (!object_ok) |
62e76326 | 536 | return; |
537 | ||
e3ef2b09 | 538 | /* |
539 | * If our last read got some data the client wants, then give | |
540 | * it to them, otherwise schedule another read. | |
541 | */ | |
528b2c61 | 542 | size_t body_sz = len - mem->swap_hdr_sz; |
62e76326 | 543 | |
528b2c61 | 544 | if (static_cast<size_t>(copyInto.offset) < body_sz) { |
62e76326 | 545 | /* |
546 | * we have (part of) what they want | |
547 | */ | |
548 | size_t copy_sz = XMIN(copyInto.length, body_sz) | |
549 | ; | |
550 | debug(90, 3) ("storeClientReadHeader: copying %d bytes of body\n", | |
551 | (int) copy_sz); | |
552 | xmemmove(copyInto.data, copyInto.data + mem->swap_hdr_sz, copy_sz); | |
553 | ||
554 | if (copyInto.offset == 0 && len > 0 && entry->getReply()->sline.status == 0) | |
555 | /* Our structure ! */ | |
556 | if (!httpReplyParse((HttpReply *)entry->getReply(), copyInto.data, | |
557 | headersEnd(copyInto.data, copy_sz))) { | |
558 | debug (90,0)("could not parse headers from on disk structure!\n"); | |
559 | } | |
528b2c61 | 560 | |
561 | callback(copy_sz); | |
62e76326 | 562 | return; |
e3ef2b09 | 563 | } |
62e76326 | 564 | |
e3ef2b09 | 565 | /* |
566 | * we don't have what the client wants, but at least we now | |
567 | * know the swap header size. | |
568 | */ | |
528b2c61 | 569 | fileRead(); |
e3ef2b09 | 570 | } |
571 | ||
f09f5b26 | 572 | int |
a4b8110e | 573 | storeClientCopyPending(store_client * sc, StoreEntry * e, void *data) |
f09f5b26 | 574 | { |
06d2839d | 575 | #if STORE_CLIENT_LIST_DEBUG |
576 | assert(sc == storeClientListSearch(e->mem_obj, data)); | |
edce4d98 | 577 | #endif |
578 | #ifndef SILLY_CODE | |
62e76326 | 579 | |
edce4d98 | 580 | assert(sc); |
06d2839d | 581 | #endif |
62e76326 | 582 | |
06d2839d | 583 | assert(sc->entry == e); |
edce4d98 | 584 | #if SILLY_CODE |
62e76326 | 585 | |
f09f5b26 | 586 | if (sc == NULL) |
62e76326 | 587 | return 0; |
588 | ||
edce4d98 | 589 | #endif |
62e76326 | 590 | |
528b2c61 | 591 | if (!sc->callbackPending()) |
62e76326 | 592 | return 0; |
593 | ||
f09f5b26 | 594 | return 1; |
595 | } | |
596 | ||
06d2839d | 597 | /* |
598 | * This routine hasn't been optimised to take advantage of the | |
599 | * passed sc. Yet. | |
600 | */ | |
f09f5b26 | 601 | int |
a4b8110e | 602 | storeUnregister(store_client * sc, StoreEntry * e, void *data) |
f09f5b26 | 603 | { |
604 | MemObject *mem = e->mem_obj; | |
06d2839d | 605 | #if STORE_CLIENT_LIST_DEBUG |
62e76326 | 606 | |
06d2839d | 607 | assert(sc == storeClientListSearch(e->mem_obj, data)); |
608 | #endif | |
62e76326 | 609 | |
f09f5b26 | 610 | if (mem == NULL) |
62e76326 | 611 | return 0; |
612 | ||
2f44bd34 | 613 | debug(90, 3) ("storeUnregister: called for '%s'\n", e->getMD5Text()); |
62e76326 | 614 | |
2f44bd34 | 615 | if (sc == NULL) { |
62e76326 | 616 | debug(90, 3) ("storeUnregister: No matching client for '%s'\n", e->getMD5Text()); |
617 | return 0; | |
2f44bd34 | 618 | } |
62e76326 | 619 | |
0e3f3e0d | 620 | if (mem->clientCount() == 0) { |
62e76326 | 621 | debug(90, 3) ("storeUnregister: Consistency failure - store client being unregistered is not in the mem object's list for '%s'\n", e->getMD5Text()); |
622 | return 0; | |
2f44bd34 | 623 | } |
62e76326 | 624 | |
0e3f3e0d | 625 | if (mem->clientIsFirst(sc)) { |
62e76326 | 626 | /* |
627 | * If we are unregistering the _first_ client for this | |
628 | * entry, then we have to reset the client FD to -1. | |
629 | */ | |
630 | mem->fd = -1; | |
4c454c5c | 631 | } |
62e76326 | 632 | |
06d2839d | 633 | dlinkDelete(&sc->node, &mem->clients); |
f09f5b26 | 634 | mem->nclients--; |
62e76326 | 635 | |
f09f5b26 | 636 | if (e->store_status == STORE_OK && e->swap_status != SWAPOUT_DONE) |
62e76326 | 637 | storeSwapOut(e); |
638 | ||
d3b3ab85 | 639 | if (sc->swapin_sio.getRaw()) { |
62e76326 | 640 | storeClose(sc->swapin_sio); |
641 | sc->swapin_sio = NULL; | |
642 | statCounter.swap.ins++; | |
eb824054 | 643 | } |
62e76326 | 644 | |
645 | if (sc->callbackPending()) { | |
646 | /* callback with ssize = -1 to indicate unexpected termination */ | |
647 | debug(90, 3) ("storeUnregister: store_client for %s has a callback\n", | |
648 | mem->url); | |
649 | sc->fail(); | |
f09f5b26 | 650 | } |
62e76326 | 651 | |
fa80a8ef | 652 | #if STORE_CLIENT_LIST_DEBUG |
653 | cbdataReferenceDone(sc->owner); | |
62e76326 | 654 | |
fa80a8ef | 655 | #endif |
62e76326 | 656 | |
528b2c61 | 657 | delete sc; |
62e76326 | 658 | |
77b32a34 | 659 | assert(e->lock_count > 0); |
62e76326 | 660 | |
77b32a34 | 661 | if (mem->nclients == 0) |
62e76326 | 662 | CheckQuickAbort(e); |
a46d2c0e | 663 | else |
664 | mem->kickReads(); | |
62e76326 | 665 | |
f09f5b26 | 666 | return 1; |
667 | } | |
668 | ||
669 | off_t | |
670 | storeLowestMemReaderOffset(const StoreEntry * entry) | |
671 | { | |
528b2c61 | 672 | return entry->mem_obj->lowestMemReaderOffset(); |
f09f5b26 | 673 | } |
674 | ||
675 | /* Call handlers waiting for data to be appended to E. */ | |
676 | void | |
677 | InvokeHandlers(StoreEntry * e) | |
678 | { | |
528b2c61 | 679 | /* Commit what we can to disk, if appropriate */ |
680 | storeSwapOut (e); | |
f09f5b26 | 681 | int i = 0; |
682 | MemObject *mem = e->mem_obj; | |
683 | store_client *sc; | |
06d2839d | 684 | dlink_node *nx = NULL; |
685 | dlink_node *node; | |
686 | ||
2f44bd34 | 687 | debug(90, 3) ("InvokeHandlers: %s\n", e->getMD5Text()); |
f09f5b26 | 688 | /* walk the entire list looking for valid callbacks */ |
62e76326 | 689 | |
06d2839d | 690 | for (node = mem->clients.head; node; node = nx) { |
62e76326 | 691 | sc = (store_client *)node->data; |
692 | nx = node->next; | |
693 | debug(90, 3) ("InvokeHandlers: checking client #%d\n", i++); | |
694 | ||
695 | if (!sc->callbackPending()) | |
696 | continue; | |
697 | ||
698 | if (sc->flags.disk_io_pending) | |
699 | continue; | |
700 | ||
701 | storeClientCopy2(e, sc); | |
f09f5b26 | 702 | } |
703 | } | |
704 | ||
705 | int | |
706 | storePendingNClients(const StoreEntry * e) | |
707 | { | |
f09f5b26 | 708 | MemObject *mem = e->mem_obj; |
36547bcf | 709 | int npend = NULL == mem ? 0 : mem->nclients; |
2f44bd34 | 710 | debug(90, 3) ("storePendingNClients: returning %d\n", npend); |
f09f5b26 | 711 | return npend; |
712 | } | |
77b32a34 | 713 | |
714 | /* return 1 if the request should be aborted */ | |
715 | static int | |
716 | CheckQuickAbort2(StoreEntry * entry) | |
717 | { | |
e6ccf245 | 718 | size_t curlen; |
719 | size_t minlen; | |
720 | size_t expectlen; | |
528b2c61 | 721 | MemObject * const mem = entry->mem_obj; |
77b32a34 | 722 | assert(mem); |
2f44bd34 | 723 | debug(90, 3) ("CheckQuickAbort2: entry=%p, mem=%p\n", entry, mem); |
62e76326 | 724 | |
0e5bd28f | 725 | if (mem->request && !mem->request->flags.cachable) { |
62e76326 | 726 | debug(90, 3) ("CheckQuickAbort2: YES !mem->request->flags.cachable\n"); |
727 | return 1; | |
77b32a34 | 728 | } |
62e76326 | 729 | |
77b32a34 | 730 | if (EBIT_TEST(entry->flags, KEY_PRIVATE)) { |
62e76326 | 731 | debug(90, 3) ("CheckQuickAbort2: YES KEY_PRIVATE\n"); |
732 | return 1; | |
77b32a34 | 733 | } |
62e76326 | 734 | |
528b2c61 | 735 | expectlen = entry->getReply()->content_length + entry->getReply()->hdr_sz; |
0e3f3e0d | 736 | |
737 | if (expectlen < 0) | |
738 | /* expectlen is < 0 if *no* information about the object has been recieved */ | |
739 | return 1; | |
740 | ||
528b2c61 | 741 | curlen = (size_t) mem->endOffset (); |
0e3f3e0d | 742 | |
e6ccf245 | 743 | minlen = (size_t) Config.quickAbort.min << 10; |
62e76326 | 744 | |
77b32a34 | 745 | if (minlen < 0) { |
62e76326 | 746 | debug(90, 3) ("CheckQuickAbort2: NO disabled\n"); |
747 | return 0; | |
77b32a34 | 748 | } |
62e76326 | 749 | |
77b32a34 | 750 | if (curlen > expectlen) { |
62e76326 | 751 | debug(90, 3) ("CheckQuickAbort2: YES bad content length\n"); |
752 | return 1; | |
77b32a34 | 753 | } |
62e76326 | 754 | |
77b32a34 | 755 | if ((expectlen - curlen) < minlen) { |
62e76326 | 756 | debug(90, 3) ("CheckQuickAbort2: NO only little more left\n"); |
757 | return 0; | |
77b32a34 | 758 | } |
62e76326 | 759 | |
77b32a34 | 760 | if ((expectlen - curlen) > (Config.quickAbort.max << 10)) { |
62e76326 | 761 | debug(90, 3) ("CheckQuickAbort2: YES too much left to go\n"); |
762 | return 1; | |
77b32a34 | 763 | } |
62e76326 | 764 | |
77b32a34 | 765 | if (expectlen < 100) { |
62e76326 | 766 | debug(90, 3) ("CheckQuickAbort2: NO avoid FPE\n"); |
767 | return 0; | |
77b32a34 | 768 | } |
62e76326 | 769 | |
e6ccf245 | 770 | if ((curlen / (expectlen / 100)) > (size_t)Config.quickAbort.pct) { |
62e76326 | 771 | debug(90, 3) ("CheckQuickAbort2: NO past point of no return\n"); |
772 | return 0; | |
77b32a34 | 773 | } |
62e76326 | 774 | |
2f44bd34 | 775 | debug(90, 3) ("CheckQuickAbort2: YES default, returning 1\n"); |
77b32a34 | 776 | return 1; |
777 | } | |
778 | ||
779 | static void | |
780 | CheckQuickAbort(StoreEntry * entry) | |
781 | { | |
528b2c61 | 782 | assert (entry); |
62e76326 | 783 | |
77b32a34 | 784 | if (storePendingNClients(entry) > 0) |
62e76326 | 785 | return; |
786 | ||
77b32a34 | 787 | if (entry->store_status != STORE_PENDING) |
62e76326 | 788 | return; |
789 | ||
986ebffc | 790 | if (EBIT_TEST(entry->flags, ENTRY_SPECIAL)) |
62e76326 | 791 | return; |
792 | ||
77b32a34 | 793 | if (CheckQuickAbort2(entry) == 0) |
62e76326 | 794 | return; |
795 | ||
7197b20d | 796 | storeAbort(entry); |
77b32a34 | 797 | } |
c8be6d7b | 798 | |
799 | void | |
528b2c61 | 800 | store_client::dumpStats(StoreEntry * output, int clientNumber) const |
c8be6d7b | 801 | { |
528b2c61 | 802 | if (callbackPending()) |
62e76326 | 803 | return; |
804 | ||
528b2c61 | 805 | storeAppendPrintf(output, "\tClient #%d, %p\n", clientNumber, _callback.callback_data); |
62e76326 | 806 | |
c8be6d7b | 807 | storeAppendPrintf(output, "\t\tcopy_offset: %lu\n", |
62e76326 | 808 | (unsigned long) copyInto.offset); |
809 | ||
c8be6d7b | 810 | storeAppendPrintf(output, "\t\tcopy_size: %d\n", |
62e76326 | 811 | (int) copyInto.length); |
812 | ||
c8be6d7b | 813 | storeAppendPrintf(output, "\t\tflags:"); |
62e76326 | 814 | |
528b2c61 | 815 | if (flags.disk_io_pending) |
62e76326 | 816 | storeAppendPrintf(output, " disk_io_pending"); |
817 | ||
528b2c61 | 818 | if (flags.store_copying) |
62e76326 | 819 | storeAppendPrintf(output, " store_copying"); |
820 | ||
528b2c61 | 821 | if (flags.copy_event_pending) |
62e76326 | 822 | storeAppendPrintf(output, " copy_event_pending"); |
823 | ||
c8be6d7b | 824 | storeAppendPrintf(output, "\n"); |
528b2c61 | 825 | } |
c8be6d7b | 826 | |
528b2c61 | 827 | bool |
828 | store_client::callbackPending() const | |
829 | { | |
830 | return _callback.callback_handler && _callback.callback_data; | |
c8be6d7b | 831 | } |
528b2c61 | 832 | |
833 | store_client::Callback::Callback(STCB *function, void *data) : callback_handler(function), callback_data (data) {} | |
b67e2c8c | 834 | |
515ec4dc | 835 | #if DELAY_POOLS |
b67e2c8c | 836 | void |
837 | store_client::setDelayId(DelayId delay_id) | |
838 | { | |
839 | delayId = delay_id; | |
840 | } | |
62e76326 | 841 | |
515ec4dc | 842 | #endif |