]>
Commit | Line | Data |
---|---|---|
9cef6668 | 1 | |
2 | /* | |
262a0e14 | 3 | * $Id$ |
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. | |
26ac0430 | 24 | * |
9cef6668 | 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. | |
26ac0430 | 29 | * |
9cef6668 | 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" |
a553a5a3 | 38 | #include "event.h" |
c8be6d7b | 39 | #include "StoreClient.h" |
e6ccf245 | 40 | #include "Store.h" |
528b2c61 | 41 | #include "HttpReply.h" |
42 | #include "MemObject.h" | |
43 | #include "StoreMeta.h" | |
44 | #include "StoreMetaUnpacker.h" | |
b67e2c8c | 45 | #if DELAY_POOLS |
46 | #include "DelayPools.h" | |
47 | #endif | |
a2ac85d9 | 48 | #include "HttpRequest.h" |
0eb49b6d | 49 | #include "MemBuf.h" |
c8be6d7b | 50 | |
e3ef2b09 | 51 | /* |
52 | * NOTE: 'Header' refers to the swapfile metadata header. | |
528b2c61 | 53 | * 'OBJHeader' refers to the object header, with cannonical |
54 | * processed object headers (which may derive from FTP/HTTP etc | |
55 | * upstream protocols | |
e3ef2b09 | 56 | * 'Body' refers to the swapfile body, which is the full |
57 | * HTTP reply (including HTTP headers and body). | |
58 | */ | |
4fcc8876 | 59 | static StoreIOState::STRCB storeClientReadBody; |
60 | static StoreIOState::STRCB storeClientReadHeader; | |
f09f5b26 | 61 | static void storeClientCopy2(StoreEntry * e, store_client * sc); |
d6f51e3c | 62 | static EVH storeClientCopyEvent; |
77b32a34 | 63 | static int CheckQuickAbort2(StoreEntry * entry); |
64 | static void CheckQuickAbort(StoreEntry * entry); | |
f09f5b26 | 65 | |
b001e822 | 66 | CBDATA_CLASS_INIT(store_client); |
528b2c61 | 67 | |
68 | void * | |
b001e822 | 69 | store_client::operator new (size_t) |
528b2c61 | 70 | { |
528b2c61 | 71 | CBDATA_INIT_TYPE(store_client); |
b001e822 | 72 | store_client *result = cbdataAlloc(store_client); |
73 | return result; | |
528b2c61 | 74 | } |
75 | ||
76 | void | |
77 | store_client::operator delete (void *address) | |
78 | { | |
b001e822 | 79 | store_client *t = static_cast<store_client *>(address); |
80 | cbdataFree(t); | |
528b2c61 | 81 | } |
82 | ||
83 | bool | |
47f6e231 | 84 | store_client::memReaderHasLowerOffset(int64_t anOffset) const |
528b2c61 | 85 | { |
86 | return getType() == STORE_MEM_CLIENT && copyInto.offset < anOffset; | |
87 | } | |
88 | ||
89 | int | |
90 | store_client::getType() const | |
91 | { | |
92 | return type; | |
93 | } | |
94 | ||
06d2839d | 95 | #if STORE_CLIENT_LIST_DEBUG |
fa80a8ef | 96 | static store_client * |
f09f5b26 | 97 | storeClientListSearch(const MemObject * mem, void *data) |
98 | { | |
06d2839d | 99 | dlink_node *node; |
100 | store_client *sc = NULL; | |
62e76326 | 101 | |
06d2839d | 102 | for (node = mem->clients.head; node; node = node->next) { |
62e76326 | 103 | sc = node->data; |
104 | ||
105 | if (sc->owner == data) | |
106 | return sc; | |
f09f5b26 | 107 | } |
62e76326 | 108 | |
06d2839d | 109 | return NULL; |
f09f5b26 | 110 | } |
c8be6d7b | 111 | |
112 | int | |
113 | storeClientIsThisAClient(store_client * sc, void *someClient) | |
114 | { | |
115 | return sc->owner == someClient; | |
116 | } | |
62e76326 | 117 | |
06d2839d | 118 | #endif |
924f73bc | 119 | #include "HttpRequest.h" |
f09f5b26 | 120 | |
121 | /* add client with fd to client list */ | |
06d2839d | 122 | store_client * |
f09f5b26 | 123 | storeClientListAdd(StoreEntry * e, void *data) |
124 | { | |
125 | MemObject *mem = e->mem_obj; | |
f09f5b26 | 126 | store_client *sc; |
127 | assert(mem); | |
06d2839d | 128 | #if STORE_CLIENT_LIST_DEBUG |
62e76326 | 129 | |
f09f5b26 | 130 | if (storeClientListSearch(mem, data) != NULL) |
62e76326 | 131 | /* XXX die! */ |
132 | assert(1 == 0); | |
133 | ||
6b8e7481 | 134 | #endif |
62e76326 | 135 | |
528b2c61 | 136 | sc = new store_client (e); |
62e76326 | 137 | |
528b2c61 | 138 | mem->addClient(sc); |
62e76326 | 139 | |
06d2839d | 140 | return sc; |
f09f5b26 | 141 | } |
142 | ||
528b2c61 | 143 | void |
144 | store_client::callback(ssize_t sz, bool error) | |
b04e66e0 | 145 | { |
967a359a | 146 | StoreIOBuffer result(sz, 0 ,copyInto.data); |
62e76326 | 147 | |
964ae882 | 148 | if (sz < 0) { |
62e76326 | 149 | result.flags.error = 1; |
964ae882 AJ |
150 | result.length = 0; |
151 | } else { | |
152 | result.flags.error = error ? 1 : 0; | |
c8be6d7b | 153 | } |
62e76326 | 154 | |
528b2c61 | 155 | result.offset = cmp_offset; |
90703668 | 156 | assert(_callback.pending()); |
528b2c61 | 157 | cmp_offset = copyInto.offset + sz; |
158 | STCB *temphandler = _callback.callback_handler; | |
159 | void *cbdata = _callback.callback_data; | |
160 | _callback = Callback(NULL, NULL); | |
161 | copyInto.data = NULL; | |
62e76326 | 162 | |
528b2c61 | 163 | if (cbdataReferenceValid(cbdata)) |
62e76326 | 164 | temphandler(cbdata, result); |
165 | ||
528b2c61 | 166 | cbdataReferenceDone(cbdata); |
b04e66e0 | 167 | } |
168 | ||
f115fadd | 169 | static void |
170 | storeClientCopyEvent(void *data) | |
171 | { | |
e6ccf245 | 172 | store_client *sc = (store_client *)data; |
bf8fe701 | 173 | debugs(90, 3, "storeClientCopyEvent: Running"); |
528b2c61 | 174 | assert (sc->flags.copy_event_pending); |
67fd69de | 175 | sc->flags.copy_event_pending = 0; |
62e76326 | 176 | |
90703668 | 177 | if (!sc->_callback.pending()) |
62e76326 | 178 | return; |
179 | ||
f115fadd | 180 | storeClientCopy2(sc->entry, sc); |
f115fadd | 181 | } |
182 | ||
b67e2c8c | 183 | store_client::store_client(StoreEntry *e) : entry (e) |
184 | #if DELAY_POOLS | |
62e76326 | 185 | , delayId() |
b67e2c8c | 186 | #endif |
62e76326 | 187 | , type (e->storeClientType()) |
188 | , object_ok(true) | |
528b2c61 | 189 | { |
190 | cmp_offset = 0; | |
191 | flags.disk_io_pending = 0; | |
192 | entry->refcount++; | |
62e76326 | 193 | |
528b2c61 | 194 | if (getType() == STORE_DISK_CLIENT) |
62e76326 | 195 | /* assert we'll be able to get the data we want */ |
02a2d80b | 196 | /* maybe we should open swapin_sio here */ |
c07cbbf4 | 197 | assert(entry->swap_filen > -1 || entry->swapOutAble()); |
62e76326 | 198 | |
528b2c61 | 199 | #if STORE_CLIENT_LIST_DEBUG |
62e76326 | 200 | |
528b2c61 | 201 | owner = cbdataReference(data); |
62e76326 | 202 | |
528b2c61 | 203 | #endif |
204 | } | |
205 | ||
2f44bd34 | 206 | store_client::~store_client() |
62e76326 | 207 | {} |
2f44bd34 | 208 | |
f09f5b26 | 209 | /* copy bytes requested by the client */ |
210 | void | |
a4b8110e | 211 | storeClientCopy(store_client * sc, |
62e76326 | 212 | StoreEntry * e, |
213 | StoreIOBuffer copyInto, | |
214 | STCB * callback, | |
215 | void *data) | |
f09f5b26 | 216 | { |
528b2c61 | 217 | assert (sc != NULL); |
218 | sc->copy(e, copyInto,callback,data); | |
219 | } | |
220 | ||
221 | void | |
222 | store_client::copy(StoreEntry * anEntry, | |
62e76326 | 223 | StoreIOBuffer copyRequest, |
224 | STCB * callback_fn, | |
225 | void *data) | |
528b2c61 | 226 | { |
227 | assert (anEntry == entry); | |
228 | assert (callback_fn); | |
229 | assert (data); | |
230 | assert(!EBIT_TEST(entry->flags, ENTRY_ABORTED)); | |
bf8fe701 | 231 | debugs(90, 3, "store_client::copy: " << entry->getMD5Text() << ", from " << |
47f6e231 | 232 | copyRequest.offset << ", for length " << |
bf8fe701 | 233 | (int) copyRequest.length << ", cb " << callback_fn << ", cbdata " << |
234 | data); | |
235 | ||
06d2839d | 236 | #if STORE_CLIENT_LIST_DEBUG |
62e76326 | 237 | |
528b2c61 | 238 | assert(this == storeClientListSearch(entry->mem_obj, data)); |
06d2839d | 239 | #endif |
62e76326 | 240 | |
90703668 | 241 | assert(!_callback.pending()); |
528b2c61 | 242 | #if ONLYCONTIGUOUSREQUESTS |
62e76326 | 243 | |
528b2c61 | 244 | assert(cmp_offset == copyRequest.offset); |
245 | #endif | |
246 | /* range requests will skip into the body */ | |
247 | cmp_offset = copyRequest.offset; | |
248 | _callback = Callback (callback_fn, cbdataReference(data)); | |
249 | copyInto.data = copyRequest.data; | |
250 | copyInto.length = copyRequest.length; | |
251 | copyInto.offset = copyRequest.offset; | |
252 | ||
1d5161bd | 253 | static bool copying (false); |
254 | assert (!copying); | |
255 | copying = true; | |
256 | PROF_start(storeClient_kickReads); | |
a46d2c0e | 257 | /* we might be blocking comm reads due to readahead limits |
258 | * now we have a new offset, trigger those reads... | |
259 | */ | |
260 | entry->mem_obj->kickReads(); | |
1d5161bd | 261 | PROF_stop(storeClient_kickReads); |
262 | copying = false; | |
a46d2c0e | 263 | |
528b2c61 | 264 | storeClientCopy2(entry, this); |
f09f5b26 | 265 | } |
266 | ||
07304bf9 | 267 | /* |
268 | * This function is used below to decide if we have any more data to | |
0bb129ee | 269 | * send to the client. If the store_status is STORE_PENDING, then we |
b7fe0ab0 | 270 | * do have more data to send. If its STORE_OK, then |
0bb129ee | 271 | * we continue checking. If the object length is negative, then we |
272 | * don't know the real length and must open the swap file to find out. | |
273 | * If the length is >= 0, then we compare it to the requested copy | |
274 | * offset. | |
07304bf9 | 275 | */ |
276 | static int | |
277 | storeClientNoMoreToSend(StoreEntry * e, store_client * sc) | |
278 | { | |
47f6e231 | 279 | int64_t len; |
62e76326 | 280 | |
0bb129ee | 281 | if (e->store_status == STORE_PENDING) |
62e76326 | 282 | return 0; |
283 | ||
707fdc47 | 284 | if ((len = e->objectLen()) < 0) |
62e76326 | 285 | return 0; |
286 | ||
c8be6d7b | 287 | if (sc->copyInto.offset < len) |
62e76326 | 288 | return 0; |
289 | ||
07304bf9 | 290 | return 1; |
291 | } | |
292 | ||
f09f5b26 | 293 | static void |
294 | storeClientCopy2(StoreEntry * e, store_client * sc) | |
295 | { | |
528b2c61 | 296 | /* reentrancy not allowed - note this could lead to |
297 | * dropped events | |
298 | */ | |
62e76326 | 299 | |
fa80a8ef | 300 | if (sc->flags.copy_event_pending) { |
62e76326 | 301 | return; |
fa80a8ef | 302 | } |
62e76326 | 303 | |
db1cd23c | 304 | if (EBIT_TEST(e->flags, ENTRY_FWD_HDR_WAIT)) { |
bf8fe701 | 305 | debugs(90, 5, "storeClientCopy2: returning because ENTRY_FWD_HDR_WAIT set"); |
62e76326 | 306 | return; |
db1cd23c | 307 | } |
62e76326 | 308 | |
67fd69de | 309 | if (sc->flags.store_copying) { |
62e76326 | 310 | sc->flags.copy_event_pending = 1; |
bf8fe701 | 311 | debugs(90, 3, "storeClientCopy2: Queueing storeClientCopyEvent()"); |
62e76326 | 312 | eventAdd("storeClientCopyEvent", storeClientCopyEvent, sc, 0.0, 0); |
313 | return; | |
67fd69de | 314 | } |
62e76326 | 315 | |
bf8fe701 | 316 | debugs(90, 3, "storeClientCopy2: " << e->getMD5Text()); |
90703668 | 317 | assert(sc->_callback.pending()); |
0bb129ee | 318 | /* |
b7fe0ab0 | 319 | * We used to check for ENTRY_ABORTED here. But there were some |
0bb129ee | 320 | * problems. For example, we might have a slow client (or two) and |
321 | * the server-side is reading far ahead and swapping to disk. Even | |
322 | * if the server-side aborts, we want to give the client(s) | |
323 | * everything we got before the abort condition occurred. | |
324 | */ | |
528b2c61 | 325 | /* Warning: doCopy may indirectly free itself in callbacks, |
7e6b941f | 326 | * hence the lock to keep it active for the duration of |
fa80a8ef | 327 | * this function |
328 | */ | |
7e6b941f | 329 | cbdataInternalLock(sc); |
528b2c61 | 330 | assert (sc->flags.store_copying == 0); |
331 | sc->doCopy(e); | |
332 | assert (sc->flags.store_copying == 0); | |
7e6b941f | 333 | cbdataInternalUnlock(sc); |
cfac48c2 | 334 | } |
335 | ||
528b2c61 | 336 | void |
337 | store_client::doCopy(StoreEntry *anEntry) | |
cfac48c2 | 338 | { |
528b2c61 | 339 | assert (anEntry == entry); |
340 | flags.store_copying = 1; | |
341 | MemObject *mem = entry->mem_obj; | |
cd748f27 | 342 | |
bf8fe701 | 343 | debugs(33, 5, "store_client::doCopy: co: " << |
47f6e231 | 344 | copyInto.offset << ", hi: " << |
345 | mem->endOffset()); | |
add2192d | 346 | |
528b2c61 | 347 | if (storeClientNoMoreToSend(entry, this)) { |
62e76326 | 348 | /* There is no more to send! */ |
26ac0430 | 349 | debugs(33, 3, HERE << "There is no more to send!"); |
62e76326 | 350 | callback(0); |
351 | flags.store_copying = 0; | |
352 | return; | |
cfac48c2 | 353 | } |
62e76326 | 354 | |
add2192d | 355 | /* Check that we actually have data */ |
528b2c61 | 356 | if (anEntry->store_status == STORE_PENDING && copyInto.offset >= mem->endOffset()) { |
bf8fe701 | 357 | debugs(90, 3, "store_client::doCopy: Waiting for more"); |
62e76326 | 358 | flags.store_copying = 0; |
359 | return; | |
cfac48c2 | 360 | } |
62e76326 | 361 | |
cfac48c2 | 362 | /* |
363 | * Slight weirdness here. We open a swapin file for any | |
364 | * STORE_DISK_CLIENT, even if we can copy the requested chunk | |
365 | * from memory in the next block. We must try to open the | |
366 | * swapin file before sending any data to the client side. If | |
367 | * we postpone the open, and then can not open the file later | |
368 | * on, the client loses big time. Its transfer just gets cut | |
369 | * off. Better to open it early (while the client side handler | |
370 | * is clientCacheHit) so that we can fall back to a cache miss | |
371 | * if needed. | |
372 | */ | |
fa80a8ef | 373 | |
85a4b153 | 374 | if (STORE_DISK_CLIENT == getType() && swapin_sio == NULL) |
4e70dae3 | 375 | startSwapin(); |
376 | else | |
377 | scheduleRead(); | |
378 | } | |
379 | ||
380 | void | |
381 | store_client::startSwapin() | |
382 | { | |
bf8fe701 | 383 | debugs(90, 3, "store_client::doCopy: Need to open swap in file"); |
4e70dae3 | 384 | /* gotta open the swapin file */ |
385 | ||
386 | if (storeTooManyDiskFilesOpen()) { | |
387 | /* yuck -- this causes a TCP_SWAPFAIL_MISS on the client side */ | |
388 | fail(); | |
389 | flags.store_copying = 0; | |
390 | return; | |
391 | } else if (!flags.disk_io_pending) { | |
392 | /* Don't set store_io_pending here */ | |
393 | storeSwapInStart(this); | |
62e76326 | 394 | |
85a4b153 | 395 | if (swapin_sio == NULL) { |
62e76326 | 396 | fail(); |
397 | flags.store_copying = 0; | |
398 | return; | |
62e76326 | 399 | } |
62e76326 | 400 | |
4e70dae3 | 401 | /* |
402 | * If the open succeeds we either copy from memory, or | |
403 | * schedule a disk read in the next block. | |
404 | */ | |
405 | scheduleRead(); | |
406 | ||
407 | return; | |
408 | } else { | |
bf8fe701 | 409 | debugs(90, 1, "WARNING: Averted multiple fd operation (1)"); |
62e76326 | 410 | flags.store_copying = 0; |
411 | return; | |
cfac48c2 | 412 | } |
4e70dae3 | 413 | } |
62e76326 | 414 | |
4e70dae3 | 415 | void |
416 | store_client::scheduleRead() | |
417 | { | |
418 | MemObject *mem = entry->mem_obj; | |
419 | ||
420 | if (copyInto.offset >= mem->inmem_lo && copyInto.offset < mem->endOffset()) | |
421 | scheduleMemRead(); | |
422 | else | |
423 | scheduleDiskRead(); | |
424 | } | |
425 | ||
426 | void | |
427 | store_client::scheduleDiskRead() | |
428 | { | |
cd748f27 | 429 | /* What the client wants is not in memory. Schedule a disk read */ |
528b2c61 | 430 | assert(STORE_DISK_CLIENT == getType()); |
62e76326 | 431 | |
528b2c61 | 432 | assert(!flags.disk_io_pending); |
62e76326 | 433 | |
bf8fe701 | 434 | debugs(90, 3, "store_client::doCopy: reading from STORE"); |
62e76326 | 435 | |
528b2c61 | 436 | fileRead(); |
62e76326 | 437 | |
528b2c61 | 438 | flags.store_copying = 0; |
f09f5b26 | 439 | } |
440 | ||
4e70dae3 | 441 | void |
442 | store_client::scheduleMemRead() | |
443 | { | |
444 | /* What the client wants is in memory */ | |
445 | /* Old style */ | |
bf8fe701 | 446 | debugs(90, 3, "store_client::doCopy: Copying normal from memory"); |
90703668 | 447 | size_t sz = entry->mem_obj->data_hdr.copy(copyInto); |
4e70dae3 | 448 | callback(sz); |
449 | flags.store_copying = 0; | |
450 | } | |
451 | ||
528b2c61 | 452 | void |
453 | store_client::fileRead() | |
f09f5b26 | 454 | { |
528b2c61 | 455 | MemObject *mem = entry->mem_obj; |
456 | ||
90703668 | 457 | assert(_callback.pending()); |
528b2c61 | 458 | assert(!flags.disk_io_pending); |
459 | flags.disk_io_pending = 1; | |
62e76326 | 460 | |
528b2c61 | 461 | if (mem->swap_hdr_sz != 0) |
62e76326 | 462 | if (entry->swap_status == SWAPOUT_WRITING) |
47f6e231 | 463 | assert(mem->swapout.sio->offset() > copyInto.offset + (int64_t)mem->swap_hdr_sz); |
62e76326 | 464 | |
528b2c61 | 465 | storeRead(swapin_sio, |
62e76326 | 466 | copyInto.data, |
467 | copyInto.length, | |
468 | copyInto.offset + mem->swap_hdr_sz, | |
469 | mem->swap_hdr_sz == 0 ? storeClientReadHeader | |
470 | : storeClientReadBody, | |
471 | this); | |
f09f5b26 | 472 | } |
473 | ||
474 | static void | |
beae59b0 | 475 | storeClientMemWriteComplete(void *data, StoreIOBuffer wroteBuffer) |
f09f5b26 | 476 | { |
3196f526 | 477 | // Nothin to do here but callback is needed |
beae59b0 HN |
478 | } |
479 | ||
480 | void | |
481 | store_client::readBody(const char *buf, ssize_t len) | |
482 | { | |
483 | int parsed_header = 0; | |
484 | ||
485 | // Don't assert disk_io_pending here.. may be called by read_header | |
486 | flags.disk_io_pending = 0; | |
487 | assert(_callback.pending()); | |
4a7a3d56 | 488 | debugs(90, 3, "storeClientReadBody: len " << len << ""); |
62e76326 | 489 | |
beae59b0 | 490 | if (copyInto.offset == 0 && len > 0 && entry->getReply()->sline.status == 0) { |
62e76326 | 491 | /* Our structure ! */ |
beae59b0 | 492 | HttpReply *rep = (HttpReply *) entry->getReply(); // bypass const |
06a5ae20 | 493 | |
beae59b0 | 494 | if (!rep->parseCharBuf(copyInto.data, headersEnd(copyInto.data, len))) { |
bf8fe701 | 495 | debugs(90, 0, "Could not parse headers from on disk object"); |
beae59b0 | 496 | } else { |
3196f526 HN |
497 | parsed_header = 1; |
498 | } | |
06a5ae20 | 499 | } |
62e76326 | 500 | |
beae59b0 HN |
501 | const HttpReply *rep = entry->getReply(); |
502 | if (len > 0 && rep && entry->mem_obj->inmem_lo == 0 && entry->objectLen() <= (int64_t)Config.Store.maxInMemObjSize) { | |
6d3c2758 HN |
503 | storeGetMemSpace(len); |
504 | // The above may start to free our object so we need to check again | |
505 | if (entry->mem_obj->inmem_lo == 0) { | |
506 | /* Copy read data back into memory. | |
507 | * but first we need to adjust offset.. some parts of the code | |
508 | * counts offset including headers, some parts count offset as | |
509 | * withing the body.. copyInto is including headers, but the mem | |
510 | * cache expects offset without headers (using negative for headers) | |
511 | * eventually not storing packed headers in memory at all. | |
512 | */ | |
619da1e9 | 513 | int64_t mem_offset = entry->mem_obj->endOffset(); |
6d3c2758 HN |
514 | if ((copyInto.offset == mem_offset) || (parsed_header && mem_offset == rep->hdr_sz)) { |
515 | entry->mem_obj->write(StoreIOBuffer(len, copyInto.offset - rep->hdr_sz, copyInto.data), storeClientMemWriteComplete, this); | |
516 | } | |
3196f526 | 517 | } |
beae59b0 HN |
518 | } |
519 | ||
520 | callback(len); | |
528b2c61 | 521 | } |
522 | ||
523 | void | |
62e76326 | 524 | store_client::fail() |
528b2c61 | 525 | { |
526 | object_ok = false; | |
db2ff94c | 527 | /* synchronous open failures callback from the store, |
528 | * before startSwapin detects the failure. | |
529 | * TODO: fix this inconsistent behaviour - probably by | |
26ac0430 | 530 | * having storeSwapInStart become a callback functions, |
db2ff94c | 531 | * not synchronous |
532 | */ | |
533 | ||
90703668 | 534 | if (_callback.pending()) |
db2ff94c | 535 | callback(0, true); |
f09f5b26 | 536 | } |
537 | ||
e3ef2b09 | 538 | static void |
e5de8b13 | 539 | storeClientReadHeader(void *data, const char *buf, ssize_t len, StoreIOState::Pointer self) |
e3ef2b09 | 540 | { |
e6ccf245 | 541 | store_client *sc = (store_client *)data; |
528b2c61 | 542 | sc->readHeader(buf, len); |
543 | } | |
544 | ||
beae59b0 HN |
545 | static void |
546 | storeClientReadBody(void *data, const char *buf, ssize_t len, StoreIOState::Pointer self) | |
547 | { | |
548 | store_client *sc = (store_client *)data; | |
549 | sc->readBody(buf, len); | |
550 | } | |
551 | ||
528b2c61 | 552 | void |
553 | store_client::unpackHeader(char const *buf, ssize_t len) | |
554 | { | |
4a7a3d56 | 555 | debugs(90, 3, "store_client::unpackHeader: len " << len << ""); |
62e76326 | 556 | |
e3ef2b09 | 557 | if (len < 0) { |
bf8fe701 | 558 | debugs(90, 3, "store_client::unpackHeader: " << xstrerror() << ""); |
62e76326 | 559 | fail(); |
560 | return; | |
e3ef2b09 | 561 | } |
62e76326 | 562 | |
528b2c61 | 563 | int swap_hdr_sz = 0; |
564 | StoreMetaUnpacker aBuilder(buf, len, &swap_hdr_sz); | |
62e76326 | 565 | |
528b2c61 | 566 | if (!aBuilder.isBufferSane()) { |
62e76326 | 567 | /* oops, bad disk file? */ |
bf8fe701 | 568 | debugs(90, 1, "WARNING: swapfile header inconsistent with available data"); |
62e76326 | 569 | fail(); |
570 | return; | |
9bc73deb | 571 | } |
62e76326 | 572 | |
528b2c61 | 573 | tlv *tlv_list = aBuilder.createStoreMeta (); |
62e76326 | 574 | |
e3ef2b09 | 575 | if (tlv_list == NULL) { |
bf8fe701 | 576 | debugs(90, 1, "WARNING: failed to unpack meta data"); |
62e76326 | 577 | fail(); |
578 | return; | |
e3ef2b09 | 579 | } |
62e76326 | 580 | |
e3ef2b09 | 581 | /* |
7e3ce7b9 | 582 | * Check the meta data and make sure we got the right object. |
e3ef2b09 | 583 | */ |
528b2c61 | 584 | for (tlv *t = tlv_list; t; t = t->next) { |
62e76326 | 585 | if (!t->checkConsistency(entry)) { |
586 | storeSwapTLVFree(tlv_list); | |
587 | fail(); | |
588 | return; | |
589 | } | |
7e3ce7b9 | 590 | } |
62e76326 | 591 | |
07304bf9 | 592 | storeSwapTLVFree(tlv_list); |
528b2c61 | 593 | |
594 | entry->mem_obj->swap_hdr_sz = swap_hdr_sz; | |
595 | entry->mem_obj->object_sz = entry->swap_file_sz - swap_hdr_sz; | |
596 | ||
597 | } | |
598 | ||
599 | void | |
600 | store_client::readHeader(char const *buf, ssize_t len) | |
601 | { | |
602 | MemObject *const mem = entry->mem_obj; | |
62e76326 | 603 | |
528b2c61 | 604 | assert(flags.disk_io_pending); |
605 | flags.disk_io_pending = 0; | |
90703668 | 606 | assert(_callback.pending()); |
528b2c61 | 607 | |
608 | unpackHeader (buf, len); | |
62e76326 | 609 | |
528b2c61 | 610 | if (!object_ok) |
62e76326 | 611 | return; |
612 | ||
e3ef2b09 | 613 | /* |
614 | * If our last read got some data the client wants, then give | |
615 | * it to them, otherwise schedule another read. | |
616 | */ | |
528b2c61 | 617 | size_t body_sz = len - mem->swap_hdr_sz; |
62e76326 | 618 | |
47f6e231 | 619 | if (copyInto.offset < static_cast<int64_t>(body_sz)) { |
62e76326 | 620 | /* |
621 | * we have (part of) what they want | |
622 | */ | |
d85c3078 | 623 | size_t copy_sz = min(copyInto.length, body_sz); |
4a7a3d56 | 624 | debugs(90, 3, "storeClientReadHeader: copying " << copy_sz << " bytes of body"); |
62e76326 | 625 | xmemmove(copyInto.data, copyInto.data + mem->swap_hdr_sz, copy_sz); |
626 | ||
3196f526 | 627 | readBody(copyInto.data, copy_sz); |
528b2c61 | 628 | |
62e76326 | 629 | return; |
e3ef2b09 | 630 | } |
62e76326 | 631 | |
e3ef2b09 | 632 | /* |
633 | * we don't have what the client wants, but at least we now | |
634 | * know the swap header size. | |
635 | */ | |
528b2c61 | 636 | fileRead(); |
e3ef2b09 | 637 | } |
638 | ||
f09f5b26 | 639 | int |
a4b8110e | 640 | storeClientCopyPending(store_client * sc, StoreEntry * e, void *data) |
f09f5b26 | 641 | { |
06d2839d | 642 | #if STORE_CLIENT_LIST_DEBUG |
643 | assert(sc == storeClientListSearch(e->mem_obj, data)); | |
edce4d98 | 644 | #endif |
645 | #ifndef SILLY_CODE | |
62e76326 | 646 | |
edce4d98 | 647 | assert(sc); |
06d2839d | 648 | #endif |
62e76326 | 649 | |
06d2839d | 650 | assert(sc->entry == e); |
edce4d98 | 651 | #if SILLY_CODE |
62e76326 | 652 | |
f09f5b26 | 653 | if (sc == NULL) |
62e76326 | 654 | return 0; |
655 | ||
edce4d98 | 656 | #endif |
62e76326 | 657 | |
90703668 | 658 | if (!sc->_callback.pending()) |
62e76326 | 659 | return 0; |
660 | ||
f09f5b26 | 661 | return 1; |
662 | } | |
663 | ||
06d2839d | 664 | /* |
665 | * This routine hasn't been optimised to take advantage of the | |
666 | * passed sc. Yet. | |
667 | */ | |
f09f5b26 | 668 | int |
a4b8110e | 669 | storeUnregister(store_client * sc, StoreEntry * e, void *data) |
f09f5b26 | 670 | { |
671 | MemObject *mem = e->mem_obj; | |
06d2839d | 672 | #if STORE_CLIENT_LIST_DEBUG |
62e76326 | 673 | |
06d2839d | 674 | assert(sc == storeClientListSearch(e->mem_obj, data)); |
675 | #endif | |
62e76326 | 676 | |
f09f5b26 | 677 | if (mem == NULL) |
62e76326 | 678 | return 0; |
679 | ||
26ac0430 | 680 | debugs(90, 3, "storeUnregister: called for '" << e->getMD5Text() << "'"); |
62e76326 | 681 | |
2f44bd34 | 682 | if (sc == NULL) { |
bf8fe701 | 683 | debugs(90, 3, "storeUnregister: No matching client for '" << e->getMD5Text() << "'"); |
62e76326 | 684 | return 0; |
2f44bd34 | 685 | } |
62e76326 | 686 | |
0e3f3e0d | 687 | if (mem->clientCount() == 0) { |
bf8fe701 | 688 | debugs(90, 3, "storeUnregister: Consistency failure - store client being unregistered is not in the mem object's list for '" << e->getMD5Text() << "'"); |
62e76326 | 689 | return 0; |
2f44bd34 | 690 | } |
62e76326 | 691 | |
06d2839d | 692 | dlinkDelete(&sc->node, &mem->clients); |
f09f5b26 | 693 | mem->nclients--; |
62e76326 | 694 | |
f09f5b26 | 695 | if (e->store_status == STORE_OK && e->swap_status != SWAPOUT_DONE) |
c07cbbf4 | 696 | e->swapOut(); |
62e76326 | 697 | |
85a4b153 | 698 | if (sc->swapin_sio != NULL) { |
62e76326 | 699 | storeClose(sc->swapin_sio); |
700 | sc->swapin_sio = NULL; | |
701 | statCounter.swap.ins++; | |
eb824054 | 702 | } |
62e76326 | 703 | |
90703668 | 704 | if (sc->_callback.pending()) { |
62e76326 | 705 | /* callback with ssize = -1 to indicate unexpected termination */ |
bf8fe701 | 706 | debugs(90, 3, "storeUnregister: store_client for " << mem->url << " has a callback"); |
62e76326 | 707 | sc->fail(); |
f09f5b26 | 708 | } |
62e76326 | 709 | |
fa80a8ef | 710 | #if STORE_CLIENT_LIST_DEBUG |
711 | cbdataReferenceDone(sc->owner); | |
62e76326 | 712 | |
fa80a8ef | 713 | #endif |
62e76326 | 714 | |
528b2c61 | 715 | delete sc; |
62e76326 | 716 | |
77b32a34 | 717 | assert(e->lock_count > 0); |
62e76326 | 718 | |
77b32a34 | 719 | if (mem->nclients == 0) |
62e76326 | 720 | CheckQuickAbort(e); |
a46d2c0e | 721 | else |
722 | mem->kickReads(); | |
62e76326 | 723 | |
f09f5b26 | 724 | return 1; |
725 | } | |
726 | ||
f09f5b26 | 727 | /* Call handlers waiting for data to be appended to E. */ |
728 | void | |
d88e3c49 | 729 | StoreEntry::invokeHandlers() |
f09f5b26 | 730 | { |
528b2c61 | 731 | /* Commit what we can to disk, if appropriate */ |
d88e3c49 | 732 | swapOut(); |
f09f5b26 | 733 | int i = 0; |
f09f5b26 | 734 | store_client *sc; |
06d2839d | 735 | dlink_node *nx = NULL; |
736 | dlink_node *node; | |
737 | ||
0bc8db64 | 738 | PROF_start(InvokeHandlers); |
9ea37c79 | 739 | |
bf8fe701 | 740 | debugs(90, 3, "InvokeHandlers: " << getMD5Text() ); |
f09f5b26 | 741 | /* walk the entire list looking for valid callbacks */ |
62e76326 | 742 | |
d88e3c49 | 743 | for (node = mem_obj->clients.head; node; node = nx) { |
62e76326 | 744 | sc = (store_client *)node->data; |
745 | nx = node->next; | |
bf8fe701 | 746 | debugs(90, 3, "StoreEntry::InvokeHandlers: checking client #" << i++ ); |
62e76326 | 747 | |
90703668 | 748 | if (!sc->_callback.pending()) |
62e76326 | 749 | continue; |
750 | ||
751 | if (sc->flags.disk_io_pending) | |
752 | continue; | |
753 | ||
d88e3c49 | 754 | storeClientCopy2(this, sc); |
f09f5b26 | 755 | } |
0bc8db64 | 756 | PROF_stop(InvokeHandlers); |
f09f5b26 | 757 | } |
758 | ||
759 | int | |
760 | storePendingNClients(const StoreEntry * e) | |
761 | { | |
f09f5b26 | 762 | MemObject *mem = e->mem_obj; |
36547bcf | 763 | int npend = NULL == mem ? 0 : mem->nclients; |
bf8fe701 | 764 | debugs(90, 3, "storePendingNClients: returning " << npend); |
f09f5b26 | 765 | return npend; |
766 | } | |
77b32a34 | 767 | |
768 | /* return 1 if the request should be aborted */ | |
769 | static int | |
770 | CheckQuickAbort2(StoreEntry * entry) | |
771 | { | |
528b2c61 | 772 | MemObject * const mem = entry->mem_obj; |
77b32a34 | 773 | assert(mem); |
bf8fe701 | 774 | debugs(90, 3, "CheckQuickAbort2: entry=" << entry << ", mem=" << mem); |
62e76326 | 775 | |
0e5bd28f | 776 | if (mem->request && !mem->request->flags.cachable) { |
bf8fe701 | 777 | debugs(90, 3, "CheckQuickAbort2: YES !mem->request->flags.cachable"); |
62e76326 | 778 | return 1; |
77b32a34 | 779 | } |
62e76326 | 780 | |
77b32a34 | 781 | if (EBIT_TEST(entry->flags, KEY_PRIVATE)) { |
bf8fe701 | 782 | debugs(90, 3, "CheckQuickAbort2: YES KEY_PRIVATE"); |
62e76326 | 783 | return 1; |
77b32a34 | 784 | } |
62e76326 | 785 | |
47f6e231 | 786 | int64_t expectlen = entry->getReply()->content_length + entry->getReply()->hdr_sz; |
0e3f3e0d | 787 | |
788 | if (expectlen < 0) | |
2324cda2 | 789 | /* expectlen is < 0 if *no* information about the object has been received */ |
0e3f3e0d | 790 | return 1; |
791 | ||
47f6e231 | 792 | int64_t curlen = mem->endOffset (); |
62e76326 | 793 | |
47f6e231 | 794 | if (Config.quickAbort.min < 0) { |
bf8fe701 | 795 | debugs(90, 3, "CheckQuickAbort2: NO disabled"); |
62e76326 | 796 | return 0; |
77b32a34 | 797 | } |
62e76326 | 798 | |
ab275c7b AJ |
799 | if ( Config.rangeOffsetLimit < 0 && mem->request && mem->request->range ) { |
800 | /* Don't abort if the admin has configured range_ofset -1 to download fully for caching. */ | |
801 | debugs(90, 3, "CheckQuickAbort2: NO admin configured range replies to full-download"); | |
802 | return 0; | |
803 | } | |
804 | ||
77b32a34 | 805 | if (curlen > expectlen) { |
bf8fe701 | 806 | debugs(90, 3, "CheckQuickAbort2: YES bad content length"); |
62e76326 | 807 | return 1; |
77b32a34 | 808 | } |
62e76326 | 809 | |
47f6e231 | 810 | if ((expectlen - curlen) < (Config.quickAbort.min << 10)) { |
bf8fe701 | 811 | debugs(90, 3, "CheckQuickAbort2: NO only little more left"); |
62e76326 | 812 | return 0; |
77b32a34 | 813 | } |
62e76326 | 814 | |
77b32a34 | 815 | if ((expectlen - curlen) > (Config.quickAbort.max << 10)) { |
bf8fe701 | 816 | debugs(90, 3, "CheckQuickAbort2: YES too much left to go"); |
62e76326 | 817 | return 1; |
77b32a34 | 818 | } |
62e76326 | 819 | |
77b32a34 | 820 | if (expectlen < 100) { |
bf8fe701 | 821 | debugs(90, 3, "CheckQuickAbort2: NO avoid FPE"); |
62e76326 | 822 | return 0; |
77b32a34 | 823 | } |
62e76326 | 824 | |
47f6e231 | 825 | if ((curlen / (expectlen / 100)) > (Config.quickAbort.pct)) { |
bf8fe701 | 826 | debugs(90, 3, "CheckQuickAbort2: NO past point of no return"); |
62e76326 | 827 | return 0; |
77b32a34 | 828 | } |
62e76326 | 829 | |
bf8fe701 | 830 | debugs(90, 3, "CheckQuickAbort2: YES default, returning 1"); |
77b32a34 | 831 | return 1; |
832 | } | |
833 | ||
834 | static void | |
835 | CheckQuickAbort(StoreEntry * entry) | |
836 | { | |
528b2c61 | 837 | assert (entry); |
62e76326 | 838 | |
77b32a34 | 839 | if (storePendingNClients(entry) > 0) |
62e76326 | 840 | return; |
841 | ||
77b32a34 | 842 | if (entry->store_status != STORE_PENDING) |
62e76326 | 843 | return; |
844 | ||
986ebffc | 845 | if (EBIT_TEST(entry->flags, ENTRY_SPECIAL)) |
62e76326 | 846 | return; |
847 | ||
77b32a34 | 848 | if (CheckQuickAbort2(entry) == 0) |
62e76326 | 849 | return; |
850 | ||
bfb55b6f | 851 | entry->abort(); |
77b32a34 | 852 | } |
c8be6d7b | 853 | |
854 | void | |
fcc35180 | 855 | store_client::dumpStats(MemBuf * output, int clientNumber) const |
c8be6d7b | 856 | { |
90703668 | 857 | if (_callback.pending()) |
62e76326 | 858 | return; |
859 | ||
2fe7eff9 | 860 | output->Printf("\tClient #%d, %p\n", clientNumber, _callback.callback_data); |
62e76326 | 861 | |
47f6e231 | 862 | output->Printf("\t\tcopy_offset: %"PRId64"\n", |
863 | copyInto.offset); | |
62e76326 | 864 | |
2fe7eff9 | 865 | output->Printf("\t\tcopy_size: %d\n", |
866 | (int) copyInto.length); | |
62e76326 | 867 | |
2fe7eff9 | 868 | output->Printf("\t\tflags:"); |
62e76326 | 869 | |
528b2c61 | 870 | if (flags.disk_io_pending) |
2fe7eff9 | 871 | output->Printf(" disk_io_pending"); |
62e76326 | 872 | |
528b2c61 | 873 | if (flags.store_copying) |
2fe7eff9 | 874 | output->Printf(" store_copying"); |
62e76326 | 875 | |
528b2c61 | 876 | if (flags.copy_event_pending) |
2fe7eff9 | 877 | output->Printf(" copy_event_pending"); |
62e76326 | 878 | |
2fe7eff9 | 879 | output->Printf("\n"); |
528b2c61 | 880 | } |
c8be6d7b | 881 | |
528b2c61 | 882 | bool |
90703668 | 883 | store_client::Callback::pending() const |
528b2c61 | 884 | { |
90703668 | 885 | return callback_handler && callback_data; |
c8be6d7b | 886 | } |
528b2c61 | 887 | |
888 | store_client::Callback::Callback(STCB *function, void *data) : callback_handler(function), callback_data (data) {} | |
b67e2c8c | 889 | |
515ec4dc | 890 | #if DELAY_POOLS |
b67e2c8c | 891 | void |
892 | store_client::setDelayId(DelayId delay_id) | |
893 | { | |
894 | delayId = delay_id; | |
895 | } | |
62e76326 | 896 | |
515ec4dc | 897 | #endif |