]> git.ipfire.org Git - thirdparty/squid.git/blame - src/store.cc
Use the new Cache Manager registration API for the newly added /dev/poll code.
[thirdparty/squid.git] / src / store.cc
CommitLineData
164f7660 1
30a4f2a8 2/*
262a0e14 3 * $Id$
30a4f2a8 4 *
8638fc66 5 * DEBUG: section 20 Storage Manager
30a4f2a8 6 * AUTHOR: Harvest Derived
7 *
2b6662ba 8 * SQUID Web Proxy Cache http://www.squid-cache.org/
e25c139f 9 * ----------------------------------------------------------
30a4f2a8 10 *
2b6662ba 11 * Squid is the result of efforts by numerous individuals from
12 * the Internet community; see the CONTRIBUTORS file for full
13 * details. Many organizations have provided support for Squid's
14 * development; see the SPONSORS file for full details. Squid is
15 * Copyrighted (C) 2001 by the Regents of the University of
16 * California; see the COPYRIGHT file for full details. Squid
17 * incorporates software developed and/or copyrighted by other
18 * sources; see the CREDITS file for full details.
30a4f2a8 19 *
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
26ac0430 24 *
30a4f2a8 25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
26ac0430 29 *
30a4f2a8 30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
cbdec147 32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
e25c139f 33 *
c943f331 34 */
090089c4 35
f09f5b26 36#include "squid.h"
a553a5a3 37#include "event.h"
04f55905 38#include "fde.h"
e6ccf245 39#include "Store.h"
8822ebee 40#include "mgr/Registration.h"
e6ccf245 41#include "StoreClient.h"
528b2c61 42#include "stmem.h"
43#include "HttpReply.h"
44#include "HttpRequest.h"
45#include "MemObject.h"
46#include "mem_node.h"
47#include "StoreMeta.h"
d3b3ab85 48#include "SwapDir.h"
b67e2c8c 49#if DELAY_POOLS
50#include "DelayPools.h"
51#endif
91caca83 52#include "Stack.h"
985c86bc 53#include "SquidTime.h"
4b981814 54#include "swap_log_op.h"
8822ebee 55#include "mgr/StoreIoAction.h"
090089c4 56
528b2c61 57static STMCB storeWriteComplete;
58
090089c4 59#define REBUILD_TIMESTAMP_DELTA_MAX 2
227fbb74 60
c21ad0f5 61#define STORE_IN_MEM_BUCKETS (229)
090089c4 62
4b981814
AJ
63
64/** \todo Convert these string constants to enum string-arrays generated */
65
26ac0430
AJ
66const char *memStatusStr[] = {
67 "NOT_IN_MEMORY",
68 "IN_MEMORY"
69};
70
71const char *pingStatusStr[] = {
72 "PING_NONE",
73 "PING_WAITING",
74 "PING_DONE"
75};
76
77const char *storeStatusStr[] = {
78 "STORE_OK",
79 "STORE_PENDING"
80};
81
82const char *swapStatusStr[] = {
83 "SWAPOUT_NONE",
84 "SWAPOUT_WRITING",
85 "SWAPOUT_DONE"
86};
9dfb6c1c 87
25b6a907 88
89/*
90 * This defines an repl type
91 */
92
93typedef struct _storerepl_entry storerepl_entry_t;
94
26ac0430 95struct _storerepl_entry {
25b6a907 96 const char *typestr;
97 REMOVALPOLICYCREATE *create;
98};
99
100static storerepl_entry_t *storerepl_list = NULL;
101
102
e3ef2b09 103/*
104 * local function prototypes
105 */
007b8be4 106static int getKeyCounter(void);
8423ff74 107static OBJH storeCheckCachableStats;
e42d5181 108static EVH storeLateRelease;
a21fbb54 109
e3ef2b09 110/*
111 * local variables
112 */
91caca83 113static Stack<StoreEntry*> LateReleaseStack;
04eb0689 114MemAllocator *StoreEntry::pool = NULL;
e6ccf245 115
c8f4eac4 116StorePointer Store::CurrentRoot = NULL;
117
118void
119Store::Root(Store * aRoot)
120{
121 CurrentRoot = aRoot;
122}
123
124void
125Store::Root(StorePointer aRoot)
126{
127 Root(aRoot.getRaw());
128}
129
130void
131Store::Stats(StoreEntry * output)
132{
133 assert (output);
134 Root().stat(*output);
135}
136
137void
138Store::create()
139{}
140
141void
142Store::diskFull()
143{}
144
145void
146Store::sync()
147{}
148
149void
150Store::unlink (StoreEntry &anEntry)
151{
152 fatal("Store::unlink on invalid Store\n");
153}
154
e6ccf245 155void *
3b13a8fd 156StoreEntry::operator new (size_t bytecount)
e6ccf245 157{
3b13a8fd 158 assert (bytecount == sizeof (StoreEntry));
62e76326 159
e6ccf245 160 if (!pool) {
04eb0689 161 pool = memPoolCreate ("StoreEntry", bytecount);
b001e822 162 pool->setChunkSize(2048 * 1024);
e6ccf245 163 }
62e76326 164
b001e822 165 return pool->alloc();
e6ccf245 166}
167
168void
3b13a8fd 169StoreEntry::operator delete (void *address)
e6ccf245 170{
dc47f531 171 pool->freeOne(address);
e6ccf245 172}
173
5ed72359 174void
175StoreEntry::makePublic()
176{
177 /* This object can be cached for a long time */
178
179 if (EBIT_TEST(flags, ENTRY_CACHABLE))
d88e3c49 180 setPublicKey();
5ed72359 181}
182
183void
184StoreEntry::makePrivate()
185{
186 /* This object should never be cached at all */
d88e3c49 187 expireNow();
188 releaseRequest(); /* delete object when not used */
189 /* releaseRequest clears ENTRY_CACHABLE flag */
5ed72359 190}
191
192void
193StoreEntry::cacheNegatively()
194{
195 /* This object may be negatively cached */
d88e3c49 196 negativeCache();
5ed72359 197
198 if (EBIT_TEST(flags, ENTRY_CACHABLE))
d88e3c49 199 setPublicKey();
5ed72359 200}
201
e6ccf245 202size_t
3b13a8fd 203StoreEntry::inUseCount()
e6ccf245 204{
205 if (!pool)
62e76326 206 return 0;
9f9e06f3 207 return pool->getInUseCount();
e6ccf245 208}
209
332dafa2 210const char *
3b13a8fd 211StoreEntry::getMD5Text() const
332dafa2 212{
213 return storeKeyText((const cache_key *)key);
214}
215
a46d2c0e 216#include "comm.h"
217
218void
219StoreEntry::DeferReader(void *theContext, CommRead const &aRead)
e6ccf245 220{
a46d2c0e 221 StoreEntry *anEntry = (StoreEntry *)theContext;
222 anEntry->delayAwareRead(aRead.fd,
223 aRead.buf,
224 aRead.len,
65517dc8 225 aRead.callback);
e6ccf245 226}
66cedb85 227
a46d2c0e 228void
65517dc8 229StoreEntry::delayAwareRead(int fd, char *buf, int len, AsyncCall::Pointer callback)
a46d2c0e 230{
231 size_t amountToRead = bytesWanted(Range<size_t>(0, len));
232 /* sketch: readdeferer* = getdeferer.
65517dc8 233 * ->deferRead (fd, buf, len, callback, DelayAwareRead, this)
a46d2c0e 234 */
235
236 if (amountToRead == 0) {
237 assert (mem_obj);
238 /* read ahead limit */
239 /* Perhaps these two calls should both live in MemObject */
a5f42284 240#if DELAY_POOLS
a46d2c0e 241
242 if (!mem_obj->readAheadPolicyCanRead()) {
a5f42284 243#endif
65517dc8 244 mem_obj->delayRead(DeferredRead(DeferReader, this, CommRead(fd, buf, len, callback)));
a46d2c0e 245 return;
a5f42284 246#if DELAY_POOLS
247
a46d2c0e 248 }
249
250 /* delay id limit */
65517dc8 251 mem_obj->mostBytesAllowed().delayRead(DeferredRead(DeferReader, this, CommRead(fd, buf, len, callback)));
a46d2c0e 252
253 return;
a5f42284 254
255#endif
256
a46d2c0e 257 }
258
65517dc8 259 comm_read(fd, buf, amountToRead, callback);
a46d2c0e 260}
261
262size_t
263StoreEntry::bytesWanted (Range<size_t> const aRange) const
528b2c61 264{
a46d2c0e 265 assert (aRange.size());
62e76326 266
528b2c61 267 if (mem_obj == NULL)
a46d2c0e 268 return aRange.end - 1;
62e76326 269
bc87dc25 270#if URL_CHECKSUM_DEBUG
62e76326 271
528b2c61 272 mem_obj->checkUrlChecksum();
62e76326 273
528b2c61 274#endif
62e76326 275
a46d2c0e 276 /* Always read *something* here - we haven't got the header yet */
277 if (EBIT_TEST(flags, ENTRY_FWD_HDR_WAIT))
278 return aRange.end - 1;
62e76326 279
a46d2c0e 280 if (!mem_obj->readAheadPolicyCanRead())
281 return 0;
62e76326 282
a46d2c0e 283 return mem_obj->mostBytesWanted(aRange.end - 1);
284}
62e76326 285
a46d2c0e 286bool
287StoreEntry::checkDeferRead(int fd) const
288{
289 return (bytesWanted(Range<size_t>(0,INT_MAX)) == 0);
290}
62e76326 291
a46d2c0e 292void
293StoreEntry::setNoDelay (bool const newValue)
294{
295 if (mem_obj)
296 mem_obj->setNoDelay(newValue);
528b2c61 297}
bc87dc25 298
528b2c61 299store_client_t
300StoreEntry::storeClientType() const
227fbb74 301{
7d31d5fa 302 /* The needed offset isn't in memory
303 * XXX TODO: this is wrong for range requests
304 * as the needed offset may *not* be 0, AND
305 * offset 0 in the memory object is the HTTP headers.
306 */
307
528b2c61 308 if (mem_obj->inmem_lo)
62e76326 309 return STORE_DISK_CLIENT;
310
528b2c61 311 if (EBIT_TEST(flags, ENTRY_ABORTED)) {
62e76326 312 /* I don't think we should be adding clients to aborted entries */
bf8fe701 313 debugs(20, 1, "storeClientType: adding to ENTRY_ABORTED entry");
62e76326 314 return STORE_MEM_CLIENT;
528b2c61 315 }
62e76326 316
528b2c61 317 if (store_status == STORE_OK) {
7d31d5fa 318 /* the object has completed. */
319
344a9006 320 if (mem_obj->inmem_lo == 0 && !isEmpty()) {
47ce0a58 321 if (swap_status == SWAPOUT_DONE) {
e1381638
AJ
322 if (mem_obj->endOffset() == mem_obj->object_sz) {
323 /* hot object fully swapped in */
324 return STORE_MEM_CLIENT;
325 }
47ce0a58 326 } else {
e1381638
AJ
327 /* Memory-only, or currently being swapped out */
328 return STORE_MEM_CLIENT;
47ce0a58
HN
329 }
330 }
331 return STORE_DISK_CLIENT;
528b2c61 332 }
62e76326 333
528b2c61 334 /* here and past, entry is STORE_PENDING */
335 /*
336 * If this is the first client, let it be the mem client
337 */
338 if (mem_obj->nclients == 1)
62e76326 339 return STORE_MEM_CLIENT;
340
528b2c61 341 /*
342 * If there is no disk file to open yet, we must make this a
343 * mem client. If we can't open the swapin file before writing
344 * to the client, there is no guarantee that we will be able
345 * to open it later when we really need it.
346 */
347 if (swap_status == SWAPOUT_NONE)
62e76326 348 return STORE_MEM_CLIENT;
349
528b2c61 350 /*
351 * otherwise, make subsequent clients read from disk so they
352 * can not delay the first, and vice-versa.
353 */
354 return STORE_DISK_CLIENT;
227fbb74 355}
356
c8f4eac4 357StoreEntry::StoreEntry()
090089c4 358{
59dbbc5c 359 debugs(20, 3, HERE << "new StoreEntry " << this);
c8f4eac4 360 mem_obj = NULL;
62e76326 361
c8f4eac4 362 expires = lastmod = lastref = timestamp = -1;
62e76326 363
c8f4eac4 364 swap_filen = -1;
365 swap_dirn = -1;
366}
62e76326 367
d5f8d05f 368StoreEntry::StoreEntry(const char *aUrl, const char *aLogUrl)
c8f4eac4 369{
59dbbc5c 370 debugs(20, 3, HERE << "new StoreEntry " << this);
d5f8d05f 371 mem_obj = new MemObject(aUrl, aLogUrl);
62e76326 372
c8f4eac4 373 expires = lastmod = lastref = timestamp = -1;
374
375 swap_filen = -1;
376 swap_dirn = -1;
090089c4 377}
378
3900307b 379void
380StoreEntry::destroyMemObject()
090089c4 381{
3900307b 382 debugs(20, 3, HERE << "destroyMemObject " << mem_obj);
383 setMemStatus(NOT_IN_MEMORY);
384 MemObject *mem = mem_obj;
385 mem_obj = NULL;
528b2c61 386 delete mem;
090089c4 387}
388
c8f4eac4 389void
528b2c61 390destroyStoreEntry(void *data)
090089c4 391{
59dbbc5c 392 debugs(20, 3, HERE << "destroyStoreEntry: destroying " << data);
c8f4eac4 393 StoreEntry *e = static_cast<StoreEntry *>(static_cast<hash_link *>(data));
9e975e4e 394 assert(e != NULL);
62e76326 395
e6ccf245 396 if (e == NullStoreEntry::getInstance())
62e76326 397 return;
398
3900307b 399 e->destroyMemObject();
62e76326 400
3900307b 401 e->hashDelete();
62e76326 402
332dafa2 403 assert(e->key == NULL);
62e76326 404
e6ccf245 405 delete e;
227fbb74 406}
090089c4 407
090089c4 408/* ----- INTERFACE BETWEEN STORAGE MANAGER AND HASH TABLE FUNCTIONS --------- */
409
f09f5b26 410void
3900307b 411StoreEntry::hashInsert(const cache_key * someKey)
090089c4 412{
bf8fe701 413 debugs(20, 3, "StoreEntry::hashInsert: Inserting Entry " << this << " key '" << storeKeyText(someKey) << "'");
3900307b 414 key = storeKeyDup(someKey);
415 hash_join(store_table, this);
090089c4 416}
417
3900307b 418void
419StoreEntry::hashDelete()
090089c4 420{
3900307b 421 hash_remove_link(store_table, this);
422 storeKeyFree((const cache_key *)key);
423 key = NULL;
090089c4 424}
425
090089c4 426/* -------------------------------------------------------------------------- */
427
090089c4 428
429/* get rid of memory copy of the object */
d88e3c49 430void
431StoreEntry::purgeMem()
090089c4 432{
d88e3c49 433 if (mem_obj == NULL)
62e76326 434 return;
435
bf8fe701 436 debugs(20, 3, "StoreEntry::purgeMem: Freeing memory-copy of " << getMD5Text());
62e76326 437
3900307b 438 destroyMemObject();
62e76326 439
d88e3c49 440 if (swap_status != SWAPOUT_DONE)
441 release();
090089c4 442}
443
c8f4eac4 444/* RBC 20050104 this is wrong- memory ref counting
445 * is not at all equivalent to the store 'usage' concept
446 * which the replacement policies should be acting upon.
447 * specifically, object iteration within stores needs
448 * memory ref counting to prevent race conditions,
449 * but this should not influence store replacement.
450 */
6a566b9c 451void
c21ad0f5 452
453StoreEntry::lock()
6a566b9c 454{
c21ad0f5 455 lock_count++;
34266cde 456 debugs(20, 3, "StoreEntry::lock: key '" << getMD5Text() <<"' count=" <<
bf8fe701 457 lock_count );
c21ad0f5 458 lastref = squid_curtime;
459 Store::Root().reference(*this);
090089c4 460}
461
43ae1d95 462void
463StoreEntry::setReleaseFlag()
464{
465 if (EBIT_TEST(flags, RELEASE_REQUEST))
466 return;
467
bf8fe701 468 debugs(20, 3, "StoreEntry::setReleaseFlag: '" << getMD5Text() << "'");
43ae1d95 469
470 EBIT_SET(flags, RELEASE_REQUEST);
471}
472
b8d8561b 473void
d88e3c49 474StoreEntry::releaseRequest()
2285407f 475{
d88e3c49 476 if (EBIT_TEST(flags, RELEASE_REQUEST))
62e76326 477 return;
478
d88e3c49 479 setReleaseFlag();
62e76326 480
f3e570e9 481 /*
482 * Clear cachable flag here because we might get called before
483 * anyone else even looks at the cachability flag. Also, this
484 * prevents httpMakePublic from really setting a public key.
485 */
d88e3c49 486 EBIT_CLR(flags, ENTRY_CACHABLE);
62e76326 487
d88e3c49 488 setPrivateKey();
2285407f 489}
490
090089c4 491/* unlock object, return -1 if object get released after unlock
492 * otherwise lock_count */
b8d8561b 493int
c21ad0f5 494StoreEntry::unlock()
090089c4 495{
c21ad0f5 496 lock_count--;
bf8fe701 497 debugs(20, 3, "StoreEntry::unlock: key '" << getMD5Text() << "' count=" << lock_count);
62e76326 498
c21ad0f5 499 if (lock_count)
500 return (int) lock_count;
62e76326 501
c21ad0f5 502 if (store_status == STORE_PENDING)
503 setReleaseFlag();
62e76326 504
c21ad0f5 505 assert(storePendingNClients(this) == 0);
62e76326 506
c21ad0f5 507 if (EBIT_TEST(flags, RELEASE_REQUEST))
5f33b71d 508 this->release();
3900307b 509 else if (keepInMemory()) {
c21ad0f5 510 Store::Root().dereference(*this);
3900307b 511 setMemStatus(IN_MEMORY);
c21ad0f5 512 mem_obj->unlinkRequest();
66f07209 513 } else {
c21ad0f5 514 Store::Root().dereference(*this);
62e76326 515
c21ad0f5 516 if (EBIT_TEST(flags, KEY_PRIVATE))
bf8fe701 517 debugs(20, 1, "WARNING: " << __FILE__ << ":" << __LINE__ << ": found KEY_PRIVATE");
42fc2858 518
d88e3c49 519 /* StoreEntry::purgeMem may free e */
520 purgeMem();
66f07209 521 }
62e76326 522
6c895381 523 return 0;
090089c4 524}
525
e6ccf245 526void
60745f24 527StoreEntry::getPublicByRequestMethod (StoreClient *aClient, HttpRequest * request, const HttpRequestMethod& method)
e6ccf245 528{
529 assert (aClient);
3b13a8fd 530 StoreEntry *result = storeGetPublicByRequestMethod( request, method);
62e76326 531
e6ccf245 532 if (!result)
62e76326 533 aClient->created (NullStoreEntry::getInstance());
e6ccf245 534 else
62e76326 535 aClient->created (result);
e6ccf245 536}
537
538void
190154cf 539StoreEntry::getPublicByRequest (StoreClient *aClient, HttpRequest * request)
e6ccf245 540{
541 assert (aClient);
3b13a8fd 542 StoreEntry *result = storeGetPublicByRequest (request);
62e76326 543
e6ccf245 544 if (!result)
62e76326 545 result = NullStoreEntry::getInstance();
546
e6ccf245 547 aClient->created (result);
548}
549
550void
60745f24 551StoreEntry::getPublic (StoreClient *aClient, const char *uri, const HttpRequestMethod& method)
e6ccf245 552{
553 assert (aClient);
3b13a8fd 554 StoreEntry *result = storeGetPublic (uri, method);
62e76326 555
e6ccf245 556 if (!result)
62e76326 557 result = NullStoreEntry::getInstance();
558
e6ccf245 559 aClient->created (result);
560}
561
08e5d64f 562StoreEntry *
60745f24 563storeGetPublic(const char *uri, const HttpRequestMethod& method)
08e5d64f 564{
c8f4eac4 565 return Store::Root().get(storeKeyPublic(uri, method));
08e5d64f 566}
567
f66a9ef4 568StoreEntry *
60745f24 569storeGetPublicByRequestMethod(HttpRequest * req, const HttpRequestMethod& method)
f66a9ef4 570{
c8f4eac4 571 return Store::Root().get(storeKeyPublicByRequestMethod(req, method));
f66a9ef4 572}
573
574StoreEntry *
190154cf 575storeGetPublicByRequest(HttpRequest * req)
f66a9ef4 576{
577 StoreEntry *e = storeGetPublicByRequestMethod(req, req->method);
62e76326 578
f66a9ef4 579 if (e == NULL && req->method == METHOD_HEAD)
62e76326 580 /* We can generate a HEAD reply from a cached GET object */
581 e = storeGetPublicByRequestMethod(req, METHOD_GET);
582
f66a9ef4 583 return e;
584}
585
007b8be4 586static int
587getKeyCounter(void)
04e8dbaa 588{
007b8be4 589 static int key_counter = 0;
62e76326 590
007b8be4 591 if (++key_counter < 0)
62e76326 592 key_counter = 1;
593
007b8be4 594 return key_counter;
04e8dbaa 595}
596
c8f4eac4 597/* RBC 20050104 AFAICT this should become simpler:
598 * rather than reinserting with a special key it should be marked
599 * as 'released' and then cleaned up when refcounting indicates.
600 * the StoreHashIndex could well implement its 'released' in the
601 * current manner.
602 * Also, clean log writing should skip over ia,t
603 * Otherwise, we need a 'remove from the index but not the store
604 * concept'.
605 */
6c57e268 606void
d88e3c49 607StoreEntry::setPrivateKey()
227fbb74 608{
9fb13bb6 609 const cache_key *newkey;
62e76326 610
d88e3c49 611 if (key && EBIT_TEST(flags, KEY_PRIVATE))
c21ad0f5 612 return; /* is already private */
62e76326 613
d88e3c49 614 if (key) {
615 if (swap_filen > -1)
616 storeDirSwapLog(this, SWAP_LOG_DEL);
62e76326 617
3900307b 618 hashDelete();
b109de6b 619 }
62e76326 620
d88e3c49 621 if (mem_obj != NULL) {
622 mem_obj->id = getKeyCounter();
623 newkey = storeKeyPrivate(mem_obj->url, mem_obj->method, mem_obj->id);
9fb13bb6 624 } else {
62e76326 625 newkey = storeKeyPrivate("JUNK", METHOD_NONE, getKeyCounter());
9fb13bb6 626 }
62e76326 627
9fb13bb6 628 assert(hash_lookup(store_table, newkey) == NULL);
d88e3c49 629 EBIT_SET(flags, KEY_PRIVATE);
3900307b 630 hashInsert(newkey);
227fbb74 631}
632
b8d8561b 633void
d88e3c49 634StoreEntry::setPublicKey()
227fbb74 635{
6eb42cae 636 StoreEntry *e2 = NULL;
9fb13bb6 637 const cache_key *newkey;
62e76326 638
d88e3c49 639 if (key && !EBIT_TEST(flags, KEY_PRIVATE))
c21ad0f5 640 return; /* is already public */
62e76326 641
d88e3c49 642 assert(mem_obj);
62e76326 643
f3e570e9 644 /*
645 * We can't make RELEASE_REQUEST objects public. Depending on
646 * when RELEASE_REQUEST gets set, we might not be swapping out
647 * the object. If we're not swapping out, then subsequent
648 * store clients won't be able to access object data which has
649 * been freed from memory.
d87ebd78 650 *
651 * If RELEASE_REQUEST is set, then ENTRY_CACHABLE should not
d88e3c49 652 * be set, and StoreEntry::setPublicKey() should not be called.
f3e570e9 653 */
6a566b9c 654#if MORE_DEBUG_OUTPUT
62e76326 655
d88e3c49 656 if (EBIT_TEST(flags, RELEASE_REQUEST))
308652fc 657 debugs(20, 1, "assertion failed: RELEASE key " << key << ", url " << mem_obj->url);
62e76326 658
2b906e48 659#endif
62e76326 660
d88e3c49 661 assert(!EBIT_TEST(flags, RELEASE_REQUEST));
62e76326 662
d88e3c49 663 if (mem_obj->request) {
664 HttpRequest *request = mem_obj->request;
62e76326 665
d88e3c49 666 if (!mem_obj->vary_headers) {
62e76326 667 /* First handle the case where the object no longer varies */
668 safe_free(request->vary_headers);
669 } else {
d88e3c49 670 if (request->vary_headers && strcmp(request->vary_headers, mem_obj->vary_headers) != 0) {
62e76326 671 /* Oops.. the variance has changed. Kill the base object
672 * to record the new variance key
673 */
c21ad0f5 674 safe_free(request->vary_headers); /* free old "bad" variance key */
d88e3c49 675 StoreEntry *pe = storeGetPublic(mem_obj->url, mem_obj->method);
62e76326 676
677 if (pe)
5f33b71d 678 pe->release();
62e76326 679 }
680
681 /* Make sure the request knows the variance status */
682 if (!request->vary_headers) {
d88e3c49 683 const char *vary = httpMakeVaryMark(request, mem_obj->getReply());
62e76326 684
685 if (vary)
686 request->vary_headers = xstrdup(vary);
687 }
688 }
689
d88e3c49 690 if (mem_obj->vary_headers && !storeGetPublic(mem_obj->url, mem_obj->method)) {
62e76326 691 /* Create "vary" base object */
30abd221 692 String vary;
d88e3c49 693 StoreEntry *pe = storeCreateEntry(mem_obj->url, mem_obj->log_url, request->flags, request->method);
62e76326 694 /* We are allowed to do this typecast */
eab8dcfa 695 HttpReply *rep = new HttpReply;
11992b6f 696 rep->setHeaders(HTTP_OK, "Internal marker object", "x-squid-internal/vary", -1, -1, squid_curtime + 100000);
d88e3c49 697 vary = mem_obj->getReply()->header.getList(HDR_VARY);
62e76326 698
30abd221 699 if (vary.size()) {
62e76326 700 /* Again, we own this structure layout */
b4f2886c 701 rep->header.putStr(HDR_VARY, vary.termedBuf());
30abd221 702 vary.clean();
62e76326 703 }
704
f66a9ef4 705#if X_ACCELERATOR_VARY
d88e3c49 706 vary = mem_obj->getReply()->header.getList(HDR_X_ACCELERATOR_VARY);
62e76326 707
b4197865 708 if (vary.defined()) {
62e76326 709 /* Again, we own this structure layout */
b4197865 710 rep->header.putStr(HDR_X_ACCELERATOR_VARY, vary.termedBuf());
30abd221 711 vary.clean();
62e76326 712 }
713
f66a9ef4 714#endif
eab8dcfa 715 pe->replaceHttpReply(rep);
62e76326 716
3900307b 717 pe->timestampsSet();
ba27c9a0 718
eab8dcfa 719 pe->makePublic();
62e76326 720
62e76326 721 pe->complete();
eab8dcfa 722
97b5e68f 723 pe->unlock();
62e76326 724 }
725
d88e3c49 726 newkey = storeKeyPublicByRequest(mem_obj->request);
f66a9ef4 727 } else
d88e3c49 728 newkey = storeKeyPublic(mem_obj->url, mem_obj->method);
62e76326 729
9fb13bb6 730 if ((e2 = (StoreEntry *) hash_lookup(store_table, newkey))) {
bf8fe701 731 debugs(20, 3, "StoreEntry::setPublicKey: Making old '" << mem_obj->url << "' private.");
d88e3c49 732 e2->setPrivateKey();
5f33b71d 733 e2->release();
62e76326 734
d88e3c49 735 if (mem_obj->request)
736 newkey = storeKeyPublicByRequest(mem_obj->request);
62e76326 737 else
d88e3c49 738 newkey = storeKeyPublic(mem_obj->url, mem_obj->method);
6eb42cae 739 }
62e76326 740
d88e3c49 741 if (key)
3900307b 742 hashDelete();
62e76326 743
d88e3c49 744 EBIT_CLR(flags, KEY_PRIVATE);
62e76326 745
3900307b 746 hashInsert(newkey);
62e76326 747
d88e3c49 748 if (swap_filen > -1)
749 storeDirSwapLog(this, SWAP_LOG_ADD);
227fbb74 750}
751
b8d8561b 752StoreEntry *
60745f24 753storeCreateEntry(const char *url, const char *log_url, request_flags flags, const HttpRequestMethod& method)
090089c4 754{
090089c4 755 StoreEntry *e = NULL;
30a4f2a8 756 MemObject *mem = NULL;
bf8fe701 757 debugs(20, 3, "storeCreateEntry: '" << url << "'");
090089c4 758
c8f4eac4 759 e = new StoreEntry(url, log_url);
c21ad0f5 760 e->lock_count = 1; /* Note lock here w/o calling storeLock() */
30a4f2a8 761 mem = e->mem_obj;
2ac237e2 762 mem->method = method;
62e76326 763
92695e5e 764 if (neighbors_do_private_keys || !flags.hierarchical)
d88e3c49 765 e->setPrivateKey();
86101e40 766 else
d88e3c49 767 e->setPublicKey();
62e76326 768
92695e5e 769 if (flags.cachable) {
62e76326 770 EBIT_SET(e->flags, ENTRY_CACHABLE);
771 EBIT_CLR(e->flags, RELEASE_REQUEST);
090089c4 772 } else {
d88e3c49 773 /* StoreEntry::releaseRequest() clears ENTRY_CACHABLE */
774 e->releaseRequest();
090089c4 775 }
62e76326 776
234967c9 777 e->store_status = STORE_PENDING;
3900307b 778 e->setMemStatus(NOT_IN_MEMORY);
8350fe9b 779 e->swap_status = SWAPOUT_NONE;
cd748f27 780 e->swap_filen = -1;
781 e->swap_dirn = -1;
090089c4 782 e->refcount = 0;
b8de7ebe 783 e->lastref = squid_curtime;
3900307b 784 e->timestamp = -1; /* set in StoreEntry::timestampsSet() */
30a4f2a8 785 e->ping_status = PING_NONE;
d46a87a8 786 EBIT_SET(e->flags, ENTRY_VALIDATED);
090089c4 787 return e;
788}
789
6eb42cae 790/* Mark object as expired */
b8d8561b 791void
d88e3c49 792StoreEntry::expireNow()
9174e204 793{
bf8fe701 794 debugs(20, 3, "StoreEntry::expireNow: '" << getMD5Text() << "'");
d88e3c49 795 expires = squid_curtime;
9174e204 796}
797
528b2c61 798void
799storeWriteComplete (void *data, StoreIOBuffer wroteBuffer)
800{
1d5161bd 801 PROF_start(storeWriteComplete);
528b2c61 802 StoreEntry *e = (StoreEntry *)data;
62e76326 803
1d5161bd 804 if (EBIT_TEST(e->flags, DELAY_SENDING)) {
805 PROF_stop(storeWriteComplete);
62e76326 806 return;
1d5161bd 807 }
62e76326 808
d88e3c49 809 e->invokeHandlers();
1d5161bd 810 PROF_stop(storeWriteComplete);
528b2c61 811}
812
813void
814StoreEntry::write (StoreIOBuffer writeBuffer)
815{
816 assert(mem_obj != NULL);
817 assert(writeBuffer.length >= 0);
818 /* This assert will change when we teach the store to update */
d2639a5b 819 PROF_start(StoreEntry_write);
528b2c61 820 assert(store_status == STORE_PENDING);
62e76326 821
26ac0430 822 if (!writeBuffer.length) {
2324cda2 823 /* the headers are received already, but we have not received
43785884 824 * any body data. There are BROKEN abuses of HTTP which require
825 * the headers to be passed along before any body data - see
826 * http://developer.apple.com/documentation/QuickTime/QTSS/Concepts/chapter_2_section_14.html
827 * for an example of such bad behaviour. To accomodate this, if
828 * we have a empty write arrive, we flush to our clients.
829 * -RBC 20060903
830 */
d2639a5b 831 PROF_stop(StoreEntry_write);
d88e3c49 832 invokeHandlers();
62e76326 833 return;
26ac0430 834 }
62e76326 835
d2639a5b 836 debugs(20, 5, "storeWrite: writing " << writeBuffer.length << " bytes for '" << getMD5Text() << "'");
837 PROF_stop(StoreEntry_write);
528b2c61 838 storeGetMemSpace(writeBuffer.length);
839 mem_obj->write (writeBuffer, storeWriteComplete, this);
840}
841
c21ad0f5 842/* Append incoming data from a primary server to an entry. */
843void
844StoreEntry::append(char const *buf, int len)
845{
846 assert(mem_obj != NULL);
3a1c3e2f 847 assert(len >= 0);
c21ad0f5 848 assert(store_status == STORE_PENDING);
528b2c61 849
850 StoreIOBuffer tempBuffer;
851 tempBuffer.data = (char *)buf;
852 tempBuffer.length = len;
aa18a4ca 853 /*
854 * XXX sigh, offset might be < 0 here, but it gets "corrected"
855 * later. This offset crap is such a mess.
856 */
c21ad0f5 857 tempBuffer.offset = mem_obj->endOffset() - (getReply() ? getReply()->hdr_sz : 0);
858 write(tempBuffer);
090089c4 859}
860
c21ad0f5 861
b8d8561b 862void
fe4e214f 863storeAppendPrintf(StoreEntry * e, const char *fmt,...)
15c05bb0 864{
62d32805 865 va_list args;
866 va_start(args, fmt);
62e76326 867
cb69b4c7 868 storeAppendVPrintf(e, fmt, args);
869 va_end(args);
870}
871
872/* used be storeAppendPrintf and Packer */
873void
874storeAppendVPrintf(StoreEntry * e, const char *fmt, va_list vargs)
875{
876 LOCAL_ARRAY(char, buf, 4096);
15c05bb0 877 buf[0] = '\0';
cb69b4c7 878 vsnprintf(buf, 4096, fmt, vargs);
3900307b 879 e->append(buf, strlen(buf));
c30c5a73 880}
881
26ac0430 882struct _store_check_cachable_hist {
62e76326 883
26ac0430 884 struct {
62e76326 885 int non_get;
886 int not_entry_cachable;
887 int wrong_content_length;
888 int negative_cached;
889 int too_big;
890 int too_small;
891 int private_key;
892 int too_many_open_files;
893 int too_many_open_fds;
2fadd50d 894 } no;
62e76326 895
26ac0430 896 struct {
62e76326 897 int Default;
2fadd50d
HN
898 } yes;
899} store_check_cachable_hist;
8423ff74 900
c47511fd 901int
902storeTooManyDiskFilesOpen(void)
903{
904 if (Config.max_open_disk_fds == 0)
62e76326 905 return 0;
906
83a29c95 907 if (store_open_disk_fd > Config.max_open_disk_fds)
62e76326 908 return 1;
909
c47511fd 910 return 0;
911}
912
3900307b 913int
914StoreEntry::checkTooSmall()
d20b1cd0 915{
3900307b 916 if (EBIT_TEST(flags, ENTRY_SPECIAL))
62e76326 917 return 0;
918
3900307b 919 if (STORE_OK == store_status)
920 if (mem_obj->object_sz < 0 ||
26ac0430 921 mem_obj->object_sz < Config.Store.minObjectSize)
62e76326 922 return 1;
3900307b 923 if (getReply()->content_length > -1)
47f6e231 924 if (getReply()->content_length < Config.Store.minObjectSize)
62e76326 925 return 1;
d20b1cd0 926 return 0;
927}
928
f09f5b26 929int
3900307b 930StoreEntry::checkCachable()
6602e70e 931{
2ac237e2 932#if CACHE_ALL_METHODS
62e76326 933
3900307b 934 if (mem_obj->method != METHOD_GET) {
bf8fe701 935 debugs(20, 2, "StoreEntry::checkCachable: NO: non-GET method");
62e76326 936 store_check_cachable_hist.no.non_get++;
2ac237e2 937 } else
938#endif
3900307b 939 if (store_status == STORE_OK && EBIT_TEST(flags, ENTRY_BAD_LENGTH)) {
bf8fe701 940 debugs(20, 2, "StoreEntry::checkCachable: NO: wrong content-length");
62e76326 941 store_check_cachable_hist.no.wrong_content_length++;
3900307b 942 } else if (!EBIT_TEST(flags, ENTRY_CACHABLE)) {
bf8fe701 943 debugs(20, 2, "StoreEntry::checkCachable: NO: not cachable");
be3b12f0 944 store_check_cachable_hist.no.not_entry_cachable++;
3900307b 945 } else if (EBIT_TEST(flags, ENTRY_NEGCACHED)) {
bf8fe701 946 debugs(20, 3, "StoreEntry::checkCachable: NO: negative cached");
62e76326 947 store_check_cachable_hist.no.negative_cached++;
c21ad0f5 948 return 0; /* avoid release call below */
3900307b 949 } else if ((getReply()->content_length > 0 &&
47f6e231 950 getReply()->content_length
62e76326 951 > Config.Store.maxObjectSize) ||
47f6e231 952 mem_obj->endOffset() > Config.Store.maxObjectSize) {
bf8fe701 953 debugs(20, 2, "StoreEntry::checkCachable: NO: too big");
62e76326 954 store_check_cachable_hist.no.too_big++;
47f6e231 955 } else if (getReply()->content_length > Config.Store.maxObjectSize) {
bf8fe701 956 debugs(20, 2, "StoreEntry::checkCachable: NO: too big");
62e76326 957 store_check_cachable_hist.no.too_big++;
3900307b 958 } else if (checkTooSmall()) {
bf8fe701 959 debugs(20, 2, "StoreEntry::checkCachable: NO: too small");
62e76326 960 store_check_cachable_hist.no.too_small++;
3900307b 961 } else if (EBIT_TEST(flags, KEY_PRIVATE)) {
bf8fe701 962 debugs(20, 3, "StoreEntry::checkCachable: NO: private key");
62e76326 963 store_check_cachable_hist.no.private_key++;
3900307b 964 } else if (swap_status != SWAPOUT_NONE) {
62e76326 965 /*
966 * here we checked the swap_status because the remaining
967 * cases are only relevant only if we haven't started swapping
968 * out the object yet.
969 */
970 return 1;
971 } else if (storeTooManyDiskFilesOpen()) {
bf8fe701 972 debugs(20, 2, "StoreEntry::checkCachable: NO: too many disk files open");
62e76326 973 store_check_cachable_hist.no.too_many_open_files++;
974 } else if (fdNFree() < RESERVED_FD) {
bf8fe701 975 debugs(20, 2, "StoreEntry::checkCachable: NO: too many FD's open");
62e76326 976 store_check_cachable_hist.no.too_many_open_fds++;
977 } else {
978 store_check_cachable_hist.yes.Default++;
979 return 1;
980 }
981
3900307b 982 releaseRequest();
d88e3c49 983 /* StoreEntry::releaseRequest() cleared ENTRY_CACHABLE */
6602e70e 984 return 0;
985}
986
3900307b 987void
988storeCheckCachableStats(StoreEntry *sentry)
8423ff74 989{
c40acff3 990 storeAppendPrintf(sentry, "Category\t Count\n");
991
a0cd8f99 992#if CACHE_ALL_METHODS
62e76326 993
8423ff74 994 storeAppendPrintf(sentry, "no.non_get\t%d\n",
62e76326 995 store_check_cachable_hist.no.non_get);
a0cd8f99 996#endif
62e76326 997
8423ff74 998 storeAppendPrintf(sentry, "no.not_entry_cachable\t%d\n",
62e76326 999 store_check_cachable_hist.no.not_entry_cachable);
8423ff74 1000 storeAppendPrintf(sentry, "no.wrong_content_length\t%d\n",
62e76326 1001 store_check_cachable_hist.no.wrong_content_length);
8423ff74 1002 storeAppendPrintf(sentry, "no.negative_cached\t%d\n",
62e76326 1003 store_check_cachable_hist.no.negative_cached);
8423ff74 1004 storeAppendPrintf(sentry, "no.too_big\t%d\n",
62e76326 1005 store_check_cachable_hist.no.too_big);
d20b1cd0 1006 storeAppendPrintf(sentry, "no.too_small\t%d\n",
62e76326 1007 store_check_cachable_hist.no.too_small);
8423ff74 1008 storeAppendPrintf(sentry, "no.private_key\t%d\n",
62e76326 1009 store_check_cachable_hist.no.private_key);
c5f627c2 1010 storeAppendPrintf(sentry, "no.too_many_open_files\t%d\n",
62e76326 1011 store_check_cachable_hist.no.too_many_open_files);
59ffcdf8 1012 storeAppendPrintf(sentry, "no.too_many_open_fds\t%d\n",
62e76326 1013 store_check_cachable_hist.no.too_many_open_fds);
8423ff74 1014 storeAppendPrintf(sentry, "yes.default\t%d\n",
62e76326 1015 store_check_cachable_hist.yes.Default);
8423ff74 1016}
1017
b8d8561b 1018void
528b2c61 1019StoreEntry::complete()
090089c4 1020{
bf8fe701 1021 debugs(20, 3, "storeComplete: '" << getMD5Text() << "'");
62e76326 1022
528b2c61 1023 if (store_status != STORE_PENDING) {
62e76326 1024 /*
1025 * if we're not STORE_PENDING, then probably we got aborted
1026 * and there should be NO clients on this entry
1027 */
1028 assert(EBIT_TEST(flags, ENTRY_ABORTED));
1029 assert(mem_obj->nclients == 0);
1030 return;
b6403fac 1031 }
62e76326 1032
7d31d5fa 1033 /* This is suspect: mem obj offsets include the headers. do we adjust for that
1034 * in use of object_sz?
1035 */
528b2c61 1036 mem_obj->object_sz = mem_obj->endOffset();
7d31d5fa 1037
528b2c61 1038 store_status = STORE_OK;
7d31d5fa 1039
528b2c61 1040 assert(mem_status == NOT_IN_MEMORY);
62e76326 1041
528b2c61 1042 if (!validLength()) {
62e76326 1043 EBIT_SET(flags, ENTRY_BAD_LENGTH);
d88e3c49 1044 releaseRequest();
41587298 1045 }
62e76326 1046
6cfa8966 1047#if USE_CACHE_DIGESTS
528b2c61 1048 if (mem_obj->request)
62e76326 1049 mem_obj->request->hier.store_complete_stop = current_time;
1050
39edba21 1051#endif
d20b1cd0 1052 /*
d88e3c49 1053 * We used to call invokeHandlers, then storeSwapOut. However,
d20b1cd0 1054 * Madhukar Reddy <myreddy@persistence.com> reported that
1055 * responses without content length would sometimes get released
1056 * in client_side, thinking that the response is incomplete.
1057 */
d88e3c49 1058 invokeHandlers();
7e3e1d01 1059}
1060
090089c4 1061/*
474cac1b 1062 * Someone wants to abort this transfer. Set the reason in the
1063 * request structure, call the server-side callback and mark the
2b906e48 1064 * entry for releasing
090089c4 1065 */
b8d8561b 1066void
bfb55b6f 1067StoreEntry::abort()
090089c4 1068{
528b2c61 1069 statCounter.aborted_requests++;
bfb55b6f 1070 assert(store_status == STORE_PENDING);
1071 assert(mem_obj != NULL);
bf8fe701 1072 debugs(20, 6, "storeAbort: " << getMD5Text());
34266cde 1073
ac9cc053 1074 lock(); /* lock while aborting */
d88e3c49 1075 negativeCache();
34266cde 1076
d88e3c49 1077 releaseRequest();
34266cde 1078
bfb55b6f 1079 EBIT_SET(flags, ENTRY_ABORTED);
34266cde 1080
3900307b 1081 setMemStatus(NOT_IN_MEMORY);
34266cde 1082
bfb55b6f 1083 store_status = STORE_OK;
34266cde 1084
496e1d76 1085 /*
1086 * We assign an object length here. The only other place we assign
1087 * the object length is in storeComplete()
1088 */
528b2c61 1089 /* RBC: What do we need an object length for? we've just aborted the
1090 * request, the request is private and negatively cached. Surely
1091 * the object length is inappropriate to set.
1092 */
bfb55b6f 1093 mem_obj->object_sz = mem_obj->endOffset();
34266cde 1094
474cac1b 1095 /* Notify the server side */
62e76326 1096
8ea67c2b 1097 /*
1098 * DPW 2007-05-07
1099 * Should we check abort.data for validity?
1100 */
bfb55b6f 1101 if (mem_obj->abort.callback) {
26ac0430
AJ
1102 if (!cbdataReferenceValid(mem_obj->abort.data))
1103 debugs(20,1,HERE << "queueing event when abort.data is not valid");
bfb55b6f 1104 eventAdd("mem_obj->abort.callback",
1105 mem_obj->abort.callback,
1106 mem_obj->abort.data,
62e76326 1107 0.0,
8ea67c2b 1108 true);
26ac0430 1109 unregisterAbort();
bfcaf585 1110 }
62e76326 1111
1112 /* XXX Should we reverse these two, so that there is no
26ac0430 1113 * unneeded disk swapping triggered?
528b2c61 1114 */
474cac1b 1115 /* Notify the client side */
d88e3c49 1116 invokeHandlers();
62e76326 1117
cd748f27 1118 /* Close any swapout file */
bfb55b6f 1119 swapOutFileClose();
62e76326 1120
bfb55b6f 1121 unlock(); /* unlock */
090089c4 1122}
1123
6d3c2758
HN
1124/**
1125 * Clear Memory storage to accommodate the given object len
1126 */
1127void
d4432957 1128storeGetMemSpace(int size)
090089c4 1129{
1d5161bd 1130 PROF_start(storeGetMemSpace);
b32508fb 1131 StoreEntry *e = NULL;
20cba4b4 1132 int released = 0;
b32508fb 1133 static time_t last_check = 0;
528b2c61 1134 size_t pages_needed;
6a566b9c 1135 RemovalPurgeWalker *walker;
62e76326 1136
1d5161bd 1137 if (squid_curtime == last_check) {
1138 PROF_stop(storeGetMemSpace);
62e76326 1139 return;
1d5161bd 1140 }
62e76326 1141
b32508fb 1142 last_check = squid_curtime;
62e76326 1143
e954773d 1144 pages_needed = (size / SM_PAGE_SIZE) + 1;
62e76326 1145
1d5161bd 1146 if (mem_node::InUseCount() + pages_needed < store_pages_max) {
1147 PROF_stop(storeGetMemSpace);
62e76326 1148 return;
1d5161bd 1149 }
62e76326 1150
e4049756 1151 debugs(20, 2, "storeGetMemSpace: Starting, need " << pages_needed <<
1152 " pages");
62e76326 1153
6a566b9c 1154 /* XXX what to set as max_scan here? */
1155 walker = mem_policy->PurgeInit(mem_policy, 100000);
62e76326 1156
c1dd71ae 1157 while ((e = walker->Next(walker))) {
d88e3c49 1158 e->purgeMem();
62e76326 1159 released++;
1160
1161 if (mem_node::InUseCount() + pages_needed < store_pages_max)
1162 break;
8350fe9b 1163 }
62e76326 1164
6a566b9c 1165 walker->Done(walker);
bf8fe701 1166 debugs(20, 3, "storeGetMemSpace stats:");
1167 debugs(20, 3, " " << std::setw(6) << hot_obj_count << " HOT objects");
1168 debugs(20, 3, " " << std::setw(6) << released << " were released");
1d5161bd 1169 PROF_stop(storeGetMemSpace);
090089c4 1170}
1171
c8f4eac4 1172
1173/* thunk through to Store::Root().maintain(). Note that this would be better still
26ac0430
AJ
1174 * if registered against the root store itself, but that requires more complex
1175 * update logic - bigger fish to fry first. Long term each store when
c8f4eac4 1176 * it becomes active will self register
1177 */
1178void
1179Store::Maintain(void *notused)
1180{
1181 Store::Root().maintain();
1182
1183 /* Reregister a maintain event .. */
1184 eventAdd("MaintainSwapSpace", Maintain, NULL, 1.0, 1);
1185
1186}
1187
090089c4 1188/* The maximum objects to scan for maintain storage space */
c21ad0f5 1189#define MAINTAIN_MAX_SCAN 1024
1190#define MAINTAIN_MAX_REMOVE 64
090089c4 1191
2b906e48 1192/*
fcefe642 1193 * This routine is to be called by main loop in main.c.
1194 * It removes expired objects on only one bucket for each time called.
fcefe642 1195 *
1196 * This should get called 1/s from main().
1197 */
679ac4f0 1198void
c8f4eac4 1199StoreController::maintain()
090089c4 1200{
6a566b9c 1201 static time_t last_warn_time = 0;
cd748f27 1202
88bfe092 1203 PROF_start(storeMaintainSwapSpace);
c8f4eac4 1204 swapDir->maintain();
62e76326 1205
c8f4eac4 1206 /* this should be emitted by the oversize dir, not globally */
62e76326 1207
c8f4eac4 1208 if (store_swap_size > Store::Root().maxSize()) {
62e76326 1209 if (squid_curtime - last_warn_time > 10) {
c8f4eac4 1210 debugs(20, 0, "WARNING: Disk space over limit: " << store_swap_size << " KB > "
1211 << Store::Root().maxSize() << " KB");
62e76326 1212 last_warn_time = squid_curtime;
1213 }
6a566b9c 1214 }
62e76326 1215
88bfe092 1216 PROF_stop(storeMaintainSwapSpace);
090089c4 1217}
1218
090089c4 1219/* release an object from a cache */
6c78a099 1220void
5f33b71d 1221StoreEntry::release()
090089c4 1222{
88bfe092 1223 PROF_start(storeRelease);
bf8fe701 1224 debugs(20, 3, "storeRelease: Releasing: '" << getMD5Text() << "'");
090089c4 1225 /* If, for any reason we can't discard this object because of an
1226 * outstanding request, mark it for pending release */
62e76326 1227
3900307b 1228 if (locked()) {
d88e3c49 1229 expireNow();
bf8fe701 1230 debugs(20, 3, "storeRelease: Only setting RELEASE_REQUEST bit");
d88e3c49 1231 releaseRequest();
62e76326 1232 PROF_stop(storeRelease);
1233 return;
090089c4 1234 }
62e76326 1235
bef81ea5 1236 if (StoreController::store_dirs_rebuilding && swap_filen > -1) {
d88e3c49 1237 setPrivateKey();
62e76326 1238
5f33b71d 1239 if (mem_obj)
3900307b 1240 destroyMemObject();
62e76326 1241
5f33b71d 1242 if (swap_filen > -1) {
62e76326 1243 /*
34266cde 1244 * Fake a call to StoreEntry->lock() When rebuilding is done,
1245 * we'll just call StoreEntry->unlock() on these.
62e76326 1246 */
5f33b71d 1247 lock_count++;
1248 setReleaseFlag();
1249 LateReleaseStack.push_back(this);
62e76326 1250 PROF_stop(storeRelease);
1251 return;
1252 } else {
5f33b71d 1253 destroyStoreEntry(static_cast<hash_link *>(this));
62e76326 1254 }
43d9cf56 1255 }
62e76326 1256
5f33b71d 1257 storeLog(STORE_LOG_RELEASE, this);
62e76326 1258
5f33b71d 1259 if (swap_filen > -1) {
1260 unlink();
62e76326 1261
5f33b71d 1262 if (swap_status == SWAPOUT_DONE)
1263 if (EBIT_TEST(flags, ENTRY_VALIDATED))
1264 store()->updateSize(swap_file_sz, -1);
62e76326 1265
5f33b71d 1266 if (!EBIT_TEST(flags, KEY_PRIVATE))
1267 storeDirSwapLog(this, SWAP_LOG_DEL);
62e76326 1268
cd748f27 1269#if 0
62e76326 1270 /* From 2.4. I think we do this in storeUnlink? */
5f33b71d 1271 storeSwapFileNumberSet(this, -1);
62e76326 1272
cd748f27 1273#endif
62e76326 1274
090089c4 1275 }
62e76326 1276
3900307b 1277 setMemStatus(NOT_IN_MEMORY);
5f33b71d 1278 destroyStoreEntry(static_cast<hash_link *>(this));
88bfe092 1279 PROF_stop(storeRelease);
090089c4 1280}
1281
e42d5181 1282static void
1283storeLateRelease(void *unused)
1284{
1285 StoreEntry *e;
1286 int i;
1287 static int n = 0;
62e76326 1288
bef81ea5 1289 if (StoreController::store_dirs_rebuilding) {
62e76326 1290 eventAdd("storeLateRelease", storeLateRelease, NULL, 1.0, 1);
1291 return;
e42d5181 1292 }
62e76326 1293
e42d5181 1294 for (i = 0; i < 10; i++) {
91caca83 1295 e = LateReleaseStack.pop();
62e76326 1296
1297 if (e == NULL) {
1298 /* done! */
bf8fe701 1299 debugs(20, 1, "storeLateRelease: released " << n << " objects");
62e76326 1300 return;
1301 }
1302
97b5e68f 1303 e->unlock();
62e76326 1304 n++;
e42d5181 1305 }
62e76326 1306
e42d5181 1307 eventAdd("storeLateRelease", storeLateRelease, NULL, 0.0, 1);
1308}
1309
090089c4 1310/* return 1 if a store entry is locked */
cd748f27 1311int
3900307b 1312StoreEntry::locked() const
090089c4 1313{
3900307b 1314 if (lock_count)
62e76326 1315 return 1;
1316
3900307b 1317 if (swap_status == SWAPOUT_WRITING)
62e76326 1318 return 1;
1319
3900307b 1320 if (store_status == STORE_PENDING)
62e76326 1321 return 1;
1322
b8890359 1323 /*
1324 * SPECIAL, PUBLIC entries should be "locked"
1325 */
3900307b 1326 if (EBIT_TEST(flags, ENTRY_SPECIAL))
1327 if (!EBIT_TEST(flags, KEY_PRIVATE))
62e76326 1328 return 1;
1329
30a4f2a8 1330 return 0;
090089c4 1331}
1332
528b2c61 1333bool
1334StoreEntry::validLength() const
6602e70e 1335{
47f6e231 1336 int64_t diff;
d8b249ef 1337 const HttpReply *reply;
528b2c61 1338 assert(mem_obj != NULL);
1339 reply = getReply();
bf8fe701 1340 debugs(20, 3, "storeEntryValidLength: Checking '" << getMD5Text() << "'");
e4049756 1341 debugs(20, 5, "storeEntryValidLength: object_len = " <<
707fdc47 1342 objectLen());
bf8fe701 1343 debugs(20, 5, "storeEntryValidLength: hdr_sz = " << reply->hdr_sz);
1344 debugs(20, 5, "storeEntryValidLength: content_length = " << reply->content_length);
62e76326 1345
d8b249ef 1346 if (reply->content_length < 0) {
bf8fe701 1347 debugs(20, 5, "storeEntryValidLength: Unspecified content length: " << getMD5Text());
62e76326 1348 return 1;
ffe4a367 1349 }
62e76326 1350
07304bf9 1351 if (reply->hdr_sz == 0) {
bf8fe701 1352 debugs(20, 5, "storeEntryValidLength: Zero header size: " << getMD5Text());
62e76326 1353 return 1;
ffe4a367 1354 }
62e76326 1355
528b2c61 1356 if (mem_obj->method == METHOD_HEAD) {
bf8fe701 1357 debugs(20, 5, "storeEntryValidLength: HEAD request: " << getMD5Text());
62e76326 1358 return 1;
ffe4a367 1359 }
62e76326 1360
cb69b4c7 1361 if (reply->sline.status == HTTP_NOT_MODIFIED)
62e76326 1362 return 1;
1363
cb69b4c7 1364 if (reply->sline.status == HTTP_NO_CONTENT)
62e76326 1365 return 1;
1366
707fdc47 1367 diff = reply->hdr_sz + reply->content_length - objectLen();
62e76326 1368
ebf4efff 1369 if (diff == 0)
62e76326 1370 return 1;
1371
bf8fe701 1372 debugs(20, 3, "storeEntryValidLength: " << (diff < 0 ? -diff : diff) << " bytes too " << (diff < 0 ? "big" : "small") <<"; '" << getMD5Text() << "'" );
62e76326 1373
ebf4efff 1374 return 0;
ffe4a367 1375}
6602e70e 1376
6b7d87bb
FC
1377static void
1378storeRegisterWithCacheManager(void)
1379{
8822ebee
AR
1380 Mgr::RegisterAction("storedir", "Store Directory Stats", Store::Stats, 0, 1);
1381 Mgr::RegisterAction("store_io", "Store IO Interface Stats", &Mgr::StoreIoAction::Create, 0, 1);
1382 Mgr::RegisterAction("store_check_cachable_stats", "storeCheckCachable() Stats",
26ac0430 1383 storeCheckCachableStats, 0, 1);
6b7d87bb
FC
1384}
1385
b8d8561b 1386void
1387storeInit(void)
c943f331 1388{
25535cbe 1389 storeKeyInit();
6a566b9c 1390 mem_policy = createRemovalPolicy(Config.memPolicy);
8638fc66 1391 storeDigestInit();
e3ef2b09 1392 storeLogOpen();
e42d5181 1393 eventAdd("storeLateRelease", storeLateRelease, NULL, 1.0, 1);
c8f4eac4 1394 Store::Root().init();
b2c141d4 1395 storeRebuildStart();
d120ed12
FC
1396
1397 storeRegisterWithCacheManager();
62ee09ca 1398}
1399
b8d8561b 1400void
1401storeConfigure(void)
b1c0cc67 1402{
c8f4eac4 1403 store_swap_high = (long) (((float) Store::Root().maxSize() *
62e76326 1404 (float) Config.Swap.highWaterMark) / (float) 100);
c8f4eac4 1405 store_swap_low = (long) (((float) Store::Root().maxSize() *
62e76326 1406 (float) Config.Swap.lowWaterMark) / (float) 100);
505d4821 1407 store_pages_max = Config.memMaxSize / sizeof(mem_node);
090089c4 1408}
1409
3900307b 1410int
1411StoreEntry::keepInMemory() const
56f29785 1412{
3900307b 1413 if (mem_obj == NULL)
62e76326 1414 return 0;
1415
3900307b 1416 if (mem_obj->data_hdr.size() == 0)
62e76326 1417 return 0;
1418
19fdd3f3 1419 if (mem_obj->inmem_lo != 0)
e1381638 1420 return 0;
19fdd3f3 1421
d227e181 1422 if (!Config.onoff.memory_cache_first && swap_status == SWAPOUT_DONE && refcount == 1)
e1381638 1423 return 0;
19fdd3f3
HN
1424
1425 return 1;
56f29785 1426}
1427
edce4d98 1428int
3900307b 1429StoreEntry::checkNegativeHit() const
edce4d98 1430{
3900307b 1431 if (!EBIT_TEST(flags, ENTRY_NEGCACHED))
62e76326 1432 return 0;
1433
3900307b 1434 if (expires <= squid_curtime)
62e76326 1435 return 0;
1436
3900307b 1437 if (store_status != STORE_OK)
62e76326 1438 return 0;
1439
edce4d98 1440 return 1;
1441}
1442
ac9cc053
AJ
1443/**
1444 * Set object for negative caching.
1445 * Preserves any expiry information given by the server.
1446 * In absence of proper expiry info it will set to expire immediately,
1447 * or with HTTP-violations enabled the configured negative-TTL is observed
1448 */
b8d8561b 1449void
d88e3c49 1450StoreEntry::negativeCache()
79b5cc5f 1451{
26ac0430 1452 if (expires == 0)
626096be 1453#if USE_HTTP_VIOLATIONS
ac9cc053
AJ
1454 expires = squid_curtime + Config.negativeTtl;
1455#else
1456 expires = squid_curtime;
1457#endif
d88e3c49 1458 EBIT_SET(flags, ENTRY_NEGCACHED);
79b5cc5f 1459}
0a21bd84 1460
1461void
1462storeFreeMemory(void)
1463{
c8f4eac4 1464 Store::Root(NULL);
9bc73deb 1465#if USE_CACHE_DIGESTS
62e76326 1466
8638fc66 1467 if (store_digest)
62e76326 1468 cacheDigestDestroy(store_digest);
1469
c68e9c6b 1470#endif
62e76326 1471
8638fc66 1472 store_digest = NULL;
0a21bd84 1473}
a7e59001 1474
1475int
1476expiresMoreThan(time_t expires, time_t when)
1477{
c21ad0f5 1478 if (expires < 0) /* No Expires given */
62e76326 1479 return 1;
1480
48f44632 1481 return (expires > (squid_curtime + when));
a7e59001 1482}
fe54d06d 1483
1484int
3900307b 1485StoreEntry::validToSend() const
fe54d06d 1486{
3900307b 1487 if (EBIT_TEST(flags, RELEASE_REQUEST))
62e76326 1488 return 0;
1489
3900307b 1490 if (EBIT_TEST(flags, ENTRY_NEGCACHED))
1491 if (expires <= squid_curtime)
62e76326 1492 return 0;
1493
3900307b 1494 if (EBIT_TEST(flags, ENTRY_ABORTED))
62e76326 1495 return 0;
1496
fe54d06d 1497 return 1;
1498}
62663274 1499
ca98227c 1500void
3900307b 1501StoreEntry::timestampsSet()
ca98227c 1502{
3900307b 1503 const HttpReply *reply = getReply();
2f58241d 1504 time_t served_date = reply->date;
a9925b40 1505 int age = reply->header.getInt(HDR_AGE);
31d36bfd 1506 /* Compute the timestamp, mimicking RFC2616 section 13.2.3. */
2f58241d 1507 /* make sure that 0 <= served_date <= squid_curtime */
62e76326 1508
2f58241d 1509 if (served_date < 0 || served_date > squid_curtime)
62e76326 1510 served_date = squid_curtime;
1511
efd900cb 1512 /*
212cbb48 1513 * Compensate with Age header if origin server clock is ahead
1514 * of us and there is a cache in between us and the origin
1515 * server. But DONT compensate if the age value is larger than
1516 * squid_curtime because it results in a negative served_date.
efd900cb 1517 */
1518 if (age > squid_curtime - served_date)
62e76326 1519 if (squid_curtime > age)
1520 served_date = squid_curtime - age;
1521
31d36bfd
AR
1522 // compensate for Squid-to-server and server-to-Squid delays
1523 if (mem_obj && mem_obj->request) {
1524 const time_t request_sent =
1525 mem_obj->request->hier.peer_http_request_sent.tv_sec;
1526 if (0 < request_sent && request_sent < squid_curtime)
1527 served_date -= (squid_curtime - request_sent);
1528 }
1529
0d465a25 1530 if (reply->expires > 0 && reply->date > -1)
26ac0430 1531 expires = served_date + (reply->expires - reply->date);
0d465a25 1532 else
26ac0430 1533 expires = reply->expires;
62e76326 1534
3900307b 1535 lastmod = reply->last_modified;
62e76326 1536
3900307b 1537 timestamp = served_date;
ca98227c 1538}
429fdbec 1539
bfcaf585 1540void
3900307b 1541StoreEntry::registerAbort(STABH * cb, void *data)
bfcaf585 1542{
3900307b 1543 assert(mem_obj);
1544 assert(mem_obj->abort.callback == NULL);
1545 mem_obj->abort.callback = cb;
8ea67c2b 1546 mem_obj->abort.data = cbdataReference(data);
bfcaf585 1547}
1548
1549void
3900307b 1550StoreEntry::unregisterAbort()
bfcaf585 1551{
3900307b 1552 assert(mem_obj);
8ea67c2b 1553 if (mem_obj->abort.callback) {
26ac0430
AJ
1554 mem_obj->abort.callback = NULL;
1555 cbdataReferenceDone(mem_obj->abort.data);
8ea67c2b 1556 }
bfcaf585 1557}
88738790 1558
f09f5b26 1559void
3900307b 1560StoreEntry::dump(int l) const
1561{
bf8fe701 1562 debugs(20, l, "StoreEntry->key: " << getMD5Text());
1563 debugs(20, l, "StoreEntry->next: " << next);
1564 debugs(20, l, "StoreEntry->mem_obj: " << mem_obj);
4a7a3d56 1565 debugs(20, l, "StoreEntry->timestamp: " << timestamp);
1566 debugs(20, l, "StoreEntry->lastref: " << lastref);
1567 debugs(20, l, "StoreEntry->expires: " << expires);
1568 debugs(20, l, "StoreEntry->lastmod: " << lastmod);
1569 debugs(20, l, "StoreEntry->swap_file_sz: " << swap_file_sz);
bf8fe701 1570 debugs(20, l, "StoreEntry->refcount: " << refcount);
1571 debugs(20, l, "StoreEntry->flags: " << storeEntryFlags(this));
4a7a3d56 1572 debugs(20, l, "StoreEntry->swap_dirn: " << swap_dirn);
1573 debugs(20, l, "StoreEntry->swap_filen: " << swap_filen);
1574 debugs(20, l, "StoreEntry->lock_count: " << lock_count);
1575 debugs(20, l, "StoreEntry->mem_status: " << mem_status);
1576 debugs(20, l, "StoreEntry->ping_status: " << ping_status);
1577 debugs(20, l, "StoreEntry->store_status: " << store_status);
1578 debugs(20, l, "StoreEntry->swap_status: " << swap_status);
d377699f 1579}
1580
1f38f50a 1581/*
1582 * NOTE, this function assumes only two mem states
1583 */
f09f5b26 1584void
3900307b 1585StoreEntry::setMemStatus(mem_status_t new_status)
8350fe9b 1586{
3900307b 1587 if (new_status == mem_status)
62e76326 1588 return;
1589
3900307b 1590 assert(mem_obj != NULL);
62e76326 1591
b93bcace 1592 if (new_status == IN_MEMORY) {
3900307b 1593 assert(mem_obj->inmem_lo == 0);
62e76326 1594
3900307b 1595 if (EBIT_TEST(flags, ENTRY_SPECIAL)) {
bf8fe701 1596 debugs(20, 4, "StoreEntry::setMemStatus: not inserting special " << mem_obj->url << " into policy");
62e76326 1597 } else {
3900307b 1598 mem_policy->Add(mem_policy, this, &mem_obj->repl);
bf8fe701 1599 debugs(20, 4, "StoreEntry::setMemStatus: inserted mem node " << mem_obj->url);
62e76326 1600 }
1601
1602 hot_obj_count++;
b93bcace 1603 } else {
3900307b 1604 if (EBIT_TEST(flags, ENTRY_SPECIAL)) {
bf8fe701 1605 debugs(20, 4, "StoreEntry::setMemStatus: special entry " << mem_obj->url);
62e76326 1606 } else {
3900307b 1607 mem_policy->Remove(mem_policy, this, &mem_obj->repl);
bf8fe701 1608 debugs(20, 4, "StoreEntry::setMemStatus: removed mem node " << mem_obj->url);
62e76326 1609 }
1610
1611 hot_obj_count--;
b93bcace 1612 }
62e76326 1613
3900307b 1614 mem_status = new_status;
8350fe9b 1615}
6e86c3e8 1616
9fb13bb6 1617const char *
3900307b 1618StoreEntry::url() const
9fb13bb6 1619{
3900307b 1620 if (this == NULL)
62e76326 1621 return "[null_entry]";
3900307b 1622 else if (mem_obj == NULL)
62e76326 1623 return "[null_mem_obj]";
9fb13bb6 1624 else
3900307b 1625 return mem_obj->url;
9fb13bb6 1626}
24ffafb4 1627
1628void
d5f8d05f 1629StoreEntry::createMemObject(const char *aUrl, const char *aLogUrl)
24ffafb4 1630{
3900307b 1631 if (mem_obj)
62e76326 1632 return;
1633
d5f8d05f 1634 mem_obj = new MemObject(aUrl, aLogUrl);
c21ad0f5 1635}
1636
1637/* this just sets DELAY_SENDING */
1638void
1639StoreEntry::buffer()
1640{
1641 EBIT_SET(flags, DELAY_SENDING);
1642}
1643
438fc1e3 1644/* this just clears DELAY_SENDING and Invokes the handlers */
1645void
c21ad0f5 1646StoreEntry::flush()
438fc1e3 1647{
c21ad0f5 1648 if (EBIT_TEST(flags, DELAY_SENDING)) {
1649 EBIT_CLR(flags, DELAY_SENDING);
d88e3c49 1650 invokeHandlers();
b66315e4 1651 }
25535cbe 1652}
07304bf9 1653
47f6e231 1654int64_t
707fdc47 1655StoreEntry::objectLen() const
07304bf9 1656{
707fdc47 1657 assert(mem_obj != NULL);
1658 return mem_obj->object_sz;
07304bf9 1659}
1660
47f6e231 1661int64_t
b37bde1e 1662StoreEntry::contentLen() const
07304bf9 1663{
b37bde1e 1664 assert(mem_obj != NULL);
1665 assert(getReply() != NULL);
1666 return objectLen() - getReply()->hdr_sz;
07304bf9 1667}
f3986a15 1668
528b2c61 1669HttpReply const *
1670StoreEntry::getReply () const
f3986a15 1671{
528b2c61 1672 if (NULL == mem_obj)
62e76326 1673 return NULL;
1674
528b2c61 1675 return mem_obj->getReply();
f3986a15 1676}
db1cd23c 1677
1678void
3900307b 1679StoreEntry::reset()
db1cd23c 1680{
3900307b 1681 assert (mem_obj);
bf8fe701 1682 debugs(20, 3, "StoreEntry::reset: " << url());
3900307b 1683 mem_obj->reset();
1684 HttpReply *rep = (HttpReply *) getReply(); // bypass const
06a5ae20 1685 rep->reset();
3900307b 1686 expires = lastmod = timestamp = -1;
db1cd23c 1687}
2b906e48 1688
cd748f27 1689/*
1690 * storeFsInit
1691 *
1692 * This routine calls the SETUP routine for each fs type.
1693 * I don't know where the best place for this is, and I'm not going to shuffle
1694 * around large chunks of code right now (that can be done once its working.)
1695 */
1696void
1697storeFsInit(void)
1698{
22d38e05 1699 storeReplSetup();
cd748f27 1700}
1701
22d38e05 1702/*
1703 * called to add another store removal policy module
1704 */
1705void
a2c963ae 1706storeReplAdd(const char *type, REMOVALPOLICYCREATE * create)
22d38e05 1707{
1708 int i;
62e76326 1709
d64c1498 1710 /* find the number of currently known repl types */
22d38e05 1711 for (i = 0; storerepl_list && storerepl_list[i].typestr; i++) {
d64c1498
AJ
1712 if (strcmp(storerepl_list[i].typestr, type) == 0) {
1713 debugs(20, 1, "WARNING: Trying to load store replacement policy " << type << " twice.");
1714 return;
1715 }
22d38e05 1716 }
62e76326 1717
22d38e05 1718 /* add the new type */
e6ccf245 1719 storerepl_list = static_cast<storerepl_entry_t *>(xrealloc(storerepl_list, (i + 2) * sizeof(storerepl_entry_t)));
62e76326 1720
22d38e05 1721 memset(&storerepl_list[i + 1], 0, sizeof(storerepl_entry_t));
62e76326 1722
22d38e05 1723 storerepl_list[i].typestr = type;
62e76326 1724
22d38e05 1725 storerepl_list[i].create = create;
1726}
1727
1728/*
1729 * Create a removal policy instance
1730 */
1731RemovalPolicy *
1732createRemovalPolicy(RemovalPolicySettings * settings)
1733{
1734 storerepl_entry_t *r;
62e76326 1735
22d38e05 1736 for (r = storerepl_list; r && r->typestr; r++) {
62e76326 1737 if (strcmp(r->typestr, settings->type) == 0)
1738 return r->create(settings->args);
22d38e05 1739 }
62e76326 1740
bf8fe701 1741 debugs(20, 1, "ERROR: Unknown policy " << settings->type);
1742 debugs(20, 1, "ERROR: Be sure to have set cache_replacement_policy");
1743 debugs(20, 1, "ERROR: and memory_replacement_policy in squid.conf!");
0c5ccf11 1744 fatalf("ERROR: Unknown policy %s\n", settings->type);
c21ad0f5 1745 return NULL; /* NOTREACHED */
22d38e05 1746}
1747
cd748f27 1748#if 0
fc8b9fc0 1749void
1750storeSwapFileNumberSet(StoreEntry * e, sfileno filn)
1751{
1752 if (e->swap_file_number == filn)
62e76326 1753 return;
1754
fc8b9fc0 1755 if (filn < 0) {
62e76326 1756 assert(-1 == filn);
1757 storeDirMapBitReset(e->swap_file_number);
1758 storeDirLRUDelete(e);
1759 e->swap_file_number = -1;
fc8b9fc0 1760 } else {
62e76326 1761 assert(-1 == e->swap_file_number);
1762 storeDirMapBitSet(e->swap_file_number = filn);
1763 storeDirLRUAdd(e);
fc8b9fc0 1764 }
1765}
62e76326 1766
cd748f27 1767#endif
e6ccf245 1768
4a56ee8d 1769
db237875 1770/*
1771 * Replace a store entry with
528b2c61 1772 * a new reply. This eats the reply.
1773 */
4a56ee8d 1774void
1775StoreEntry::replaceHttpReply(HttpReply *rep)
1776{
bf8fe701 1777 debugs(20, 3, "StoreEntry::replaceHttpReply: " << url());
528b2c61 1778 Packer p;
62e76326 1779
4a56ee8d 1780 if (!mem_obj) {
bf8fe701 1781 debugs(20, 0, "Attempt to replace object with no in-memory representation");
62e76326 1782 return;
528b2c61 1783 }
62e76326 1784
4a56ee8d 1785 mem_obj->replaceHttpReply(rep);
1786
528b2c61 1787 /* TODO: when we store headers serparately remove the header portion */
1788 /* TODO: mark the length of the headers ? */
1789 /* We ONLY want the headers */
4a56ee8d 1790 packerToStoreInit(&p, this);
62e76326 1791
4a56ee8d 1792 assert (isEmpty());
62e76326 1793
4a56ee8d 1794 getReply()->packHeadersInto(&p);
62e76326 1795
4a56ee8d 1796 rep->hdr_sz = mem_obj->endOffset();
62e76326 1797
4a56ee8d 1798 httpBodyPackInto(&getReply()->body, &p);
62e76326 1799
528b2c61 1800 packerClean(&p);
1801}
62e76326 1802
e6ccf245 1803
528b2c61 1804char const *
1805StoreEntry::getSerialisedMetaData()
1806{
1807 StoreMeta *tlv_list = storeSwapMetaBuild(this);
1808 int swap_hdr_sz;
1809 char *result = storeSwapMetaPack(tlv_list, &swap_hdr_sz);
1810 storeSwapTLVFree(tlv_list);
1811 assert (swap_hdr_sz >= 0);
1812 mem_obj->swap_hdr_sz = (size_t) swap_hdr_sz;
1813 return result;
1814}
1815
1816bool
1817StoreEntry::swapoutPossible()
1818{
1819 /* should we swap something out to disk? */
bf8fe701 1820 debugs(20, 7, "storeSwapOut: " << url());
1821 debugs(20, 7, "storeSwapOut: store_status = " << storeStatusStr[store_status]);
62e76326 1822
528b2c61 1823 if (EBIT_TEST(flags, ENTRY_ABORTED)) {
62e76326 1824 assert(EBIT_TEST(flags, RELEASE_REQUEST));
c07cbbf4 1825 swapOutFileClose();
62e76326 1826 return false;
528b2c61 1827 }
62e76326 1828
528b2c61 1829 if (EBIT_TEST(flags, ENTRY_SPECIAL)) {
bf8fe701 1830 debugs(20, 3, "storeSwapOut: " << url() << " SPECIAL");
62e76326 1831 return false;
528b2c61 1832 }
62e76326 1833
528b2c61 1834 return true;
1835}
1836
1837void
1838StoreEntry::trimMemory()
1839{
7fef2365 1840 /*
1841 * DPW 2007-05-09
1842 * Bug #1943. We must not let go any data for IN_MEMORY
1843 * objects. We have to wait until the mem_status changes.
1844 */
1845 if (mem_status == IN_MEMORY)
26ac0430 1846 return;
7fef2365 1847
c07cbbf4 1848 if (!swapOutAble()) {
e1381638
AJ
1849 if (mem_obj->policyLowestOffsetToKeep(0) == 0) {
1850 /* Nothing to do */
1851 return;
1852 }
62e76326 1853 /*
1854 * Its not swap-able, and we're about to delete a chunk,
1855 * so we must make it PRIVATE. This is tricky/ugly because
1856 * for the most part, we treat swapable == cachable here.
1857 */
d88e3c49 1858 releaseRequest();
62e76326 1859 mem_obj->trimUnSwappable ();
528b2c61 1860 } else {
62e76326 1861 mem_obj->trimSwappable ();
528b2c61 1862 }
1863}
62e76326 1864
0655fa4d 1865bool
190154cf 1866StoreEntry::modifiedSince(HttpRequest * request) const
0655fa4d 1867{
1868 int object_length;
1869 time_t mod_time = lastmod;
1870
1871 if (mod_time < 0)
1872 mod_time = timestamp;
1873
bf8fe701 1874 debugs(88, 3, "modifiedSince: '" << url() << "'");
0655fa4d 1875
4a7a3d56 1876 debugs(88, 3, "modifiedSince: mod_time = " << mod_time);
0655fa4d 1877
1878 if (mod_time < 0)
1879 return true;
1880
1881 /* Find size of the object */
1882 object_length = getReply()->content_length;
1883
1884 if (object_length < 0)
b37bde1e 1885 object_length = contentLen();
0655fa4d 1886
1887 if (mod_time > request->ims) {
bf8fe701 1888 debugs(88, 3, "--> YES: entry newer than client");
0655fa4d 1889 return true;
1890 } else if (mod_time < request->ims) {
bf8fe701 1891 debugs(88, 3, "--> NO: entry older than client");
0655fa4d 1892 return false;
1893 } else if (request->imslen < 0) {
bf8fe701 1894 debugs(88, 3, "--> NO: same LMT, no client length");
0655fa4d 1895 return false;
1896 } else if (request->imslen == object_length) {
bf8fe701 1897 debugs(88, 3, "--> NO: same LMT, same length");
0655fa4d 1898 return false;
1899 } else {
bf8fe701 1900 debugs(88, 3, "--> YES: same LMT, different length");
0655fa4d 1901 return true;
1902 }
1903}
1904
79c8035e
AR
1905bool
1906StoreEntry::hasIfMatchEtag(const HttpRequest &request) const
1907{
1908 const String reqETags = request.header.getList(HDR_IF_MATCH);
1909 return hasOneOfEtags(reqETags, false);
1910}
1911
1912bool
1913StoreEntry::hasIfNoneMatchEtag(const HttpRequest &request) const
1914{
1915 const String reqETags = request.header.getList(HDR_IF_NONE_MATCH);
1916 // weak comparison is allowed only for HEAD or full-body GET requests
1917 const bool allowWeakMatch = !request.flags.range &&
b59e6847 1918 (request.method == METHOD_GET || request.method == METHOD_HEAD);
79c8035e
AR
1919 return hasOneOfEtags(reqETags, allowWeakMatch);
1920}
1921
1922/// whether at least one of the request ETags matches entity ETag
1923bool
1924StoreEntry::hasOneOfEtags(const String &reqETags, const bool allowWeakMatch) const
1925{
1926 const ETag repETag = getReply()->header.getETag(HDR_ETAG);
1927 if (!repETag.str)
1928 return strListIsMember(&reqETags, "*", ',');
1929
1930 bool matched = false;
1931 const char *pos = NULL;
1932 const char *item;
1933 int ilen;
1934 while (!matched && strListGetItem(&reqETags, ',', &item, &ilen, &pos)) {
1935 if (!strncmp(item, "*", ilen))
1936 matched = true;
1937 else {
1938 String str;
1939 str.append(item, ilen);
1940 ETag reqETag;
1941 if (etagParseInit(&reqETag, str.termedBuf())) {
1942 matched = allowWeakMatch ? etagIsWeakEqual(repETag, reqETag) :
b59e6847 1943 etagIsStrongEqual(repETag, reqETag);
79c8035e
AR
1944 }
1945 }
1946 }
1947 return matched;
1948}
1949
c8f4eac4 1950StorePointer
1951StoreEntry::store() const
1952{
1953 assert(0 <= swap_dirn && swap_dirn < Config.cacheSwap.n_configured);
1954 return INDEXSD(swap_dirn);
1955}
1956
1957void
1958StoreEntry::unlink()
1959{
1960 store()->unlink(*this);
1961}
0655fa4d 1962
aa18a4ca 1963/*
1964 * return true if the entry is in a state where
1965 * it can accept more data (ie with write() method)
1966 */
1967bool
1968StoreEntry::isAccepting() const
1969{
1970 if (STORE_PENDING != store_status)
1971 return false;
1972
1973 if (EBIT_TEST(flags, ENTRY_ABORTED))
1974 return false;
1975
1976 return true;
1977}
1978
e6ccf245 1979/* NullStoreEntry */
1980
1981NullStoreEntry NullStoreEntry::_instance;
1982
1983NullStoreEntry *
1984NullStoreEntry::getInstance()
1985{
1986 return &_instance;
1987}
332dafa2 1988
1989char const *
1990NullStoreEntry::getMD5Text() const
1991{
1992 return "N/A";
1993}
528b2c61 1994
43ae1d95 1995void
1996NullStoreEntry::operator delete(void*)
1997{
1998 fatal ("Attempt to delete NullStoreEntry\n");
1999}
2000
528b2c61 2001char const *
2002NullStoreEntry::getSerialisedMetaData()
2003{
2004 return NULL;
2005}
2006
32d002cb 2007#if !_USE_INLINE_
528b2c61 2008#include "Store.cci"
2009#endif