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