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