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