]>
Commit | Line | Data |
---|---|---|
30a4f2a8 | 1 | /* |
4ac4a490 | 2 | * Copyright (C) 1996-2017 The Squid Software Foundation and contributors |
e25c139f | 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. | |
c943f331 | 7 | */ |
090089c4 | 8 | |
bbc27441 AJ |
9 | /* DEBUG: section 20 Storage Manager */ |
10 | ||
582c2af2 | 11 | #include "squid.h" |
b814e8d4 | 12 | #include "CacheDigest.h" |
ec20038e AJ |
13 | #include "CacheManager.h" |
14 | #include "comm/Connection.h" | |
7e66d5e2 | 15 | #include "comm/Read.h" |
81a94152 | 16 | #include "ETag.h" |
a553a5a3 | 17 | #include "event.h" |
04f55905 | 18 | #include "fde.h" |
af69c635 | 19 | #include "globals.h" |
582c2af2 | 20 | #include "http.h" |
528b2c61 | 21 | #include "HttpReply.h" |
22 | #include "HttpRequest.h" | |
528b2c61 | 23 | #include "mem_node.h" |
582c2af2 FC |
24 | #include "MemObject.h" |
25 | #include "mgr/Registration.h" | |
26 | #include "mgr/StoreIoAction.h" | |
27 | #include "profiler/Profiler.h" | |
e452f48d | 28 | #include "repl_modules.h" |
f206b652 | 29 | #include "RequestFlags.h" |
4d5904f7 | 30 | #include "SquidConfig.h" |
582c2af2 | 31 | #include "SquidTime.h" |
e4f1fdae | 32 | #include "StatCounters.h" |
582c2af2 | 33 | #include "stmem.h" |
602d9612 | 34 | #include "Store.h" |
b3f7fd88 AR |
35 | #include "store/Controller.h" |
36 | #include "store/Disk.h" | |
37 | #include "store/Disks.h" | |
35a28a37 | 38 | #include "store_digest.h" |
fb548aaf | 39 | #include "store_key_md5.h" |
10818c0a | 40 | #include "store_log.h" |
687f5275 | 41 | #include "store_rebuild.h" |
e87137f1 FC |
42 | #include "StoreClient.h" |
43 | #include "StoreIOState.h" | |
44 | #include "StoreMeta.h" | |
45 | #include "StrList.h" | |
582c2af2 | 46 | #include "swap_log_op.h" |
5bed43d6 | 47 | #include "tools.h" |
9a0a18de | 48 | #if USE_DELAY_POOLS |
b67e2c8c | 49 | #include "DelayPools.h" |
50 | #endif | |
090089c4 | 51 | |
ed6e9fb9 AJ |
52 | /** StoreEntry uses explicit new/delete operators, which set pool chunk size to 2MB |
53 | * XXX: convert to MEMPROXY_CLASS() API | |
54 | */ | |
55 | #include "mem/Pool.h" | |
56 | ||
074d6a40 | 57 | #include <climits> |
cfb88efb | 58 | #include <stack> |
06e91875 | 59 | |
090089c4 | 60 | #define REBUILD_TIMESTAMP_DELTA_MAX 2 |
227fbb74 | 61 | |
c21ad0f5 | 62 | #define STORE_IN_MEM_BUCKETS (229) |
090089c4 | 63 | |
4b981814 AJ |
64 | /** \todo Convert these string constants to enum string-arrays generated */ |
65 | ||
26ac0430 AJ |
66 | const char *memStatusStr[] = { |
67 | "NOT_IN_MEMORY", | |
68 | "IN_MEMORY" | |
69 | }; | |
70 | ||
71 | const char *pingStatusStr[] = { | |
72 | "PING_NONE", | |
73 | "PING_WAITING", | |
74 | "PING_DONE" | |
75 | }; | |
76 | ||
77 | const char *storeStatusStr[] = { | |
78 | "STORE_OK", | |
79 | "STORE_PENDING" | |
80 | }; | |
81 | ||
82 | const char *swapStatusStr[] = { | |
83 | "SWAPOUT_NONE", | |
84 | "SWAPOUT_WRITING", | |
85 | "SWAPOUT_DONE" | |
86 | }; | |
9dfb6c1c | 87 | |
25b6a907 | 88 | /* |
89 | * This defines an repl type | |
90 | */ | |
91 | ||
92 | typedef struct _storerepl_entry storerepl_entry_t; | |
93 | ||
26ac0430 | 94 | struct _storerepl_entry { |
25b6a907 | 95 | const char *typestr; |
96 | REMOVALPOLICYCREATE *create; | |
97 | }; | |
98 | ||
99 | static storerepl_entry_t *storerepl_list = NULL; | |
100 | ||
e3ef2b09 | 101 | /* |
102 | * local function prototypes | |
103 | */ | |
007b8be4 | 104 | static int getKeyCounter(void); |
8423ff74 | 105 | static OBJH storeCheckCachableStats; |
e42d5181 | 106 | static EVH storeLateRelease; |
a21fbb54 | 107 | |
e3ef2b09 | 108 | /* |
109 | * local variables | |
110 | */ | |
cfb88efb | 111 | static std::stack<StoreEntry*> LateReleaseStack; |
04eb0689 | 112 | MemAllocator *StoreEntry::pool = NULL; |
e6ccf245 | 113 | |
c8f4eac4 | 114 | void |
115 | Store::Stats(StoreEntry * output) | |
116 | { | |
ced8def3 | 117 | assert(output); |
c8f4eac4 | 118 | Root().stat(*output); |
119 | } | |
120 | ||
0b934349 AJ |
121 | // XXX: new/delete operators need to be replaced with MEMPROXY_CLASS |
122 | // definitions but doing so exposes bug 4370, and maybe 4354 and 4355 | |
123 | void * | |
124 | StoreEntry::operator new (size_t bytecount) | |
125 | { | |
126 | assert(bytecount == sizeof (StoreEntry)); | |
127 | ||
128 | if (!pool) { | |
129 | pool = memPoolCreate ("StoreEntry", bytecount); | |
130 | } | |
131 | ||
132 | return pool->alloc(); | |
133 | } | |
134 | ||
135 | void | |
136 | StoreEntry::operator delete (void *address) | |
137 | { | |
138 | pool->freeOne(address); | |
139 | } | |
140 | ||
5ed72359 | 141 | void |
1a210de4 | 142 | StoreEntry::makePublic(const KeyScope scope) |
5ed72359 | 143 | { |
144 | /* This object can be cached for a long time */ | |
6919be24 | 145 | if (!EBIT_TEST(flags, RELEASE_REQUEST)) |
1a210de4 | 146 | setPublicKey(scope); |
5ed72359 | 147 | } |
148 | ||
149 | void | |
150 | StoreEntry::makePrivate() | |
151 | { | |
152 | /* This object should never be cached at all */ | |
d88e3c49 | 153 | expireNow(); |
154 | releaseRequest(); /* delete object when not used */ | |
5ed72359 | 155 | } |
156 | ||
157 | void | |
158 | StoreEntry::cacheNegatively() | |
159 | { | |
160 | /* This object may be negatively cached */ | |
d88e3c49 | 161 | negativeCache(); |
6919be24 | 162 | makePublic(); |
5ed72359 | 163 | } |
164 | ||
e6ccf245 | 165 | size_t |
3b13a8fd | 166 | StoreEntry::inUseCount() |
e6ccf245 | 167 | { |
168 | if (!pool) | |
62e76326 | 169 | return 0; |
9f9e06f3 | 170 | return pool->getInUseCount(); |
e6ccf245 | 171 | } |
172 | ||
332dafa2 | 173 | const char * |
3b13a8fd | 174 | StoreEntry::getMD5Text() const |
332dafa2 | 175 | { |
176 | return storeKeyText((const cache_key *)key); | |
177 | } | |
178 | ||
a46d2c0e | 179 | #include "comm.h" |
180 | ||
181 | void | |
182 | StoreEntry::DeferReader(void *theContext, CommRead const &aRead) | |
e6ccf245 | 183 | { |
a46d2c0e | 184 | StoreEntry *anEntry = (StoreEntry *)theContext; |
3e4bebf8 | 185 | anEntry->delayAwareRead(aRead.conn, |
a46d2c0e | 186 | aRead.buf, |
187 | aRead.len, | |
65517dc8 | 188 | aRead.callback); |
e6ccf245 | 189 | } |
66cedb85 | 190 | |
a46d2c0e | 191 | void |
3e4bebf8 | 192 | StoreEntry::delayAwareRead(const Comm::ConnectionPointer &conn, char *buf, int len, AsyncCall::Pointer callback) |
a46d2c0e | 193 | { |
194 | size_t amountToRead = bytesWanted(Range<size_t>(0, len)); | |
195 | /* sketch: readdeferer* = getdeferer. | |
65517dc8 | 196 | * ->deferRead (fd, buf, len, callback, DelayAwareRead, this) |
a46d2c0e | 197 | */ |
198 | ||
f1ba1fba | 199 | if (amountToRead <= 0) { |
a46d2c0e | 200 | assert (mem_obj); |
f1ba1fba | 201 | mem_obj->delayRead(DeferredRead(DeferReader, this, CommRead(conn, buf, len, callback))); |
a46d2c0e | 202 | return; |
203 | } | |
204 | ||
109cf61c | 205 | if (fd_table[conn->fd].closing()) { |
f5f9e44c AR |
206 | // Readers must have closing callbacks if they want to be notified. No |
207 | // readers appeared to care around 2009/12/14 as they skipped reading | |
208 | // for other reasons. Closing may already be true at the delyaAwareRead | |
209 | // call time or may happen while we wait after delayRead() above. | |
109cf61c | 210 | debugs(20, 3, HERE << "wont read from closing " << conn << " for " << |
f5f9e44c AR |
211 | callback); |
212 | return; // the read callback will never be called | |
213 | } | |
214 | ||
3e4bebf8 | 215 | comm_read(conn, buf, amountToRead, callback); |
a46d2c0e | 216 | } |
217 | ||
218 | size_t | |
384a7590 | 219 | StoreEntry::bytesWanted (Range<size_t> const aRange, bool ignoreDelayPools) const |
528b2c61 | 220 | { |
528b2c61 | 221 | if (mem_obj == NULL) |
4dc2b072 | 222 | return aRange.end; |
62e76326 | 223 | |
bc87dc25 | 224 | #if URL_CHECKSUM_DEBUG |
62e76326 | 225 | |
528b2c61 | 226 | mem_obj->checkUrlChecksum(); |
62e76326 | 227 | |
528b2c61 | 228 | #endif |
62e76326 | 229 | |
a46d2c0e | 230 | if (!mem_obj->readAheadPolicyCanRead()) |
231 | return 0; | |
62e76326 | 232 | |
384a7590 | 233 | return mem_obj->mostBytesWanted(aRange.end, ignoreDelayPools); |
a46d2c0e | 234 | } |
62e76326 | 235 | |
a46d2c0e | 236 | bool |
ced8def3 | 237 | StoreEntry::checkDeferRead(int) const |
a46d2c0e | 238 | { |
239 | return (bytesWanted(Range<size_t>(0,INT_MAX)) == 0); | |
240 | } | |
62e76326 | 241 | |
a46d2c0e | 242 | void |
ced8def3 | 243 | StoreEntry::setNoDelay(bool const newValue) |
a46d2c0e | 244 | { |
245 | if (mem_obj) | |
246 | mem_obj->setNoDelay(newValue); | |
528b2c61 | 247 | } |
bc87dc25 | 248 | |
f25d697f AR |
249 | // XXX: Type names mislead. STORE_DISK_CLIENT actually means that we should |
250 | // open swapin file, aggressively trim memory, and ignore read-ahead gap. | |
251 | // It does not mean we will read from disk exclusively (or at all!). | |
252 | // XXX: May create STORE_DISK_CLIENT with no disk caching configured. | |
253 | // XXX: Collapsed clients cannot predict their type. | |
528b2c61 | 254 | store_client_t |
255 | StoreEntry::storeClientType() const | |
227fbb74 | 256 | { |
7d31d5fa | 257 | /* The needed offset isn't in memory |
258 | * XXX TODO: this is wrong for range requests | |
259 | * as the needed offset may *not* be 0, AND | |
260 | * offset 0 in the memory object is the HTTP headers. | |
261 | */ | |
262 | ||
9487bae9 AR |
263 | assert(mem_obj); |
264 | ||
528b2c61 | 265 | if (mem_obj->inmem_lo) |
62e76326 | 266 | return STORE_DISK_CLIENT; |
267 | ||
528b2c61 | 268 | if (EBIT_TEST(flags, ENTRY_ABORTED)) { |
62e76326 | 269 | /* I don't think we should be adding clients to aborted entries */ |
e0236918 | 270 | debugs(20, DBG_IMPORTANT, "storeClientType: adding to ENTRY_ABORTED entry"); |
62e76326 | 271 | return STORE_MEM_CLIENT; |
528b2c61 | 272 | } |
62e76326 | 273 | |
528b2c61 | 274 | if (store_status == STORE_OK) { |
7d31d5fa | 275 | /* the object has completed. */ |
276 | ||
344a9006 | 277 | if (mem_obj->inmem_lo == 0 && !isEmpty()) { |
47ce0a58 | 278 | if (swap_status == SWAPOUT_DONE) { |
e2851fe7 | 279 | debugs(20,7, HERE << mem_obj << " lo: " << mem_obj->inmem_lo << " hi: " << mem_obj->endOffset() << " size: " << mem_obj->object_sz); |
e1381638 | 280 | if (mem_obj->endOffset() == mem_obj->object_sz) { |
f25d697f | 281 | /* hot object fully swapped in (XXX: or swapped out?) */ |
e1381638 AJ |
282 | return STORE_MEM_CLIENT; |
283 | } | |
47ce0a58 | 284 | } else { |
e1381638 AJ |
285 | /* Memory-only, or currently being swapped out */ |
286 | return STORE_MEM_CLIENT; | |
47ce0a58 HN |
287 | } |
288 | } | |
289 | return STORE_DISK_CLIENT; | |
528b2c61 | 290 | } |
62e76326 | 291 | |
528b2c61 | 292 | /* here and past, entry is STORE_PENDING */ |
293 | /* | |
294 | * If this is the first client, let it be the mem client | |
295 | */ | |
296 | if (mem_obj->nclients == 1) | |
62e76326 | 297 | return STORE_MEM_CLIENT; |
298 | ||
528b2c61 | 299 | /* |
300 | * If there is no disk file to open yet, we must make this a | |
301 | * mem client. If we can't open the swapin file before writing | |
302 | * to the client, there is no guarantee that we will be able | |
303 | * to open it later when we really need it. | |
304 | */ | |
305 | if (swap_status == SWAPOUT_NONE) | |
62e76326 | 306 | return STORE_MEM_CLIENT; |
307 | ||
528b2c61 | 308 | /* |
309 | * otherwise, make subsequent clients read from disk so they | |
310 | * can not delay the first, and vice-versa. | |
311 | */ | |
312 | return STORE_DISK_CLIENT; | |
227fbb74 | 313 | } |
314 | ||
8ebae981 | 315 | StoreEntry::StoreEntry() : |
f53969cc SM |
316 | mem_obj(NULL), |
317 | timestamp(-1), | |
318 | lastref(-1), | |
319 | expires(-1), | |
438b41ba | 320 | lastModified_(-1), |
f53969cc SM |
321 | swap_file_sz(0), |
322 | refcount(0), | |
323 | flags(0), | |
324 | swap_filen(-1), | |
325 | swap_dirn(-1), | |
326 | mem_status(NOT_IN_MEMORY), | |
327 | ping_status(PING_NONE), | |
328 | store_status(STORE_PENDING), | |
329 | swap_status(SWAPOUT_NONE), | |
330 | lock_count(0) | |
090089c4 | 331 | { |
539283df | 332 | debugs(20, 5, "StoreEntry constructed, this=" << this); |
c8f4eac4 | 333 | } |
62e76326 | 334 | |
6d8d05b5 DK |
335 | StoreEntry::~StoreEntry() |
336 | { | |
539283df | 337 | debugs(20, 5, "StoreEntry destructed, this=" << this); |
6d8d05b5 DK |
338 | } |
339 | ||
0ad2b63b CT |
340 | #if USE_ADAPTATION |
341 | void | |
342 | StoreEntry::deferProducer(const AsyncCall::Pointer &producer) | |
343 | { | |
344 | if (!deferredProducer) | |
345 | deferredProducer = producer; | |
346 | else | |
e83cdc25 | 347 | debugs(20, 5, HERE << "Deferred producer call is allready set to: " << |
0ad2b63b CT |
348 | *deferredProducer << ", requested call: " << *producer); |
349 | } | |
350 | ||
351 | void | |
352 | StoreEntry::kickProducer() | |
353 | { | |
e83cdc25 | 354 | if (deferredProducer != NULL) { |
0ad2b63b CT |
355 | ScheduleCallHere(deferredProducer); |
356 | deferredProducer = NULL; | |
357 | } | |
358 | } | |
359 | #endif | |
360 | ||
3900307b | 361 | void |
362 | StoreEntry::destroyMemObject() | |
090089c4 | 363 | { |
3900307b | 364 | debugs(20, 3, HERE << "destroyMemObject " << mem_obj); |
29c56e41 AR |
365 | |
366 | if (MemObject *mem = mem_obj) { | |
367 | // Store::Root() is FATALly missing during shutdown | |
368 | if (mem->xitTable.index >= 0 && !shutting_down) | |
369 | Store::Root().transientsDisconnect(*mem); | |
370 | if (mem->memCache.index >= 0 && !shutting_down) | |
371 | Store::Root().memoryDisconnect(*this); | |
372 | ||
373 | setMemStatus(NOT_IN_MEMORY); | |
374 | mem_obj = NULL; | |
375 | delete mem; | |
376 | } | |
090089c4 | 377 | } |
378 | ||
c8f4eac4 | 379 | void |
528b2c61 | 380 | destroyStoreEntry(void *data) |
090089c4 | 381 | { |
59dbbc5c | 382 | debugs(20, 3, HERE << "destroyStoreEntry: destroying " << data); |
c8f4eac4 | 383 | StoreEntry *e = static_cast<StoreEntry *>(static_cast<hash_link *>(data)); |
9e975e4e | 384 | assert(e != NULL); |
62e76326 | 385 | |
e6ccf245 | 386 | if (e == NullStoreEntry::getInstance()) |
62e76326 | 387 | return; |
388 | ||
fa6d2c65 | 389 | // Store::Root() is FATALly missing during shutdown |
2745fea5 AR |
390 | if (e->swap_filen >= 0 && !shutting_down) |
391 | e->disk().disconnect(*e); | |
fa6d2c65 | 392 | |
3900307b | 393 | e->destroyMemObject(); |
62e76326 | 394 | |
3900307b | 395 | e->hashDelete(); |
62e76326 | 396 | |
332dafa2 | 397 | assert(e->key == NULL); |
62e76326 | 398 | |
e6ccf245 | 399 | delete e; |
227fbb74 | 400 | } |
090089c4 | 401 | |
090089c4 | 402 | /* ----- INTERFACE BETWEEN STORAGE MANAGER AND HASH TABLE FUNCTIONS --------- */ |
403 | ||
f09f5b26 | 404 | void |
3900307b | 405 | StoreEntry::hashInsert(const cache_key * someKey) |
090089c4 | 406 | { |
4475555f | 407 | debugs(20, 3, "StoreEntry::hashInsert: Inserting Entry " << *this << " key '" << storeKeyText(someKey) << "'"); |
3900307b | 408 | key = storeKeyDup(someKey); |
409 | hash_join(store_table, this); | |
090089c4 | 410 | } |
411 | ||
3900307b | 412 | void |
413 | StoreEntry::hashDelete() | |
090089c4 | 414 | { |
cb868059 AR |
415 | if (key) { // some test cases do not create keys and do not hashInsert() |
416 | hash_remove_link(store_table, this); | |
417 | storeKeyFree((const cache_key *)key); | |
418 | key = NULL; | |
419 | } | |
090089c4 | 420 | } |
421 | ||
090089c4 | 422 | /* -------------------------------------------------------------------------- */ |
423 | ||
090089c4 | 424 | /* get rid of memory copy of the object */ |
d88e3c49 | 425 | void |
426 | StoreEntry::purgeMem() | |
090089c4 | 427 | { |
d88e3c49 | 428 | if (mem_obj == NULL) |
62e76326 | 429 | return; |
430 | ||
bf8fe701 | 431 | debugs(20, 3, "StoreEntry::purgeMem: Freeing memory-copy of " << getMD5Text()); |
62e76326 | 432 | |
ce49546e | 433 | Store::Root().memoryUnlink(*this); |
62e76326 | 434 | |
d88e3c49 | 435 | if (swap_status != SWAPOUT_DONE) |
436 | release(); | |
090089c4 | 437 | } |
438 | ||
6a566b9c | 439 | void |
18994992 | 440 | StoreEntry::lock(const char *context) |
6a566b9c | 441 | { |
5db6bf73 | 442 | ++lock_count; |
18994992 AR |
443 | debugs(20, 3, context << " locked key " << getMD5Text() << ' ' << *this); |
444 | } | |
445 | ||
446 | void | |
9d4e9cfb AR |
447 | StoreEntry::touch() |
448 | { | |
c21ad0f5 | 449 | lastref = squid_curtime; |
090089c4 | 450 | } |
451 | ||
43ae1d95 | 452 | void |
453 | StoreEntry::setReleaseFlag() | |
454 | { | |
455 | if (EBIT_TEST(flags, RELEASE_REQUEST)) | |
456 | return; | |
457 | ||
bf8fe701 | 458 | debugs(20, 3, "StoreEntry::setReleaseFlag: '" << getMD5Text() << "'"); |
43ae1d95 | 459 | |
460 | EBIT_SET(flags, RELEASE_REQUEST); | |
1bfe9ade AR |
461 | |
462 | Store::Root().markForUnlink(*this); | |
43ae1d95 | 463 | } |
464 | ||
b8d8561b | 465 | void |
d88e3c49 | 466 | StoreEntry::releaseRequest() |
2285407f | 467 | { |
d88e3c49 | 468 | if (EBIT_TEST(flags, RELEASE_REQUEST)) |
62e76326 | 469 | return; |
470 | ||
6919be24 | 471 | setReleaseFlag(); // makes validToSend() false, preventing future hits |
62e76326 | 472 | |
d88e3c49 | 473 | setPrivateKey(); |
2285407f | 474 | } |
475 | ||
b8d8561b | 476 | int |
18994992 | 477 | StoreEntry::unlock(const char *context) |
090089c4 | 478 | { |
18994992 AR |
479 | debugs(20, 3, (context ? context : "somebody") << |
480 | " unlocking key " << getMD5Text() << ' ' << *this); | |
c47b98ac | 481 | assert(lock_count > 0); |
5e263176 | 482 | --lock_count; |
62e76326 | 483 | |
c21ad0f5 | 484 | if (lock_count) |
485 | return (int) lock_count; | |
62e76326 | 486 | |
c21ad0f5 | 487 | if (store_status == STORE_PENDING) |
488 | setReleaseFlag(); | |
62e76326 | 489 | |
c21ad0f5 | 490 | assert(storePendingNClients(this) == 0); |
62e76326 | 491 | |
9199139f | 492 | if (EBIT_TEST(flags, RELEASE_REQUEST)) { |
5f33b71d | 493 | this->release(); |
984f9874 AR |
494 | return 0; |
495 | } | |
496 | ||
9487bae9 | 497 | if (EBIT_TEST(flags, KEY_PRIVATE)) |
e0236918 | 498 | debugs(20, DBG_IMPORTANT, "WARNING: " << __FILE__ << ":" << __LINE__ << ": found KEY_PRIVATE"); |
62e76326 | 499 | |
9487bae9 | 500 | Store::Root().handleIdleEntry(*this); // may delete us |
6c895381 | 501 | return 0; |
090089c4 | 502 | } |
503 | ||
e6ccf245 | 504 | void |
60745f24 | 505 | StoreEntry::getPublicByRequestMethod (StoreClient *aClient, HttpRequest * request, const HttpRequestMethod& method) |
e6ccf245 | 506 | { |
507 | assert (aClient); | |
3b13a8fd | 508 | StoreEntry *result = storeGetPublicByRequestMethod( request, method); |
62e76326 | 509 | |
e6ccf245 | 510 | if (!result) |
62e76326 | 511 | aClient->created (NullStoreEntry::getInstance()); |
e6ccf245 | 512 | else |
62e76326 | 513 | aClient->created (result); |
e6ccf245 | 514 | } |
515 | ||
516 | void | |
190154cf | 517 | StoreEntry::getPublicByRequest (StoreClient *aClient, HttpRequest * request) |
e6ccf245 | 518 | { |
519 | assert (aClient); | |
3b13a8fd | 520 | StoreEntry *result = storeGetPublicByRequest (request); |
62e76326 | 521 | |
e6ccf245 | 522 | if (!result) |
62e76326 | 523 | result = NullStoreEntry::getInstance(); |
524 | ||
e6ccf245 | 525 | aClient->created (result); |
526 | } | |
527 | ||
528 | void | |
60745f24 | 529 | StoreEntry::getPublic (StoreClient *aClient, const char *uri, const HttpRequestMethod& method) |
e6ccf245 | 530 | { |
531 | assert (aClient); | |
3b13a8fd | 532 | StoreEntry *result = storeGetPublic (uri, method); |
62e76326 | 533 | |
e6ccf245 | 534 | if (!result) |
62e76326 | 535 | result = NullStoreEntry::getInstance(); |
536 | ||
e6ccf245 | 537 | aClient->created (result); |
538 | } | |
539 | ||
08e5d64f | 540 | StoreEntry * |
60745f24 | 541 | storeGetPublic(const char *uri, const HttpRequestMethod& method) |
08e5d64f | 542 | { |
c8f4eac4 | 543 | return Store::Root().get(storeKeyPublic(uri, method)); |
08e5d64f | 544 | } |
545 | ||
f66a9ef4 | 546 | StoreEntry * |
1a210de4 | 547 | storeGetPublicByRequestMethod(HttpRequest * req, const HttpRequestMethod& method, const KeyScope keyScope) |
f66a9ef4 | 548 | { |
1a210de4 | 549 | return Store::Root().get(storeKeyPublicByRequestMethod(req, method, keyScope)); |
f66a9ef4 | 550 | } |
551 | ||
552 | StoreEntry * | |
1a210de4 | 553 | storeGetPublicByRequest(HttpRequest * req, const KeyScope keyScope) |
f66a9ef4 | 554 | { |
1a210de4 | 555 | StoreEntry *e = storeGetPublicByRequestMethod(req, req->method, keyScope); |
62e76326 | 556 | |
c2a7cefd | 557 | if (e == NULL && req->method == Http::METHOD_HEAD) |
62e76326 | 558 | /* We can generate a HEAD reply from a cached GET object */ |
1a210de4 | 559 | e = storeGetPublicByRequestMethod(req, Http::METHOD_GET, keyScope); |
62e76326 | 560 | |
f66a9ef4 | 561 | return e; |
562 | } | |
563 | ||
007b8be4 | 564 | static int |
565 | getKeyCounter(void) | |
04e8dbaa | 566 | { |
007b8be4 | 567 | static int key_counter = 0; |
62e76326 | 568 | |
007b8be4 | 569 | if (++key_counter < 0) |
62e76326 | 570 | key_counter = 1; |
571 | ||
007b8be4 | 572 | return key_counter; |
04e8dbaa | 573 | } |
574 | ||
c8f4eac4 | 575 | /* RBC 20050104 AFAICT this should become simpler: |
576 | * rather than reinserting with a special key it should be marked | |
577 | * as 'released' and then cleaned up when refcounting indicates. | |
578 | * the StoreHashIndex could well implement its 'released' in the | |
579 | * current manner. | |
580 | * Also, clean log writing should skip over ia,t | |
581 | * Otherwise, we need a 'remove from the index but not the store | |
582 | * concept'. | |
583 | */ | |
6c57e268 | 584 | void |
d88e3c49 | 585 | StoreEntry::setPrivateKey() |
227fbb74 | 586 | { |
d88e3c49 | 587 | if (key && EBIT_TEST(flags, KEY_PRIVATE)) |
c21ad0f5 | 588 | return; /* is already private */ |
62e76326 | 589 | |
d88e3c49 | 590 | if (key) { |
6919be24 AR |
591 | setReleaseFlag(); // will markForUnlink(); all caches/workers will know |
592 | ||
593 | // TODO: move into SwapDir::markForUnlink() already called by Root() | |
d88e3c49 | 594 | if (swap_filen > -1) |
595 | storeDirSwapLog(this, SWAP_LOG_DEL); | |
62e76326 | 596 | |
3900307b | 597 | hashDelete(); |
b109de6b | 598 | } |
62e76326 | 599 | |
0a132302 | 600 | if (mem_obj && mem_obj->hasUris()) |
d88e3c49 | 601 | mem_obj->id = getKeyCounter(); |
0a132302 | 602 | const cache_key *newkey = storeKeyPrivate(); |
62e76326 | 603 | |
9fb13bb6 | 604 | assert(hash_lookup(store_table, newkey) == NULL); |
d88e3c49 | 605 | EBIT_SET(flags, KEY_PRIVATE); |
3900307b | 606 | hashInsert(newkey); |
227fbb74 | 607 | } |
608 | ||
b8d8561b | 609 | void |
1a210de4 | 610 | StoreEntry::setPublicKey(const KeyScope scope) |
227fbb74 | 611 | { |
d88e3c49 | 612 | if (key && !EBIT_TEST(flags, KEY_PRIVATE)) |
c21ad0f5 | 613 | return; /* is already public */ |
62e76326 | 614 | |
d88e3c49 | 615 | assert(mem_obj); |
62e76326 | 616 | |
f3e570e9 | 617 | /* |
618 | * We can't make RELEASE_REQUEST objects public. Depending on | |
619 | * when RELEASE_REQUEST gets set, we might not be swapping out | |
620 | * the object. If we're not swapping out, then subsequent | |
621 | * store clients won't be able to access object data which has | |
622 | * been freed from memory. | |
d87ebd78 | 623 | * |
6919be24 | 624 | * If RELEASE_REQUEST is set, setPublicKey() should not be called. |
f3e570e9 | 625 | */ |
6a566b9c | 626 | #if MORE_DEBUG_OUTPUT |
62e76326 | 627 | |
d88e3c49 | 628 | if (EBIT_TEST(flags, RELEASE_REQUEST)) |
e0236918 | 629 | debugs(20, DBG_IMPORTANT, "assertion failed: RELEASE key " << key << ", url " << mem_obj->url); |
62e76326 | 630 | |
2b906e48 | 631 | #endif |
62e76326 | 632 | |
d88e3c49 | 633 | assert(!EBIT_TEST(flags, RELEASE_REQUEST)); |
62e76326 | 634 | |
1a210de4 EB |
635 | adjustVary(); |
636 | forcePublicKey(calcPublicKey(scope)); | |
637 | } | |
62e76326 | 638 | |
1a210de4 EB |
639 | void |
640 | StoreEntry::clearPublicKeyScope() | |
641 | { | |
642 | if (!key || EBIT_TEST(flags, KEY_PRIVATE)) | |
643 | return; // probably the old public key was deleted or made private | |
b8a899c0 | 644 | |
1a210de4 | 645 | // TODO: adjustVary() when collapsed revalidation supports that |
eab8dcfa | 646 | |
1a210de4 EB |
647 | const cache_key *newKey = calcPublicKey(ksDefault); |
648 | if (!storeKeyHashCmp(key, newKey)) | |
649 | return; // probably another collapsed revalidation beat us to this change | |
62e76326 | 650 | |
1a210de4 EB |
651 | forcePublicKey(newKey); |
652 | } | |
62e76326 | 653 | |
1a210de4 EB |
654 | /// Unconditionally sets public key for this store entry. |
655 | /// Releases the old entry with the same public key (if any). | |
656 | void | |
657 | StoreEntry::forcePublicKey(const cache_key *newkey) | |
658 | { | |
c877c0bc | 659 | if (StoreEntry *e2 = (StoreEntry *)hash_lookup(store_table, newkey)) { |
1a210de4 | 660 | assert(e2 != this); |
c877c0bc | 661 | debugs(20, 3, "Making old " << *e2 << " private."); |
d88e3c49 | 662 | e2->setPrivateKey(); |
5f33b71d | 663 | e2->release(); |
6eb42cae | 664 | } |
62e76326 | 665 | |
d88e3c49 | 666 | if (key) |
3900307b | 667 | hashDelete(); |
62e76326 | 668 | |
d88e3c49 | 669 | EBIT_CLR(flags, KEY_PRIVATE); |
62e76326 | 670 | |
3900307b | 671 | hashInsert(newkey); |
62e76326 | 672 | |
d88e3c49 | 673 | if (swap_filen > -1) |
674 | storeDirSwapLog(this, SWAP_LOG_ADD); | |
227fbb74 | 675 | } |
676 | ||
1a210de4 EB |
677 | /// Calculates correct public key for feeding forcePublicKey(). |
678 | /// Assumes adjustVary() has been called for this entry already. | |
679 | const cache_key * | |
680 | StoreEntry::calcPublicKey(const KeyScope keyScope) | |
681 | { | |
682 | assert(mem_obj); | |
c43405e7 | 683 | return mem_obj->request ? storeKeyPublicByRequest(mem_obj->request.getRaw(), keyScope) : |
1a210de4 EB |
684 | storeKeyPublic(mem_obj->storeId(), mem_obj->method, keyScope); |
685 | } | |
686 | ||
687 | /// Updates mem_obj->request->vary_headers to reflect the current Vary. | |
688 | /// The vary_headers field is used to calculate the Vary marker key. | |
689 | /// Releases the old Vary marker with an outdated key (if any). | |
690 | void | |
691 | StoreEntry::adjustVary() | |
692 | { | |
693 | assert(mem_obj); | |
694 | ||
695 | if (!mem_obj->request) | |
696 | return; | |
697 | ||
c43405e7 | 698 | HttpRequestPointer request(mem_obj->request); |
1a210de4 EB |
699 | |
700 | if (mem_obj->vary_headers.isEmpty()) { | |
701 | /* First handle the case where the object no longer varies */ | |
702 | request->vary_headers.clear(); | |
703 | } else { | |
704 | if (!request->vary_headers.isEmpty() && request->vary_headers.cmp(mem_obj->vary_headers) != 0) { | |
705 | /* Oops.. the variance has changed. Kill the base object | |
706 | * to record the new variance key | |
707 | */ | |
708 | request->vary_headers.clear(); /* free old "bad" variance key */ | |
709 | if (StoreEntry *pe = storeGetPublic(mem_obj->storeId(), mem_obj->method)) | |
710 | pe->release(); | |
711 | } | |
712 | ||
713 | /* Make sure the request knows the variance status */ | |
714 | if (request->vary_headers.isEmpty()) | |
a0c227a9 | 715 | request->vary_headers = httpMakeVaryMark(request.getRaw(), mem_obj->getReply().getRaw()); |
1a210de4 EB |
716 | } |
717 | ||
718 | // TODO: storeGetPublic() calls below may create unlocked entries. | |
719 | // We should add/use storeHas() API or lock/unlock those entries. | |
720 | if (!mem_obj->vary_headers.isEmpty() && !storeGetPublic(mem_obj->storeId(), mem_obj->method)) { | |
721 | /* Create "vary" base object */ | |
722 | String vary; | |
723 | StoreEntry *pe = storeCreateEntry(mem_obj->storeId(), mem_obj->logUri(), request->flags, request->method); | |
724 | /* We are allowed to do this typecast */ | |
725 | HttpReply *rep = new HttpReply; | |
726 | rep->setHeaders(Http::scOkay, "Internal marker object", "x-squid-internal/vary", -1, -1, squid_curtime + 100000); | |
727 | vary = mem_obj->getReply()->header.getList(Http::HdrType::VARY); | |
728 | ||
729 | if (vary.size()) { | |
730 | /* Again, we own this structure layout */ | |
731 | rep->header.putStr(Http::HdrType::VARY, vary.termedBuf()); | |
732 | vary.clean(); | |
733 | } | |
734 | ||
735 | #if X_ACCELERATOR_VARY | |
736 | vary = mem_obj->getReply()->header.getList(Http::HdrType::HDR_X_ACCELERATOR_VARY); | |
737 | ||
738 | if (vary.size() > 0) { | |
739 | /* Again, we own this structure layout */ | |
740 | rep->header.putStr(Http::HdrType::HDR_X_ACCELERATOR_VARY, vary.termedBuf()); | |
741 | vary.clean(); | |
742 | } | |
743 | ||
744 | #endif | |
745 | pe->replaceHttpReply(rep, false); // no write until key is public | |
746 | ||
747 | pe->timestampsSet(); | |
748 | ||
749 | pe->makePublic(); | |
750 | ||
751 | pe->startWriting(); // after makePublic() | |
752 | ||
753 | pe->complete(); | |
754 | ||
755 | pe->unlock("StoreEntry::forcePublicKey+Vary"); | |
756 | } | |
757 | } | |
758 | ||
b8d8561b | 759 | StoreEntry * |
1bfe9ade | 760 | storeCreatePureEntry(const char *url, const char *log_url, const RequestFlags &flags, const HttpRequestMethod& method) |
090089c4 | 761 | { |
090089c4 | 762 | StoreEntry *e = NULL; |
bf8fe701 | 763 | debugs(20, 3, "storeCreateEntry: '" << url << "'"); |
090089c4 | 764 | |
c877c0bc | 765 | e = new StoreEntry(); |
c877c0bc AR |
766 | e->makeMemObject(); |
767 | e->mem_obj->setUris(url, log_url, method); | |
62e76326 | 768 | |
45e5102d | 769 | if (flags.cachable) { |
62e76326 | 770 | EBIT_CLR(e->flags, RELEASE_REQUEST); |
090089c4 | 771 | } else { |
d88e3c49 | 772 | e->releaseRequest(); |
090089c4 | 773 | } |
62e76326 | 774 | |
234967c9 | 775 | e->store_status = STORE_PENDING; |
090089c4 | 776 | e->refcount = 0; |
b8de7ebe | 777 | e->lastref = squid_curtime; |
3900307b | 778 | e->timestamp = -1; /* set in StoreEntry::timestampsSet() */ |
30a4f2a8 | 779 | e->ping_status = PING_NONE; |
d46a87a8 | 780 | EBIT_SET(e->flags, ENTRY_VALIDATED); |
090089c4 | 781 | return e; |
782 | } | |
783 | ||
1bfe9ade AR |
784 | StoreEntry * |
785 | storeCreateEntry(const char *url, const char *logUrl, const RequestFlags &flags, const HttpRequestMethod& method) | |
786 | { | |
787 | StoreEntry *e = storeCreatePureEntry(url, logUrl, flags, method); | |
788 | e->lock("storeCreateEntry"); | |
789 | ||
790 | if (neighbors_do_private_keys || !flags.hierarchical) | |
791 | e->setPrivateKey(); | |
792 | else | |
793 | e->setPublicKey(); | |
794 | ||
795 | return e; | |
796 | } | |
797 | ||
6eb42cae | 798 | /* Mark object as expired */ |
b8d8561b | 799 | void |
d88e3c49 | 800 | StoreEntry::expireNow() |
9174e204 | 801 | { |
bf8fe701 | 802 | debugs(20, 3, "StoreEntry::expireNow: '" << getMD5Text() << "'"); |
d88e3c49 | 803 | expires = squid_curtime; |
9174e204 | 804 | } |
805 | ||
528b2c61 | 806 | void |
807 | StoreEntry::write (StoreIOBuffer writeBuffer) | |
808 | { | |
809 | assert(mem_obj != NULL); | |
528b2c61 | 810 | /* This assert will change when we teach the store to update */ |
d2639a5b | 811 | PROF_start(StoreEntry_write); |
528b2c61 | 812 | assert(store_status == STORE_PENDING); |
62e76326 | 813 | |
55759ffb | 814 | // XXX: caller uses content offset, but we also store headers |
a0c227a9 | 815 | if (const HttpReplyPointer reply = mem_obj->getReply()) |
55759ffb AR |
816 | writeBuffer.offset += reply->hdr_sz; |
817 | ||
d2639a5b | 818 | debugs(20, 5, "storeWrite: writing " << writeBuffer.length << " bytes for '" << getMD5Text() << "'"); |
819 | PROF_stop(StoreEntry_write); | |
528b2c61 | 820 | storeGetMemSpace(writeBuffer.length); |
55759ffb AR |
821 | mem_obj->write(writeBuffer); |
822 | ||
823 | if (!EBIT_TEST(flags, DELAY_SENDING)) | |
824 | invokeHandlers(); | |
528b2c61 | 825 | } |
826 | ||
c21ad0f5 | 827 | /* Append incoming data from a primary server to an entry. */ |
828 | void | |
829 | StoreEntry::append(char const *buf, int len) | |
830 | { | |
831 | assert(mem_obj != NULL); | |
3a1c3e2f | 832 | assert(len >= 0); |
c21ad0f5 | 833 | assert(store_status == STORE_PENDING); |
528b2c61 | 834 | |
835 | StoreIOBuffer tempBuffer; | |
836 | tempBuffer.data = (char *)buf; | |
837 | tempBuffer.length = len; | |
aa18a4ca | 838 | /* |
839 | * XXX sigh, offset might be < 0 here, but it gets "corrected" | |
840 | * later. This offset crap is such a mess. | |
841 | */ | |
c21ad0f5 | 842 | tempBuffer.offset = mem_obj->endOffset() - (getReply() ? getReply()->hdr_sz : 0); |
843 | write(tempBuffer); | |
090089c4 | 844 | } |
845 | ||
0f33a01d AJ |
846 | void |
847 | StoreEntry::vappendf(const char *fmt, va_list vargs) | |
848 | { | |
849 | LOCAL_ARRAY(char, buf, 4096); | |
850 | *buf = 0; | |
851 | int x; | |
852 | ||
bd746807 AJ |
853 | #ifdef VA_COPY |
854 | va_args ap; | |
855 | /* Fix of bug 753r. The value of vargs is undefined | |
856 | * after vsnprintf() returns. Make a copy of vargs | |
857 | * incase we loop around and call vsnprintf() again. | |
858 | */ | |
859 | VA_COPY(ap,vargs); | |
860 | errno = 0; | |
861 | if ((x = vsnprintf(buf, sizeof(buf), fmt, ap)) < 0) { | |
1fab8344 | 862 | fatal(xstrerr(errno)); |
bd746807 AJ |
863 | return; |
864 | } | |
865 | va_end(ap); | |
866 | #else /* VA_COPY */ | |
0f33a01d AJ |
867 | errno = 0; |
868 | if ((x = vsnprintf(buf, sizeof(buf), fmt, vargs)) < 0) { | |
1fab8344 | 869 | fatal(xstrerr(errno)); |
0f33a01d AJ |
870 | return; |
871 | } | |
bd746807 | 872 | #endif /*VA_COPY*/ |
0f33a01d | 873 | |
1fab8344 | 874 | if (x < static_cast<int>(sizeof(buf))) { |
0f33a01d AJ |
875 | append(buf, x); |
876 | return; | |
877 | } | |
878 | ||
879 | // okay, do it the slow way. | |
880 | char *buf2 = new char[x+1]; | |
881 | int y = vsnprintf(buf2, x+1, fmt, vargs); | |
882 | assert(y >= 0 && y == x); | |
883 | append(buf2, y); | |
884 | delete[] buf2; | |
885 | } | |
886 | ||
887 | // deprecated. use StoreEntry::appendf() instead. | |
b8d8561b | 888 | void |
fe4e214f | 889 | storeAppendPrintf(StoreEntry * e, const char *fmt,...) |
15c05bb0 | 890 | { |
62d32805 | 891 | va_list args; |
892 | va_start(args, fmt); | |
0f33a01d | 893 | e->vappendf(fmt, args); |
cb69b4c7 | 894 | va_end(args); |
895 | } | |
896 | ||
0f33a01d | 897 | // deprecated. use StoreEntry::appendf() instead. |
cb69b4c7 | 898 | void |
899 | storeAppendVPrintf(StoreEntry * e, const char *fmt, va_list vargs) | |
900 | { | |
0f33a01d | 901 | e->vappendf(fmt, vargs); |
c30c5a73 | 902 | } |
903 | ||
26ac0430 | 904 | struct _store_check_cachable_hist { |
62e76326 | 905 | |
26ac0430 | 906 | struct { |
62e76326 | 907 | int non_get; |
908 | int not_entry_cachable; | |
909 | int wrong_content_length; | |
910 | int negative_cached; | |
911 | int too_big; | |
912 | int too_small; | |
913 | int private_key; | |
914 | int too_many_open_files; | |
915 | int too_many_open_fds; | |
d48fa91c | 916 | int missing_parts; |
2fadd50d | 917 | } no; |
62e76326 | 918 | |
26ac0430 | 919 | struct { |
62e76326 | 920 | int Default; |
2fadd50d HN |
921 | } yes; |
922 | } store_check_cachable_hist; | |
8423ff74 | 923 | |
c47511fd | 924 | int |
925 | storeTooManyDiskFilesOpen(void) | |
926 | { | |
927 | if (Config.max_open_disk_fds == 0) | |
62e76326 | 928 | return 0; |
929 | ||
83a29c95 | 930 | if (store_open_disk_fd > Config.max_open_disk_fds) |
62e76326 | 931 | return 1; |
932 | ||
c47511fd | 933 | return 0; |
934 | } | |
935 | ||
3900307b | 936 | int |
937 | StoreEntry::checkTooSmall() | |
d20b1cd0 | 938 | { |
3900307b | 939 | if (EBIT_TEST(flags, ENTRY_SPECIAL)) |
62e76326 | 940 | return 0; |
941 | ||
3900307b | 942 | if (STORE_OK == store_status) |
41afe8b2 | 943 | if (mem_obj->object_sz >= 0 && |
26ac0430 | 944 | mem_obj->object_sz < Config.Store.minObjectSize) |
62e76326 | 945 | return 1; |
3900307b | 946 | if (getReply()->content_length > -1) |
47f6e231 | 947 | if (getReply()->content_length < Config.Store.minObjectSize) |
62e76326 | 948 | return 1; |
d20b1cd0 | 949 | return 0; |
950 | } | |
951 | ||
2be042f6 AR |
952 | bool |
953 | StoreEntry::checkTooBig() const | |
954 | { | |
955 | if (mem_obj->endOffset() > store_maxobjsize) | |
956 | return true; | |
957 | ||
958 | if (getReply()->content_length < 0) | |
959 | return false; | |
960 | ||
961 | return (getReply()->content_length > store_maxobjsize); | |
962 | } | |
963 | ||
ddc9b32c | 964 | // TODO: move "too many open..." checks outside -- we are called too early/late |
7015a149 | 965 | bool |
3900307b | 966 | StoreEntry::checkCachable() |
6602e70e | 967 | { |
97754f5a AR |
968 | // XXX: This method is used for both memory and disk caches, but some |
969 | // checks are specific to disk caches. Move them to mayStartSwapOut(). | |
970 | ||
971 | // XXX: This method may be called several times, sometimes with different | |
972 | // outcomes, making store_check_cachable_hist counters misleading. | |
973 | ||
974 | // check this first to optimize handling of repeated calls for uncachables | |
975 | if (EBIT_TEST(flags, RELEASE_REQUEST)) { | |
976 | debugs(20, 2, "StoreEntry::checkCachable: NO: not cachable"); | |
977 | ++store_check_cachable_hist.no.not_entry_cachable; // TODO: rename? | |
978 | return 0; // avoid rerequesting release below | |
979 | } | |
980 | ||
2ac237e2 | 981 | #if CACHE_ALL_METHODS |
62e76326 | 982 | |
c2a7cefd | 983 | if (mem_obj->method != Http::METHOD_GET) { |
bf8fe701 | 984 | debugs(20, 2, "StoreEntry::checkCachable: NO: non-GET method"); |
5db6bf73 | 985 | ++store_check_cachable_hist.no.non_get; |
2ac237e2 | 986 | } else |
987 | #endif | |
3900307b | 988 | if (store_status == STORE_OK && EBIT_TEST(flags, ENTRY_BAD_LENGTH)) { |
bf8fe701 | 989 | debugs(20, 2, "StoreEntry::checkCachable: NO: wrong content-length"); |
5db6bf73 | 990 | ++store_check_cachable_hist.no.wrong_content_length; |
3900307b | 991 | } else if (EBIT_TEST(flags, ENTRY_NEGCACHED)) { |
bf8fe701 | 992 | debugs(20, 3, "StoreEntry::checkCachable: NO: negative cached"); |
5db6bf73 | 993 | ++store_check_cachable_hist.no.negative_cached; |
c21ad0f5 | 994 | return 0; /* avoid release call below */ |
2be042f6 AR |
995 | } else if (!mem_obj || !getReply()) { |
996 | // XXX: In bug 4131, we forgetHit() without mem_obj, so we need | |
997 | // this segfault protection, but how can we get such a HIT? | |
2be042f6 | 998 | debugs(20, 2, "StoreEntry::checkCachable: NO: missing parts: " << *this); |
d48fa91c | 999 | ++store_check_cachable_hist.no.missing_parts; |
2be042f6 | 1000 | } else if (checkTooBig()) { |
bf8fe701 | 1001 | debugs(20, 2, "StoreEntry::checkCachable: NO: too big"); |
5db6bf73 | 1002 | ++store_check_cachable_hist.no.too_big; |
3900307b | 1003 | } else if (checkTooSmall()) { |
bf8fe701 | 1004 | debugs(20, 2, "StoreEntry::checkCachable: NO: too small"); |
5db6bf73 | 1005 | ++store_check_cachable_hist.no.too_small; |
3900307b | 1006 | } else if (EBIT_TEST(flags, KEY_PRIVATE)) { |
bf8fe701 | 1007 | debugs(20, 3, "StoreEntry::checkCachable: NO: private key"); |
5db6bf73 | 1008 | ++store_check_cachable_hist.no.private_key; |
3900307b | 1009 | } else if (swap_status != SWAPOUT_NONE) { |
62e76326 | 1010 | /* |
1011 | * here we checked the swap_status because the remaining | |
1012 | * cases are only relevant only if we haven't started swapping | |
1013 | * out the object yet. | |
1014 | */ | |
1015 | return 1; | |
1016 | } else if (storeTooManyDiskFilesOpen()) { | |
bf8fe701 | 1017 | debugs(20, 2, "StoreEntry::checkCachable: NO: too many disk files open"); |
5db6bf73 | 1018 | ++store_check_cachable_hist.no.too_many_open_files; |
62e76326 | 1019 | } else if (fdNFree() < RESERVED_FD) { |
bf8fe701 | 1020 | debugs(20, 2, "StoreEntry::checkCachable: NO: too many FD's open"); |
5db6bf73 | 1021 | ++store_check_cachable_hist.no.too_many_open_fds; |
62e76326 | 1022 | } else { |
5db6bf73 | 1023 | ++store_check_cachable_hist.yes.Default; |
62e76326 | 1024 | return 1; |
1025 | } | |
1026 | ||
3900307b | 1027 | releaseRequest(); |
6602e70e | 1028 | return 0; |
1029 | } | |
1030 | ||
3900307b | 1031 | void |
1032 | storeCheckCachableStats(StoreEntry *sentry) | |
8423ff74 | 1033 | { |
c40acff3 | 1034 | storeAppendPrintf(sentry, "Category\t Count\n"); |
1035 | ||
a0cd8f99 | 1036 | #if CACHE_ALL_METHODS |
62e76326 | 1037 | |
8423ff74 | 1038 | storeAppendPrintf(sentry, "no.non_get\t%d\n", |
62e76326 | 1039 | store_check_cachable_hist.no.non_get); |
a0cd8f99 | 1040 | #endif |
62e76326 | 1041 | |
8423ff74 | 1042 | storeAppendPrintf(sentry, "no.not_entry_cachable\t%d\n", |
62e76326 | 1043 | store_check_cachable_hist.no.not_entry_cachable); |
8423ff74 | 1044 | storeAppendPrintf(sentry, "no.wrong_content_length\t%d\n", |
62e76326 | 1045 | store_check_cachable_hist.no.wrong_content_length); |
8423ff74 | 1046 | storeAppendPrintf(sentry, "no.negative_cached\t%d\n", |
62e76326 | 1047 | store_check_cachable_hist.no.negative_cached); |
d48fa91c AJ |
1048 | storeAppendPrintf(sentry, "no.missing_parts\t%d\n", |
1049 | store_check_cachable_hist.no.missing_parts); | |
8423ff74 | 1050 | storeAppendPrintf(sentry, "no.too_big\t%d\n", |
62e76326 | 1051 | store_check_cachable_hist.no.too_big); |
d20b1cd0 | 1052 | storeAppendPrintf(sentry, "no.too_small\t%d\n", |
62e76326 | 1053 | store_check_cachable_hist.no.too_small); |
8423ff74 | 1054 | storeAppendPrintf(sentry, "no.private_key\t%d\n", |
62e76326 | 1055 | store_check_cachable_hist.no.private_key); |
c5f627c2 | 1056 | storeAppendPrintf(sentry, "no.too_many_open_files\t%d\n", |
62e76326 | 1057 | store_check_cachable_hist.no.too_many_open_files); |
59ffcdf8 | 1058 | storeAppendPrintf(sentry, "no.too_many_open_fds\t%d\n", |
62e76326 | 1059 | store_check_cachable_hist.no.too_many_open_fds); |
8423ff74 | 1060 | storeAppendPrintf(sentry, "yes.default\t%d\n", |
62e76326 | 1061 | store_check_cachable_hist.yes.Default); |
8423ff74 | 1062 | } |
1063 | ||
7224ca5a AR |
1064 | void |
1065 | StoreEntry::lengthWentBad(const char *reason) | |
1066 | { | |
1067 | debugs(20, 3, "because " << reason << ": " << *this); | |
1068 | EBIT_SET(flags, ENTRY_BAD_LENGTH); | |
1069 | releaseRequest(); | |
1070 | } | |
1071 | ||
b8d8561b | 1072 | void |
528b2c61 | 1073 | StoreEntry::complete() |
090089c4 | 1074 | { |
bf8fe701 | 1075 | debugs(20, 3, "storeComplete: '" << getMD5Text() << "'"); |
62e76326 | 1076 | |
528b2c61 | 1077 | if (store_status != STORE_PENDING) { |
62e76326 | 1078 | /* |
1079 | * if we're not STORE_PENDING, then probably we got aborted | |
1080 | * and there should be NO clients on this entry | |
1081 | */ | |
1082 | assert(EBIT_TEST(flags, ENTRY_ABORTED)); | |
1083 | assert(mem_obj->nclients == 0); | |
1084 | return; | |
b6403fac | 1085 | } |
62e76326 | 1086 | |
7d31d5fa | 1087 | /* This is suspect: mem obj offsets include the headers. do we adjust for that |
1088 | * in use of object_sz? | |
1089 | */ | |
528b2c61 | 1090 | mem_obj->object_sz = mem_obj->endOffset(); |
7d31d5fa | 1091 | |
528b2c61 | 1092 | store_status = STORE_OK; |
7d31d5fa | 1093 | |
528b2c61 | 1094 | assert(mem_status == NOT_IN_MEMORY); |
62e76326 | 1095 | |
7224ca5a AR |
1096 | if (!EBIT_TEST(flags, ENTRY_BAD_LENGTH) && !validLength()) |
1097 | lengthWentBad("!validLength() in complete()"); | |
62e76326 | 1098 | |
6cfa8966 | 1099 | #if USE_CACHE_DIGESTS |
528b2c61 | 1100 | if (mem_obj->request) |
62e76326 | 1101 | mem_obj->request->hier.store_complete_stop = current_time; |
1102 | ||
39edba21 | 1103 | #endif |
d20b1cd0 | 1104 | /* |
d88e3c49 | 1105 | * We used to call invokeHandlers, then storeSwapOut. However, |
d20b1cd0 | 1106 | * Madhukar Reddy <myreddy@persistence.com> reported that |
1107 | * responses without content length would sometimes get released | |
1108 | * in client_side, thinking that the response is incomplete. | |
1109 | */ | |
d88e3c49 | 1110 | invokeHandlers(); |
7e3e1d01 | 1111 | } |
1112 | ||
090089c4 | 1113 | /* |
474cac1b | 1114 | * Someone wants to abort this transfer. Set the reason in the |
d5430dc8 | 1115 | * request structure, call the callback and mark the |
2b906e48 | 1116 | * entry for releasing |
090089c4 | 1117 | */ |
b8d8561b | 1118 | void |
bfb55b6f | 1119 | StoreEntry::abort() |
090089c4 | 1120 | { |
5db6bf73 | 1121 | ++statCounter.aborted_requests; |
bfb55b6f | 1122 | assert(store_status == STORE_PENDING); |
1123 | assert(mem_obj != NULL); | |
bf8fe701 | 1124 | debugs(20, 6, "storeAbort: " << getMD5Text()); |
34266cde | 1125 | |
1bfe9ade | 1126 | lock("StoreEntry::abort"); /* lock while aborting */ |
d88e3c49 | 1127 | negativeCache(); |
34266cde | 1128 | |
d88e3c49 | 1129 | releaseRequest(); |
34266cde | 1130 | |
bfb55b6f | 1131 | EBIT_SET(flags, ENTRY_ABORTED); |
34266cde | 1132 | |
3900307b | 1133 | setMemStatus(NOT_IN_MEMORY); |
34266cde | 1134 | |
bfb55b6f | 1135 | store_status = STORE_OK; |
34266cde | 1136 | |
474cac1b | 1137 | /* Notify the server side */ |
62e76326 | 1138 | |
8ea67c2b | 1139 | /* |
1140 | * DPW 2007-05-07 | |
1141 | * Should we check abort.data for validity? | |
1142 | */ | |
bfb55b6f | 1143 | if (mem_obj->abort.callback) { |
26ac0430 | 1144 | if (!cbdataReferenceValid(mem_obj->abort.data)) |
e0236918 | 1145 | debugs(20, DBG_IMPORTANT,HERE << "queueing event when abort.data is not valid"); |
bfb55b6f | 1146 | eventAdd("mem_obj->abort.callback", |
1147 | mem_obj->abort.callback, | |
1148 | mem_obj->abort.data, | |
62e76326 | 1149 | 0.0, |
8ea67c2b | 1150 | true); |
26ac0430 | 1151 | unregisterAbort(); |
bfcaf585 | 1152 | } |
62e76326 | 1153 | |
1154 | /* XXX Should we reverse these two, so that there is no | |
26ac0430 | 1155 | * unneeded disk swapping triggered? |
528b2c61 | 1156 | */ |
474cac1b | 1157 | /* Notify the client side */ |
d88e3c49 | 1158 | invokeHandlers(); |
62e76326 | 1159 | |
aa1a691e AR |
1160 | // abort swap out, invalidating what was created so far (release follows) |
1161 | swapOutFileClose(StoreIOState::writerGone); | |
62e76326 | 1162 | |
1bfe9ade | 1163 | unlock("StoreEntry::abort"); /* unlock */ |
090089c4 | 1164 | } |
1165 | ||
6d3c2758 HN |
1166 | /** |
1167 | * Clear Memory storage to accommodate the given object len | |
1168 | */ | |
1169 | void | |
d4432957 | 1170 | storeGetMemSpace(int size) |
090089c4 | 1171 | { |
1d5161bd | 1172 | PROF_start(storeGetMemSpace); |
b32508fb | 1173 | StoreEntry *e = NULL; |
20cba4b4 | 1174 | int released = 0; |
b32508fb | 1175 | static time_t last_check = 0; |
528b2c61 | 1176 | size_t pages_needed; |
6a566b9c | 1177 | RemovalPurgeWalker *walker; |
62e76326 | 1178 | |
1d5161bd | 1179 | if (squid_curtime == last_check) { |
1180 | PROF_stop(storeGetMemSpace); | |
62e76326 | 1181 | return; |
1d5161bd | 1182 | } |
62e76326 | 1183 | |
b32508fb | 1184 | last_check = squid_curtime; |
62e76326 | 1185 | |
2ad51840 | 1186 | pages_needed = (size + SM_PAGE_SIZE-1) / SM_PAGE_SIZE; |
62e76326 | 1187 | |
1d5161bd | 1188 | if (mem_node::InUseCount() + pages_needed < store_pages_max) { |
1189 | PROF_stop(storeGetMemSpace); | |
62e76326 | 1190 | return; |
1d5161bd | 1191 | } |
62e76326 | 1192 | |
e4049756 | 1193 | debugs(20, 2, "storeGetMemSpace: Starting, need " << pages_needed << |
1194 | " pages"); | |
62e76326 | 1195 | |
6a566b9c | 1196 | /* XXX what to set as max_scan here? */ |
1197 | walker = mem_policy->PurgeInit(mem_policy, 100000); | |
62e76326 | 1198 | |
c1dd71ae | 1199 | while ((e = walker->Next(walker))) { |
d88e3c49 | 1200 | e->purgeMem(); |
5db6bf73 | 1201 | ++released; |
62e76326 | 1202 | |
1203 | if (mem_node::InUseCount() + pages_needed < store_pages_max) | |
1204 | break; | |
8350fe9b | 1205 | } |
62e76326 | 1206 | |
6a566b9c | 1207 | walker->Done(walker); |
bf8fe701 | 1208 | debugs(20, 3, "storeGetMemSpace stats:"); |
1209 | debugs(20, 3, " " << std::setw(6) << hot_obj_count << " HOT objects"); | |
1210 | debugs(20, 3, " " << std::setw(6) << released << " were released"); | |
1d5161bd | 1211 | PROF_stop(storeGetMemSpace); |
090089c4 | 1212 | } |
1213 | ||
c8f4eac4 | 1214 | /* thunk through to Store::Root().maintain(). Note that this would be better still |
26ac0430 AJ |
1215 | * if registered against the root store itself, but that requires more complex |
1216 | * update logic - bigger fish to fry first. Long term each store when | |
c8f4eac4 | 1217 | * it becomes active will self register |
1218 | */ | |
1219 | void | |
ced8def3 | 1220 | Store::Maintain(void *) |
c8f4eac4 | 1221 | { |
1222 | Store::Root().maintain(); | |
1223 | ||
1224 | /* Reregister a maintain event .. */ | |
1225 | eventAdd("MaintainSwapSpace", Maintain, NULL, 1.0, 1); | |
1226 | ||
1227 | } | |
1228 | ||
090089c4 | 1229 | /* The maximum objects to scan for maintain storage space */ |
c21ad0f5 | 1230 | #define MAINTAIN_MAX_SCAN 1024 |
1231 | #define MAINTAIN_MAX_REMOVE 64 | |
090089c4 | 1232 | |
090089c4 | 1233 | /* release an object from a cache */ |
6c78a099 | 1234 | void |
5f33b71d | 1235 | StoreEntry::release() |
090089c4 | 1236 | { |
88bfe092 | 1237 | PROF_start(storeRelease); |
1bfe9ade | 1238 | debugs(20, 3, "releasing " << *this << ' ' << getMD5Text()); |
090089c4 | 1239 | /* If, for any reason we can't discard this object because of an |
1240 | * outstanding request, mark it for pending release */ | |
62e76326 | 1241 | |
3900307b | 1242 | if (locked()) { |
d88e3c49 | 1243 | expireNow(); |
bf8fe701 | 1244 | debugs(20, 3, "storeRelease: Only setting RELEASE_REQUEST bit"); |
d88e3c49 | 1245 | releaseRequest(); |
62e76326 | 1246 | PROF_stop(storeRelease); |
1247 | return; | |
090089c4 | 1248 | } |
62e76326 | 1249 | |
2745fea5 AR |
1250 | if (Store::Controller::store_dirs_rebuilding && swap_filen > -1) { |
1251 | /* TODO: Teach disk stores to handle releases during rebuild instead. */ | |
ce49546e | 1252 | |
2745fea5 | 1253 | Store::Root().memoryUnlink(*this); |
62e76326 | 1254 | |
2745fea5 | 1255 | setPrivateKey(); |
22c25cbb | 1256 | |
2745fea5 AR |
1257 | // lock the entry until rebuilding is done |
1258 | lock("storeLateRelease"); | |
1259 | setReleaseFlag(); | |
1260 | LateReleaseStack.push(this); | |
22c25cbb | 1261 | return; |
43d9cf56 | 1262 | } |
62e76326 | 1263 | |
5f33b71d | 1264 | storeLog(STORE_LOG_RELEASE, this); |
2745fea5 | 1265 | if (swap_filen > -1 && !EBIT_TEST(flags, KEY_PRIVATE)) { |
f58bb2f4 | 1266 | // log before unlink() below clears swap_filen |
2745fea5 | 1267 | storeDirSwapLog(this, SWAP_LOG_DEL); |
090089c4 | 1268 | } |
62e76326 | 1269 | |
2745fea5 | 1270 | Store::Root().unlink(*this); |
5f33b71d | 1271 | destroyStoreEntry(static_cast<hash_link *>(this)); |
88bfe092 | 1272 | PROF_stop(storeRelease); |
090089c4 | 1273 | } |
1274 | ||
e42d5181 | 1275 | static void |
ced8def3 | 1276 | storeLateRelease(void *) |
e42d5181 | 1277 | { |
1278 | StoreEntry *e; | |
e42d5181 | 1279 | static int n = 0; |
62e76326 | 1280 | |
2745fea5 | 1281 | if (Store::Controller::store_dirs_rebuilding) { |
62e76326 | 1282 | eventAdd("storeLateRelease", storeLateRelease, NULL, 1.0, 1); |
1283 | return; | |
e42d5181 | 1284 | } |
62e76326 | 1285 | |
cfb88efb AR |
1286 | // TODO: this works but looks unelegant. |
1287 | for (int i = 0; i < 10; ++i) { | |
1288 | if (LateReleaseStack.empty()) { | |
24b14da9 FC |
1289 | debugs(20, DBG_IMPORTANT, "storeLateRelease: released " << n << " objects"); |
1290 | return; | |
cfb88efb AR |
1291 | } else { |
1292 | e = LateReleaseStack.top(); | |
1293 | LateReleaseStack.pop(); | |
3aa53107 | 1294 | } |
62e76326 | 1295 | |
1bfe9ade | 1296 | e->unlock("storeLateRelease"); |
5db6bf73 | 1297 | ++n; |
e42d5181 | 1298 | } |
62e76326 | 1299 | |
e42d5181 | 1300 | eventAdd("storeLateRelease", storeLateRelease, NULL, 0.0, 1); |
1301 | } | |
1302 | ||
090089c4 | 1303 | /* return 1 if a store entry is locked */ |
cd748f27 | 1304 | int |
3900307b | 1305 | StoreEntry::locked() const |
090089c4 | 1306 | { |
3900307b | 1307 | if (lock_count) |
62e76326 | 1308 | return 1; |
1309 | ||
b8890359 | 1310 | /* |
1bfe9ade AR |
1311 | * SPECIAL, PUBLIC entries should be "locked"; |
1312 | * XXX: Their owner should lock them then instead of relying on this hack. | |
b8890359 | 1313 | */ |
3900307b | 1314 | if (EBIT_TEST(flags, ENTRY_SPECIAL)) |
1315 | if (!EBIT_TEST(flags, KEY_PRIVATE)) | |
62e76326 | 1316 | return 1; |
1317 | ||
30a4f2a8 | 1318 | return 0; |
090089c4 | 1319 | } |
1320 | ||
528b2c61 | 1321 | bool |
1322 | StoreEntry::validLength() const | |
6602e70e | 1323 | { |
47f6e231 | 1324 | int64_t diff; |
d8b249ef | 1325 | const HttpReply *reply; |
528b2c61 | 1326 | assert(mem_obj != NULL); |
1327 | reply = getReply(); | |
bf8fe701 | 1328 | debugs(20, 3, "storeEntryValidLength: Checking '" << getMD5Text() << "'"); |
e4049756 | 1329 | debugs(20, 5, "storeEntryValidLength: object_len = " << |
707fdc47 | 1330 | objectLen()); |
bf8fe701 | 1331 | debugs(20, 5, "storeEntryValidLength: hdr_sz = " << reply->hdr_sz); |
1332 | debugs(20, 5, "storeEntryValidLength: content_length = " << reply->content_length); | |
62e76326 | 1333 | |
d8b249ef | 1334 | if (reply->content_length < 0) { |
bf8fe701 | 1335 | debugs(20, 5, "storeEntryValidLength: Unspecified content length: " << getMD5Text()); |
62e76326 | 1336 | return 1; |
ffe4a367 | 1337 | } |
62e76326 | 1338 | |
07304bf9 | 1339 | if (reply->hdr_sz == 0) { |
bf8fe701 | 1340 | debugs(20, 5, "storeEntryValidLength: Zero header size: " << getMD5Text()); |
62e76326 | 1341 | return 1; |
ffe4a367 | 1342 | } |
62e76326 | 1343 | |
c2a7cefd | 1344 | if (mem_obj->method == Http::METHOD_HEAD) { |
bf8fe701 | 1345 | debugs(20, 5, "storeEntryValidLength: HEAD request: " << getMD5Text()); |
62e76326 | 1346 | return 1; |
ffe4a367 | 1347 | } |
62e76326 | 1348 | |
9b769c67 | 1349 | if (reply->sline.status() == Http::scNotModified) |
62e76326 | 1350 | return 1; |
1351 | ||
9b769c67 | 1352 | if (reply->sline.status() == Http::scNoContent) |
62e76326 | 1353 | return 1; |
1354 | ||
707fdc47 | 1355 | diff = reply->hdr_sz + reply->content_length - objectLen(); |
62e76326 | 1356 | |
ebf4efff | 1357 | if (diff == 0) |
62e76326 | 1358 | return 1; |
1359 | ||
bf8fe701 | 1360 | debugs(20, 3, "storeEntryValidLength: " << (diff < 0 ? -diff : diff) << " bytes too " << (diff < 0 ? "big" : "small") <<"; '" << getMD5Text() << "'" ); |
62e76326 | 1361 | |
ebf4efff | 1362 | return 0; |
ffe4a367 | 1363 | } |
6602e70e | 1364 | |
6b7d87bb FC |
1365 | static void |
1366 | storeRegisterWithCacheManager(void) | |
1367 | { | |
8822ebee AR |
1368 | Mgr::RegisterAction("storedir", "Store Directory Stats", Store::Stats, 0, 1); |
1369 | Mgr::RegisterAction("store_io", "Store IO Interface Stats", &Mgr::StoreIoAction::Create, 0, 1); | |
1370 | Mgr::RegisterAction("store_check_cachable_stats", "storeCheckCachable() Stats", | |
d9fc6862 | 1371 | storeCheckCachableStats, 0, 1); |
6b7d87bb FC |
1372 | } |
1373 | ||
b8d8561b | 1374 | void |
1375 | storeInit(void) | |
c943f331 | 1376 | { |
25535cbe | 1377 | storeKeyInit(); |
6a566b9c | 1378 | mem_policy = createRemovalPolicy(Config.memPolicy); |
8638fc66 | 1379 | storeDigestInit(); |
e3ef2b09 | 1380 | storeLogOpen(); |
e42d5181 | 1381 | eventAdd("storeLateRelease", storeLateRelease, NULL, 1.0, 1); |
c8f4eac4 | 1382 | Store::Root().init(); |
b2c141d4 | 1383 | storeRebuildStart(); |
d120ed12 FC |
1384 | |
1385 | storeRegisterWithCacheManager(); | |
62ee09ca | 1386 | } |
1387 | ||
b8d8561b | 1388 | void |
1389 | storeConfigure(void) | |
b1c0cc67 | 1390 | { |
5ca027f0 | 1391 | Store::Root().updateLimits(); |
090089c4 | 1392 | } |
1393 | ||
9487bae9 | 1394 | bool |
97754f5a | 1395 | StoreEntry::memoryCachable() |
56f29785 | 1396 | { |
97754f5a AR |
1397 | if (!checkCachable()) |
1398 | return 0; | |
1399 | ||
3900307b | 1400 | if (mem_obj == NULL) |
62e76326 | 1401 | return 0; |
1402 | ||
3900307b | 1403 | if (mem_obj->data_hdr.size() == 0) |
62e76326 | 1404 | return 0; |
1405 | ||
19fdd3f3 | 1406 | if (mem_obj->inmem_lo != 0) |
e1381638 | 1407 | return 0; |
19fdd3f3 | 1408 | |
d227e181 | 1409 | if (!Config.onoff.memory_cache_first && swap_status == SWAPOUT_DONE && refcount == 1) |
e1381638 | 1410 | return 0; |
19fdd3f3 HN |
1411 | |
1412 | return 1; | |
56f29785 | 1413 | } |
1414 | ||
edce4d98 | 1415 | int |
3900307b | 1416 | StoreEntry::checkNegativeHit() const |
edce4d98 | 1417 | { |
3900307b | 1418 | if (!EBIT_TEST(flags, ENTRY_NEGCACHED)) |
62e76326 | 1419 | return 0; |
1420 | ||
3900307b | 1421 | if (expires <= squid_curtime) |
62e76326 | 1422 | return 0; |
1423 | ||
3900307b | 1424 | if (store_status != STORE_OK) |
62e76326 | 1425 | return 0; |
1426 | ||
edce4d98 | 1427 | return 1; |
1428 | } | |
1429 | ||
ac9cc053 AJ |
1430 | /** |
1431 | * Set object for negative caching. | |
1432 | * Preserves any expiry information given by the server. | |
1433 | * In absence of proper expiry info it will set to expire immediately, | |
1434 | * or with HTTP-violations enabled the configured negative-TTL is observed | |
1435 | */ | |
b8d8561b | 1436 | void |
d88e3c49 | 1437 | StoreEntry::negativeCache() |
79b5cc5f | 1438 | { |
da03a7e0 AJ |
1439 | // XXX: should make the default for expires 0 instead of -1 |
1440 | // so we can distinguish "Expires: -1" from nothing. | |
1441 | if (expires <= 0) | |
626096be | 1442 | #if USE_HTTP_VIOLATIONS |
ac9cc053 AJ |
1443 | expires = squid_curtime + Config.negativeTtl; |
1444 | #else | |
1445 | expires = squid_curtime; | |
1446 | #endif | |
d88e3c49 | 1447 | EBIT_SET(flags, ENTRY_NEGCACHED); |
79b5cc5f | 1448 | } |
0a21bd84 | 1449 | |
1450 | void | |
1451 | storeFreeMemory(void) | |
1452 | { | |
2745fea5 | 1453 | Store::FreeMemory(); |
9bc73deb | 1454 | #if USE_CACHE_DIGESTS |
a901d0b4 | 1455 | delete store_digest; |
c68e9c6b | 1456 | #endif |
8638fc66 | 1457 | store_digest = NULL; |
0a21bd84 | 1458 | } |
a7e59001 | 1459 | |
1460 | int | |
1461 | expiresMoreThan(time_t expires, time_t when) | |
1462 | { | |
c21ad0f5 | 1463 | if (expires < 0) /* No Expires given */ |
62e76326 | 1464 | return 1; |
1465 | ||
48f44632 | 1466 | return (expires > (squid_curtime + when)); |
a7e59001 | 1467 | } |
fe54d06d | 1468 | |
1469 | int | |
3900307b | 1470 | StoreEntry::validToSend() const |
fe54d06d | 1471 | { |
3900307b | 1472 | if (EBIT_TEST(flags, RELEASE_REQUEST)) |
62e76326 | 1473 | return 0; |
1474 | ||
3900307b | 1475 | if (EBIT_TEST(flags, ENTRY_NEGCACHED)) |
1476 | if (expires <= squid_curtime) | |
62e76326 | 1477 | return 0; |
1478 | ||
3900307b | 1479 | if (EBIT_TEST(flags, ENTRY_ABORTED)) |
62e76326 | 1480 | return 0; |
1481 | ||
22696a16 AR |
1482 | // now check that the entry has a cache backing or is collapsed |
1483 | if (swap_filen > -1) // backed by a disk cache | |
1484 | return 1; | |
1485 | ||
1486 | if (swappingOut()) // will be backed by a disk cache | |
1487 | return 1; | |
1488 | ||
1489 | if (!mem_obj) // not backed by a memory cache and not collapsed | |
1490 | return 0; | |
1491 | ||
22696a16 | 1492 | // StoreEntry::storeClientType() assumes DISK_CLIENT here, but there is no |
a4b04ff8 AR |
1493 | // disk cache backing that store_client constructor will assert. XXX: This |
1494 | // is wrong for range requests (that could feed off nibbled memory) and for | |
1495 | // entries backed by the shared memory cache (that could, in theory, get | |
1496 | // nibbled bytes from that cache, but there is no such "memoryIn" code). | |
1497 | if (mem_obj->inmem_lo) // in memory cache, but got nibbled at | |
22696a16 AR |
1498 | return 0; |
1499 | ||
a4b04ff8 AR |
1500 | // The following check is correct but useless at this position. TODO: Move |
1501 | // it up when the shared memory cache can either replenish locally nibbled | |
1502 | // bytes or, better, does not use local RAM copy at all. | |
1503 | // if (mem_obj->memCache.index >= 0) // backed by a shared memory cache | |
1504 | // return 1; | |
1505 | ||
fe54d06d | 1506 | return 1; |
1507 | } | |
62663274 | 1508 | |
1a210de4 | 1509 | bool |
3900307b | 1510 | StoreEntry::timestampsSet() |
ca98227c | 1511 | { |
3900307b | 1512 | const HttpReply *reply = getReply(); |
2f58241d | 1513 | time_t served_date = reply->date; |
789217a2 | 1514 | int age = reply->header.getInt(Http::HdrType::AGE); |
31d36bfd | 1515 | /* Compute the timestamp, mimicking RFC2616 section 13.2.3. */ |
2f58241d | 1516 | /* make sure that 0 <= served_date <= squid_curtime */ |
62e76326 | 1517 | |
2f58241d | 1518 | if (served_date < 0 || served_date > squid_curtime) |
62e76326 | 1519 | served_date = squid_curtime; |
1520 | ||
525bf9dc BD |
1521 | /* Bug 1791: |
1522 | * If the returned Date: is more than 24 hours older than | |
1523 | * the squid_curtime, then one of us needs to use NTP to set our | |
1524 | * clock. We'll pretend that our clock is right. | |
1525 | */ | |
1526 | else if (served_date < (squid_curtime - 24 * 60 * 60) ) | |
1527 | served_date = squid_curtime; | |
62e76326 | 1528 | |
efd900cb | 1529 | /* |
212cbb48 | 1530 | * Compensate with Age header if origin server clock is ahead |
1531 | * of us and there is a cache in between us and the origin | |
1532 | * server. But DONT compensate if the age value is larger than | |
1533 | * squid_curtime because it results in a negative served_date. | |
efd900cb | 1534 | */ |
1535 | if (age > squid_curtime - served_date) | |
62e76326 | 1536 | if (squid_curtime > age) |
1537 | served_date = squid_curtime - age; | |
1538 | ||
31d36bfd AR |
1539 | // compensate for Squid-to-server and server-to-Squid delays |
1540 | if (mem_obj && mem_obj->request) { | |
1541 | const time_t request_sent = | |
1542 | mem_obj->request->hier.peer_http_request_sent.tv_sec; | |
1543 | if (0 < request_sent && request_sent < squid_curtime) | |
1544 | served_date -= (squid_curtime - request_sent); | |
1545 | } | |
1546 | ||
1a210de4 | 1547 | time_t exp = 0; |
0d465a25 | 1548 | if (reply->expires > 0 && reply->date > -1) |
1a210de4 | 1549 | exp = served_date + (reply->expires - reply->date); |
0d465a25 | 1550 | else |
1a210de4 EB |
1551 | exp = reply->expires; |
1552 | ||
438b41ba EB |
1553 | if (timestamp == served_date && expires == exp) { |
1554 | // if the reply lacks LMT, then we now know that our effective | |
1555 | // LMT (i.e., timestamp) will stay the same, otherwise, old and | |
1556 | // new modification times must match | |
1557 | if (reply->last_modified < 0 || reply->last_modified == lastModified()) | |
1558 | return false; // nothing has changed | |
1559 | } | |
1a210de4 EB |
1560 | |
1561 | expires = exp; | |
62e76326 | 1562 | |
438b41ba | 1563 | lastModified_ = reply->last_modified; |
62e76326 | 1564 | |
3900307b | 1565 | timestamp = served_date; |
1a210de4 EB |
1566 | |
1567 | return true; | |
ca98227c | 1568 | } |
429fdbec | 1569 | |
bfcaf585 | 1570 | void |
3900307b | 1571 | StoreEntry::registerAbort(STABH * cb, void *data) |
bfcaf585 | 1572 | { |
3900307b | 1573 | assert(mem_obj); |
1574 | assert(mem_obj->abort.callback == NULL); | |
1575 | mem_obj->abort.callback = cb; | |
8ea67c2b | 1576 | mem_obj->abort.data = cbdataReference(data); |
bfcaf585 | 1577 | } |
1578 | ||
1579 | void | |
3900307b | 1580 | StoreEntry::unregisterAbort() |
bfcaf585 | 1581 | { |
3900307b | 1582 | assert(mem_obj); |
8ea67c2b | 1583 | if (mem_obj->abort.callback) { |
26ac0430 AJ |
1584 | mem_obj->abort.callback = NULL; |
1585 | cbdataReferenceDone(mem_obj->abort.data); | |
8ea67c2b | 1586 | } |
bfcaf585 | 1587 | } |
88738790 | 1588 | |
f09f5b26 | 1589 | void |
3900307b | 1590 | StoreEntry::dump(int l) const |
1591 | { | |
bf8fe701 | 1592 | debugs(20, l, "StoreEntry->key: " << getMD5Text()); |
1593 | debugs(20, l, "StoreEntry->next: " << next); | |
1594 | debugs(20, l, "StoreEntry->mem_obj: " << mem_obj); | |
4a7a3d56 | 1595 | debugs(20, l, "StoreEntry->timestamp: " << timestamp); |
1596 | debugs(20, l, "StoreEntry->lastref: " << lastref); | |
1597 | debugs(20, l, "StoreEntry->expires: " << expires); | |
438b41ba | 1598 | debugs(20, l, "StoreEntry->lastModified_: " << lastModified_); |
4a7a3d56 | 1599 | debugs(20, l, "StoreEntry->swap_file_sz: " << swap_file_sz); |
bf8fe701 | 1600 | debugs(20, l, "StoreEntry->refcount: " << refcount); |
1601 | debugs(20, l, "StoreEntry->flags: " << storeEntryFlags(this)); | |
4a7a3d56 | 1602 | debugs(20, l, "StoreEntry->swap_dirn: " << swap_dirn); |
1603 | debugs(20, l, "StoreEntry->swap_filen: " << swap_filen); | |
1604 | debugs(20, l, "StoreEntry->lock_count: " << lock_count); | |
1605 | debugs(20, l, "StoreEntry->mem_status: " << mem_status); | |
1606 | debugs(20, l, "StoreEntry->ping_status: " << ping_status); | |
1607 | debugs(20, l, "StoreEntry->store_status: " << store_status); | |
1608 | debugs(20, l, "StoreEntry->swap_status: " << swap_status); | |
d377699f | 1609 | } |
1610 | ||
1f38f50a | 1611 | /* |
1612 | * NOTE, this function assumes only two mem states | |
1613 | */ | |
f09f5b26 | 1614 | void |
3900307b | 1615 | StoreEntry::setMemStatus(mem_status_t new_status) |
8350fe9b | 1616 | { |
3900307b | 1617 | if (new_status == mem_status) |
62e76326 | 1618 | return; |
1619 | ||
6ebe9a4c AR |
1620 | // are we using a shared memory cache? |
1621 | if (Config.memShared && IamWorkerProcess()) { | |
9487bae9 AR |
1622 | // This method was designed to update replacement policy, not to |
1623 | // actually purge something from the memory cache (TODO: rename?). | |
1624 | // Shared memory cache does not have a policy that needs updates. | |
1625 | mem_status = new_status; | |
1626 | return; | |
1627 | } | |
1628 | ||
3900307b | 1629 | assert(mem_obj != NULL); |
62e76326 | 1630 | |
b93bcace | 1631 | if (new_status == IN_MEMORY) { |
3900307b | 1632 | assert(mem_obj->inmem_lo == 0); |
62e76326 | 1633 | |
3900307b | 1634 | if (EBIT_TEST(flags, ENTRY_SPECIAL)) { |
c877c0bc | 1635 | debugs(20, 4, "not inserting special " << *this << " into policy"); |
62e76326 | 1636 | } else { |
3900307b | 1637 | mem_policy->Add(mem_policy, this, &mem_obj->repl); |
c877c0bc | 1638 | debugs(20, 4, "inserted " << *this << " key: " << getMD5Text()); |
62e76326 | 1639 | } |
1640 | ||
5db6bf73 | 1641 | ++hot_obj_count; // TODO: maintain for the shared hot cache as well |
b93bcace | 1642 | } else { |
3900307b | 1643 | if (EBIT_TEST(flags, ENTRY_SPECIAL)) { |
c877c0bc | 1644 | debugs(20, 4, "not removing special " << *this << " from policy"); |
62e76326 | 1645 | } else { |
3900307b | 1646 | mem_policy->Remove(mem_policy, this, &mem_obj->repl); |
c877c0bc | 1647 | debugs(20, 4, "removed " << *this); |
62e76326 | 1648 | } |
1649 | ||
5e263176 | 1650 | --hot_obj_count; |
b93bcace | 1651 | } |
62e76326 | 1652 | |
3900307b | 1653 | mem_status = new_status; |
8350fe9b | 1654 | } |
6e86c3e8 | 1655 | |
9fb13bb6 | 1656 | const char * |
3900307b | 1657 | StoreEntry::url() const |
9fb13bb6 | 1658 | { |
328a07ab | 1659 | if (mem_obj == NULL) |
62e76326 | 1660 | return "[null_mem_obj]"; |
9fb13bb6 | 1661 | else |
c877c0bc | 1662 | return mem_obj->storeId(); |
9fb13bb6 | 1663 | } |
24ffafb4 | 1664 | |
c877c0bc AR |
1665 | MemObject * |
1666 | StoreEntry::makeMemObject() | |
24ffafb4 | 1667 | { |
c877c0bc AR |
1668 | if (!mem_obj) |
1669 | mem_obj = new MemObject(); | |
1670 | return mem_obj; | |
1671 | } | |
9487bae9 | 1672 | |
c877c0bc AR |
1673 | void |
1674 | StoreEntry::createMemObject(const char *aUrl, const char *aLogUrl, const HttpRequestMethod &aMethod) | |
1675 | { | |
1676 | makeMemObject(); | |
1677 | mem_obj->setUris(aUrl, aLogUrl, aMethod); | |
c21ad0f5 | 1678 | } |
1679 | ||
7e10ac87 AJ |
1680 | /** disable sending content to the clients. |
1681 | * | |
1682 | * This just sets DELAY_SENDING. | |
1683 | */ | |
c21ad0f5 | 1684 | void |
1685 | StoreEntry::buffer() | |
1686 | { | |
1687 | EBIT_SET(flags, DELAY_SENDING); | |
1688 | } | |
1689 | ||
7e10ac87 AJ |
1690 | /** flush any buffered content. |
1691 | * | |
1692 | * This just clears DELAY_SENDING and Invokes the handlers | |
1693 | * to begin sending anything that may be buffered. | |
1694 | */ | |
438fc1e3 | 1695 | void |
c21ad0f5 | 1696 | StoreEntry::flush() |
438fc1e3 | 1697 | { |
c21ad0f5 | 1698 | if (EBIT_TEST(flags, DELAY_SENDING)) { |
1699 | EBIT_CLR(flags, DELAY_SENDING); | |
d88e3c49 | 1700 | invokeHandlers(); |
b66315e4 | 1701 | } |
25535cbe | 1702 | } |
07304bf9 | 1703 | |
47f6e231 | 1704 | int64_t |
707fdc47 | 1705 | StoreEntry::objectLen() const |
07304bf9 | 1706 | { |
707fdc47 | 1707 | assert(mem_obj != NULL); |
1708 | return mem_obj->object_sz; | |
07304bf9 | 1709 | } |
1710 | ||
47f6e231 | 1711 | int64_t |
b37bde1e | 1712 | StoreEntry::contentLen() const |
07304bf9 | 1713 | { |
b37bde1e | 1714 | assert(mem_obj != NULL); |
1715 | assert(getReply() != NULL); | |
1716 | return objectLen() - getReply()->hdr_sz; | |
07304bf9 | 1717 | } |
f3986a15 | 1718 | |
528b2c61 | 1719 | HttpReply const * |
a0c227a9 | 1720 | StoreEntry::getReply() const |
f3986a15 | 1721 | { |
a0c227a9 | 1722 | return (mem_obj ? mem_obj->getReply().getRaw() : nullptr); |
f3986a15 | 1723 | } |
db1cd23c | 1724 | |
1725 | void | |
3900307b | 1726 | StoreEntry::reset() |
db1cd23c | 1727 | { |
3900307b | 1728 | assert (mem_obj); |
a0c227a9 | 1729 | debugs(20, 3, url()); |
3900307b | 1730 | mem_obj->reset(); |
438b41ba | 1731 | expires = lastModified_ = timestamp = -1; |
db1cd23c | 1732 | } |
2b906e48 | 1733 | |
cd748f27 | 1734 | /* |
1735 | * storeFsInit | |
1736 | * | |
1737 | * This routine calls the SETUP routine for each fs type. | |
1738 | * I don't know where the best place for this is, and I'm not going to shuffle | |
1739 | * around large chunks of code right now (that can be done once its working.) | |
1740 | */ | |
1741 | void | |
1742 | storeFsInit(void) | |
1743 | { | |
22d38e05 | 1744 | storeReplSetup(); |
cd748f27 | 1745 | } |
1746 | ||
22d38e05 | 1747 | /* |
1748 | * called to add another store removal policy module | |
1749 | */ | |
1750 | void | |
a2c963ae | 1751 | storeReplAdd(const char *type, REMOVALPOLICYCREATE * create) |
22d38e05 | 1752 | { |
1753 | int i; | |
62e76326 | 1754 | |
d64c1498 | 1755 | /* find the number of currently known repl types */ |
5db6bf73 | 1756 | for (i = 0; storerepl_list && storerepl_list[i].typestr; ++i) { |
d64c1498 | 1757 | if (strcmp(storerepl_list[i].typestr, type) == 0) { |
e0236918 | 1758 | debugs(20, DBG_IMPORTANT, "WARNING: Trying to load store replacement policy " << type << " twice."); |
d64c1498 AJ |
1759 | return; |
1760 | } | |
22d38e05 | 1761 | } |
62e76326 | 1762 | |
22d38e05 | 1763 | /* add the new type */ |
e6ccf245 | 1764 | storerepl_list = static_cast<storerepl_entry_t *>(xrealloc(storerepl_list, (i + 2) * sizeof(storerepl_entry_t))); |
62e76326 | 1765 | |
22d38e05 | 1766 | memset(&storerepl_list[i + 1], 0, sizeof(storerepl_entry_t)); |
62e76326 | 1767 | |
22d38e05 | 1768 | storerepl_list[i].typestr = type; |
62e76326 | 1769 | |
22d38e05 | 1770 | storerepl_list[i].create = create; |
1771 | } | |
1772 | ||
1773 | /* | |
1774 | * Create a removal policy instance | |
1775 | */ | |
1776 | RemovalPolicy * | |
1777 | createRemovalPolicy(RemovalPolicySettings * settings) | |
1778 | { | |
1779 | storerepl_entry_t *r; | |
62e76326 | 1780 | |
5db6bf73 | 1781 | for (r = storerepl_list; r && r->typestr; ++r) { |
62e76326 | 1782 | if (strcmp(r->typestr, settings->type) == 0) |
1783 | return r->create(settings->args); | |
22d38e05 | 1784 | } |
62e76326 | 1785 | |
e0236918 FC |
1786 | debugs(20, DBG_IMPORTANT, "ERROR: Unknown policy " << settings->type); |
1787 | debugs(20, DBG_IMPORTANT, "ERROR: Be sure to have set cache_replacement_policy"); | |
1788 | debugs(20, DBG_IMPORTANT, "ERROR: and memory_replacement_policy in squid.conf!"); | |
0c5ccf11 | 1789 | fatalf("ERROR: Unknown policy %s\n", settings->type); |
c21ad0f5 | 1790 | return NULL; /* NOTREACHED */ |
22d38e05 | 1791 | } |
1792 | ||
cd748f27 | 1793 | #if 0 |
fc8b9fc0 | 1794 | void |
1795 | storeSwapFileNumberSet(StoreEntry * e, sfileno filn) | |
1796 | { | |
1797 | if (e->swap_file_number == filn) | |
62e76326 | 1798 | return; |
1799 | ||
fc8b9fc0 | 1800 | if (filn < 0) { |
62e76326 | 1801 | assert(-1 == filn); |
1802 | storeDirMapBitReset(e->swap_file_number); | |
1803 | storeDirLRUDelete(e); | |
1804 | e->swap_file_number = -1; | |
fc8b9fc0 | 1805 | } else { |
62e76326 | 1806 | assert(-1 == e->swap_file_number); |
1807 | storeDirMapBitSet(e->swap_file_number = filn); | |
1808 | storeDirLRUAdd(e); | |
fc8b9fc0 | 1809 | } |
1810 | } | |
62e76326 | 1811 | |
cd748f27 | 1812 | #endif |
e6ccf245 | 1813 | |
eacfca83 AR |
1814 | void |
1815 | StoreEntry::storeErrorResponse(HttpReply *reply) | |
1816 | { | |
1817 | lock("StoreEntry::storeErrorResponse"); | |
1818 | buffer(); | |
1819 | replaceHttpReply(reply); | |
1820 | flush(); | |
1821 | complete(); | |
1822 | negativeCache(); | |
1823 | releaseRequest(); | |
1824 | unlock("StoreEntry::storeErrorResponse"); | |
1825 | } | |
1826 | ||
db237875 | 1827 | /* |
1828 | * Replace a store entry with | |
528b2c61 | 1829 | * a new reply. This eats the reply. |
1830 | */ | |
4a56ee8d | 1831 | void |
3756e5c0 | 1832 | StoreEntry::replaceHttpReply(HttpReply *rep, bool andStartWriting) |
4a56ee8d | 1833 | { |
bf8fe701 | 1834 | debugs(20, 3, "StoreEntry::replaceHttpReply: " << url()); |
62e76326 | 1835 | |
4a56ee8d | 1836 | if (!mem_obj) { |
fa84c01d | 1837 | debugs(20, DBG_CRITICAL, "Attempt to replace object with no in-memory representation"); |
62e76326 | 1838 | return; |
528b2c61 | 1839 | } |
62e76326 | 1840 | |
a0c227a9 | 1841 | mem_obj->replaceReply(HttpReplyPointer(rep)); |
4a56ee8d | 1842 | |
3756e5c0 AR |
1843 | if (andStartWriting) |
1844 | startWriting(); | |
1845 | } | |
1846 | ||
3756e5c0 AR |
1847 | void |
1848 | StoreEntry::startWriting() | |
1849 | { | |
691ad095 | 1850 | /* TODO: when we store headers separately remove the header portion */ |
528b2c61 | 1851 | /* TODO: mark the length of the headers ? */ |
1852 | /* We ONLY want the headers */ | |
62e76326 | 1853 | |
4a56ee8d | 1854 | assert (isEmpty()); |
3756e5c0 | 1855 | assert(mem_obj); |
9199139f | 1856 | |
3756e5c0 AR |
1857 | const HttpReply *rep = getReply(); |
1858 | assert(rep); | |
62e76326 | 1859 | |
15179984 AJ |
1860 | buffer(); |
1861 | rep->packHeadersInto(this); | |
3756e5c0 | 1862 | mem_obj->markEndOfReplyHeaders(); |
4475555f | 1863 | EBIT_CLR(flags, ENTRY_FWD_HDR_WAIT); |
62e76326 | 1864 | |
15179984 | 1865 | rep->body.packInto(this); |
691ad095 | 1866 | flush(); |
528b2c61 | 1867 | } |
62e76326 | 1868 | |
528b2c61 | 1869 | char const * |
1870 | StoreEntry::getSerialisedMetaData() | |
1871 | { | |
1872 | StoreMeta *tlv_list = storeSwapMetaBuild(this); | |
1873 | int swap_hdr_sz; | |
1874 | char *result = storeSwapMetaPack(tlv_list, &swap_hdr_sz); | |
1875 | storeSwapTLVFree(tlv_list); | |
aa1a691e AR |
1876 | assert (swap_hdr_sz >= 0); |
1877 | mem_obj->swap_hdr_sz = (size_t) swap_hdr_sz; | |
528b2c61 | 1878 | return result; |
1879 | } | |
1880 | ||
0cdcf3d7 AR |
1881 | /** |
1882 | * Abandon the transient entry our worker has created if neither the shared | |
1883 | * memory cache nor the disk cache wants to store it. Collapsed requests, if | |
1884 | * any, should notice and use Plan B instead of getting stuck waiting for us | |
1885 | * to start swapping the entry out. | |
1886 | */ | |
1887 | void | |
2da4bfe6 A |
1888 | StoreEntry::transientsAbandonmentCheck() |
1889 | { | |
0cdcf3d7 | 1890 | if (mem_obj && !mem_obj->smpCollapsed && // this worker is responsible |
2da4bfe6 A |
1891 | mem_obj->xitTable.index >= 0 && // other workers may be interested |
1892 | mem_obj->memCache.index < 0 && // rejected by the shared memory cache | |
1893 | mem_obj->swapout.decision == MemObject::SwapOut::swImpossible) { | |
0cdcf3d7 AR |
1894 | debugs(20, 7, "cannot be shared: " << *this); |
1895 | if (!shutting_down) // Store::Root() is FATALly missing during shutdown | |
1896 | Store::Root().transientsAbandon(*this); | |
1897 | } | |
1898 | } | |
1899 | ||
1900 | void | |
ced8def3 | 1901 | StoreEntry::memOutDecision(const bool) |
2da4bfe6 | 1902 | { |
0cdcf3d7 AR |
1903 | transientsAbandonmentCheck(); |
1904 | } | |
1905 | ||
1906 | void | |
2da4bfe6 A |
1907 | StoreEntry::swapOutDecision(const MemObject::SwapOut::Decision &decision) |
1908 | { | |
0cdcf3d7 AR |
1909 | // Abandon our transient entry if neither shared memory nor disk wants it. |
1910 | assert(mem_obj); | |
1911 | mem_obj->swapout.decision = decision; | |
1912 | transientsAbandonmentCheck(); | |
1913 | } | |
1914 | ||
528b2c61 | 1915 | void |
5b55f1f1 | 1916 | StoreEntry::trimMemory(const bool preserveSwappable) |
528b2c61 | 1917 | { |
7fef2365 | 1918 | /* |
1919 | * DPW 2007-05-09 | |
1920 | * Bug #1943. We must not let go any data for IN_MEMORY | |
1921 | * objects. We have to wait until the mem_status changes. | |
1922 | */ | |
1923 | if (mem_status == IN_MEMORY) | |
26ac0430 | 1924 | return; |
7fef2365 | 1925 | |
c5426f8f AR |
1926 | if (EBIT_TEST(flags, ENTRY_SPECIAL)) |
1927 | return; // cannot trim because we do not load them again | |
1928 | ||
99921d9d AR |
1929 | if (preserveSwappable) |
1930 | mem_obj->trimSwappable(); | |
1931 | else | |
1932 | mem_obj->trimUnSwappable(); | |
1933 | ||
1934 | debugs(88, 7, *this << " inmem_lo=" << mem_obj->inmem_lo); | |
528b2c61 | 1935 | } |
62e76326 | 1936 | |
0655fa4d | 1937 | bool |
438b41ba | 1938 | StoreEntry::modifiedSince(const time_t ims, const int imslen) const |
0655fa4d | 1939 | { |
1940 | int object_length; | |
438b41ba | 1941 | const time_t mod_time = lastModified(); |
0655fa4d | 1942 | |
bf8fe701 | 1943 | debugs(88, 3, "modifiedSince: '" << url() << "'"); |
0655fa4d | 1944 | |
4a7a3d56 | 1945 | debugs(88, 3, "modifiedSince: mod_time = " << mod_time); |
0655fa4d | 1946 | |
1947 | if (mod_time < 0) | |
1948 | return true; | |
1949 | ||
1950 | /* Find size of the object */ | |
1951 | object_length = getReply()->content_length; | |
1952 | ||
1953 | if (object_length < 0) | |
b37bde1e | 1954 | object_length = contentLen(); |
0655fa4d | 1955 | |
438b41ba | 1956 | if (mod_time > ims) { |
bf8fe701 | 1957 | debugs(88, 3, "--> YES: entry newer than client"); |
0655fa4d | 1958 | return true; |
438b41ba | 1959 | } else if (mod_time < ims) { |
bf8fe701 | 1960 | debugs(88, 3, "--> NO: entry older than client"); |
0655fa4d | 1961 | return false; |
438b41ba | 1962 | } else if (imslen < 0) { |
bf8fe701 | 1963 | debugs(88, 3, "--> NO: same LMT, no client length"); |
0655fa4d | 1964 | return false; |
438b41ba | 1965 | } else if (imslen == object_length) { |
bf8fe701 | 1966 | debugs(88, 3, "--> NO: same LMT, same length"); |
0655fa4d | 1967 | return false; |
1968 | } else { | |
bf8fe701 | 1969 | debugs(88, 3, "--> YES: same LMT, different length"); |
0655fa4d | 1970 | return true; |
1971 | } | |
1972 | } | |
1973 | ||
46017fdd CT |
1974 | bool |
1975 | StoreEntry::hasEtag(ETag &etag) const | |
1976 | { | |
1977 | if (const HttpReply *reply = getReply()) { | |
789217a2 | 1978 | etag = reply->header.getETag(Http::HdrType::ETAG); |
46017fdd CT |
1979 | if (etag.str) |
1980 | return true; | |
1981 | } | |
1982 | return false; | |
1983 | } | |
1984 | ||
79c8035e AR |
1985 | bool |
1986 | StoreEntry::hasIfMatchEtag(const HttpRequest &request) const | |
1987 | { | |
789217a2 | 1988 | const String reqETags = request.header.getList(Http::HdrType::IF_MATCH); |
79c8035e AR |
1989 | return hasOneOfEtags(reqETags, false); |
1990 | } | |
1991 | ||
1992 | bool | |
1993 | StoreEntry::hasIfNoneMatchEtag(const HttpRequest &request) const | |
1994 | { | |
789217a2 | 1995 | const String reqETags = request.header.getList(Http::HdrType::IF_NONE_MATCH); |
79c8035e | 1996 | // weak comparison is allowed only for HEAD or full-body GET requests |
450fe1cb | 1997 | const bool allowWeakMatch = !request.flags.isRanged && |
c2a7cefd | 1998 | (request.method == Http::METHOD_GET || request.method == Http::METHOD_HEAD); |
79c8035e AR |
1999 | return hasOneOfEtags(reqETags, allowWeakMatch); |
2000 | } | |
2001 | ||
2002 | /// whether at least one of the request ETags matches entity ETag | |
2003 | bool | |
2004 | StoreEntry::hasOneOfEtags(const String &reqETags, const bool allowWeakMatch) const | |
2005 | { | |
789217a2 | 2006 | const ETag repETag = getReply()->header.getETag(Http::HdrType::ETAG); |
79c8035e AR |
2007 | if (!repETag.str) |
2008 | return strListIsMember(&reqETags, "*", ','); | |
2009 | ||
2010 | bool matched = false; | |
2011 | const char *pos = NULL; | |
2012 | const char *item; | |
2013 | int ilen; | |
2014 | while (!matched && strListGetItem(&reqETags, ',', &item, &ilen, &pos)) { | |
2015 | if (!strncmp(item, "*", ilen)) | |
2016 | matched = true; | |
2017 | else { | |
2018 | String str; | |
2019 | str.append(item, ilen); | |
2020 | ETag reqETag; | |
2021 | if (etagParseInit(&reqETag, str.termedBuf())) { | |
2022 | matched = allowWeakMatch ? etagIsWeakEqual(repETag, reqETag) : | |
b59e6847 | 2023 | etagIsStrongEqual(repETag, reqETag); |
79c8035e AR |
2024 | } |
2025 | } | |
2026 | } | |
2027 | return matched; | |
2028 | } | |
2029 | ||
2745fea5 AR |
2030 | Store::Disk & |
2031 | StoreEntry::disk() const | |
c8f4eac4 | 2032 | { |
2033 | assert(0 <= swap_dirn && swap_dirn < Config.cacheSwap.n_configured); | |
2745fea5 AR |
2034 | const RefCount<Store::Disk> &sd = INDEXSD(swap_dirn); |
2035 | assert(sd); | |
2036 | return *sd; | |
c8f4eac4 | 2037 | } |
0655fa4d | 2038 | |
aa18a4ca | 2039 | /* |
2040 | * return true if the entry is in a state where | |
2041 | * it can accept more data (ie with write() method) | |
2042 | */ | |
2043 | bool | |
2044 | StoreEntry::isAccepting() const | |
2045 | { | |
2046 | if (STORE_PENDING != store_status) | |
2047 | return false; | |
2048 | ||
2049 | if (EBIT_TEST(flags, ENTRY_ABORTED)) | |
2050 | return false; | |
2051 | ||
2052 | return true; | |
2053 | } | |
2054 | ||
438b41ba EB |
2055 | const char * |
2056 | StoreEntry::describeTimestamps() const | |
2057 | { | |
2058 | LOCAL_ARRAY(char, buf, 256); | |
2059 | snprintf(buf, 256, "LV:%-9d LU:%-9d LM:%-9d EX:%-9d", | |
2060 | static_cast<int>(timestamp), | |
2061 | static_cast<int>(lastref), | |
2062 | static_cast<int>(lastModified_), | |
2063 | static_cast<int>(expires)); | |
2064 | return buf; | |
2065 | } | |
2066 | ||
2c4cd1ad AR |
2067 | std::ostream &operator <<(std::ostream &os, const StoreEntry &e) |
2068 | { | |
c0280457 AR |
2069 | os << "e:"; |
2070 | ||
99921d9d AR |
2071 | if (e.mem_obj) { |
2072 | if (e.mem_obj->xitTable.index > -1) | |
2073 | os << 't' << e.mem_obj->xitTable.index; | |
2074 | if (e.mem_obj->memCache.index > -1) | |
2075 | os << 'm' << e.mem_obj->memCache.index; | |
2076 | } | |
c0280457 | 2077 | if (e.swap_filen > -1 || e.swap_dirn > -1) |
99921d9d | 2078 | os << 'd' << e.swap_filen << '@' << e.swap_dirn; |
4475555f AR |
2079 | |
2080 | os << '='; | |
c0280457 AR |
2081 | |
2082 | // print only non-default status values, using unique letters | |
2083 | if (e.mem_status != NOT_IN_MEMORY || | |
9d4e9cfb AR |
2084 | e.store_status != STORE_PENDING || |
2085 | e.swap_status != SWAPOUT_NONE || | |
2086 | e.ping_status != PING_NONE) { | |
c0280457 AR |
2087 | if (e.mem_status != NOT_IN_MEMORY) os << 'm'; |
2088 | if (e.store_status != STORE_PENDING) os << 's'; | |
2089 | if (e.swap_status != SWAPOUT_NONE) os << 'w' << e.swap_status; | |
2090 | if (e.ping_status != PING_NONE) os << 'p' << e.ping_status; | |
c0280457 AR |
2091 | } |
2092 | ||
2093 | // print only set flags, using unique letters | |
2094 | if (e.flags) { | |
2095 | if (EBIT_TEST(e.flags, ENTRY_SPECIAL)) os << 'S'; | |
fa83b766 | 2096 | if (EBIT_TEST(e.flags, ENTRY_REVALIDATE_ALWAYS)) os << 'R'; |
99921d9d | 2097 | if (EBIT_TEST(e.flags, DELAY_SENDING)) os << 'P'; |
c0280457 AR |
2098 | if (EBIT_TEST(e.flags, RELEASE_REQUEST)) os << 'X'; |
2099 | if (EBIT_TEST(e.flags, REFRESH_REQUEST)) os << 'F'; | |
fa83b766 | 2100 | if (EBIT_TEST(e.flags, ENTRY_REVALIDATE_STALE)) os << 'E'; |
c0280457 AR |
2101 | if (EBIT_TEST(e.flags, ENTRY_DISPATCHED)) os << 'D'; |
2102 | if (EBIT_TEST(e.flags, KEY_PRIVATE)) os << 'I'; | |
2103 | if (EBIT_TEST(e.flags, ENTRY_FWD_HDR_WAIT)) os << 'W'; | |
2104 | if (EBIT_TEST(e.flags, ENTRY_NEGCACHED)) os << 'N'; | |
2105 | if (EBIT_TEST(e.flags, ENTRY_VALIDATED)) os << 'V'; | |
2106 | if (EBIT_TEST(e.flags, ENTRY_BAD_LENGTH)) os << 'L'; | |
2107 | if (EBIT_TEST(e.flags, ENTRY_ABORTED)) os << 'A'; | |
c0280457 AR |
2108 | } |
2109 | ||
4475555f AR |
2110 | if (e.mem_obj && e.mem_obj->smpCollapsed) |
2111 | os << 'O'; | |
2112 | ||
1bfe9ade | 2113 | return os << '/' << &e << '*' << e.locks(); |
2c4cd1ad AR |
2114 | } |
2115 | ||
e6ccf245 | 2116 | /* NullStoreEntry */ |
2117 | ||
2118 | NullStoreEntry NullStoreEntry::_instance; | |
2119 | ||
2120 | NullStoreEntry * | |
2121 | NullStoreEntry::getInstance() | |
2122 | { | |
2123 | return &_instance; | |
2124 | } | |
332dafa2 | 2125 | |
2126 | char const * | |
2127 | NullStoreEntry::getMD5Text() const | |
2128 | { | |
2129 | return "N/A"; | |
2130 | } | |
528b2c61 | 2131 | |
43ae1d95 | 2132 | void |
2133 | NullStoreEntry::operator delete(void*) | |
2134 | { | |
2135 | fatal ("Attempt to delete NullStoreEntry\n"); | |
2136 | } | |
2137 | ||
528b2c61 | 2138 | char const * |
2139 | NullStoreEntry::getSerialisedMetaData() | |
2140 | { | |
2141 | return NULL; | |
2142 | } | |
7d84d4ca | 2143 |