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