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