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