2 Copyright (c) 2003-2006 by Juliusz Chroboczek
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 int mindlesslyCacheVary
= 0;
26 int objectHashTableSize
= 0;
27 int log2ObjectHashTableSize
;
29 static ObjectPtr object_list
= NULL
;
30 static ObjectPtr object_list_end
= NULL
;
32 int objectExpiryScheduled
;
34 int publicObjectCount
;
35 int privateObjectCount
;
36 int cacheIsShared
= 1;
37 int publicObjectLowMark
= 0, objectHighMark
= 2048;
39 static ObjectPtr
*objectHashTable
;
40 int maxExpiresAge
= (30 * 24 + 1) * 3600;
41 int maxAge
= (14 * 24 + 1) * 3600;
42 float maxAgeFraction
= 0.1;
43 int maxNoModifiedAge
= 23 * 60;
44 int maxWriteoutWhenIdle
= 64 * 1024;
45 int maxObjectsWhenIdle
= 32;
47 int dontCacheCookies
= 0;
52 CONFIG_VARIABLE_SETTABLE(idleTime
, CONFIG_TIME
, configIntSetter
,
53 "Time to remain idle before writing out.");
54 CONFIG_VARIABLE_SETTABLE(maxWriteoutWhenIdle
, CONFIG_INT
, configIntSetter
,
55 "Amount of data to write at a time when idle.");
56 CONFIG_VARIABLE_SETTABLE(maxObjectsWhenIdle
, CONFIG_INT
, configIntSetter
,
57 "Number of objects to write at a time "
59 CONFIG_VARIABLE_SETTABLE(cacheIsShared
, CONFIG_BOOLEAN
, configIntSetter
,
60 "If false, ignore s-maxage and private.");
61 CONFIG_VARIABLE_SETTABLE(mindlesslyCacheVary
, CONFIG_BOOLEAN
,
63 "If true, mindlessly cache negotiated objects.");
64 CONFIG_VARIABLE(objectHashTableSize
, CONFIG_INT
,
65 "Size of the object hash table (0 = auto).");
66 CONFIG_VARIABLE(objectHighMark
, CONFIG_INT
,
67 "High object count mark.");
68 CONFIG_VARIABLE(publicObjectLowMark
, CONFIG_INT
,
69 "Low object count mark (0 = auto).");
70 CONFIG_VARIABLE_SETTABLE(maxExpiresAge
, CONFIG_TIME
, configIntSetter
,
71 "Max age for objects with Expires header.");
72 CONFIG_VARIABLE_SETTABLE(maxAge
, CONFIG_TIME
, configIntSetter
,
73 "Max age for objects without Expires header.");
74 CONFIG_VARIABLE_SETTABLE(maxAgeFraction
, CONFIG_FLOAT
, configFloatSetter
,
75 "Fresh fraction of modification time.");
76 CONFIG_VARIABLE_SETTABLE(maxNoModifiedAge
, CONFIG_TIME
, configIntSetter
,
77 "Max age for objects without Last-modified.");
78 CONFIG_VARIABLE_SETTABLE(dontCacheCookies
, CONFIG_BOOLEAN
, configIntSetter
,
79 "Work around cachable cookies.");
86 if(objectHighMark
< 16) {
88 do_log(L_WARN
, "Impossibly low objectHighMark -- setting to %d.\n",
93 if(publicObjectLowMark
== 0) q
= 1;
94 if(publicObjectLowMark
< 8 || publicObjectLowMark
>= objectHighMark
- 4) {
95 publicObjectLowMark
= objectHighMark
/ 2;
97 do_log(L_WARN
, "Impossible publicObjectLowMark value -- "
98 "setting to %d.\n", publicObjectLowMark
);
102 if(objectHashTableSize
<= objectHighMark
/ 2 ||
103 objectHashTableSize
> objectHighMark
* 1024) {
104 if(objectHashTableSize
!= 0) q
= 0;
105 objectHashTableSize
= objectHighMark
* 16;
107 log2ObjectHashTableSize
= log2_ceil(objectHashTableSize
);
108 objectHashTableSize
= 1 << log2ObjectHashTableSize
;
110 do_log(L_WARN
, "Suspicious objectHashTableSize value -- "
111 "setting to %d.\n", objectHashTableSize
);
114 object_list_end
= NULL
;
115 publicObjectCount
= 0;
116 privateObjectCount
= 0;
117 objectHashTable
= calloc(1 << log2ObjectHashTableSize
,
119 if(!objectHashTable
) {
120 do_log(L_ERROR
, "Couldn't allocate object hash table.\n");
126 findObject(int type
, const void *key
, int key_size
)
131 assert(key_size
<= 10000);
133 h
= hash(type
, key
, key_size
, log2ObjectHashTableSize
);
134 object
= objectHashTable
[h
];
137 if(object
->type
!= type
|| object
->key_size
!= key_size
||
138 memcmp(object
->key
, key
, key_size
) != 0) {
142 object
->next
->previous
= object
->previous
;
144 object
->previous
->next
= object
->next
;
145 if(object_list
== object
)
146 object_list
= object
->next
;
147 if(object_list_end
== object
)
148 object_list_end
= object
->previous
;
149 object
->previous
= NULL
;
150 object
->next
= object_list
;
152 object_list
->previous
= object
;
153 object_list
= object
;
155 object_list_end
= object
;
156 return retainObject(object
);
160 makeObject(int type
, const void *key
, int key_size
, int public, int fromdisk
,
161 RequestFunction request
, void* request_closure
)
166 object
= findObject(type
, key
, key_size
);
171 privatiseObject(object
, 0);
174 if(publicObjectCount
+ privateObjectCount
>= objectHighMark
) {
175 if(!objectExpiryScheduled
)
176 discardObjects(0, 0);
177 if(publicObjectCount
+ privateObjectCount
>= objectHighMark
) {
182 if(publicObjectCount
>= publicObjectLowMark
&&
183 !objectExpiryScheduled
) {
184 TimeEventHandlerPtr event
;
185 event
= scheduleTimeEvent(-1, discardObjectsHandler
, 0, NULL
);
187 objectExpiryScheduled
= 1;
189 do_log(L_ERROR
, "Couldn't schedule object expiry.\n");
192 object
= malloc(sizeof(ObjectRec
));
197 object
->request
= request
;
198 object
->request_closure
= request_closure
;
199 object
->key
= malloc(key_size
);
200 if(object
->key
== NULL
) {
204 memcpy(object
->key
, key
, key_size
);
205 object
->key_size
= key_size
;
206 object
->flags
= (public?OBJECT_PUBLIC
:0) | OBJECT_INITIAL
;
208 h
= hash(object
->type
, object
->key
, object
->key_size
,
209 log2ObjectHashTableSize
);
210 if(objectHashTable
[h
]) {
211 writeoutToDisk(objectHashTable
[h
], objectHashTable
[h
]->size
, -1);
212 privatiseObject(objectHashTable
[h
], 0);
213 assert(!objectHashTable
[h
]);
215 objectHashTable
[h
] = object
;
216 object
->next
= object_list
;
217 object
->previous
= NULL
;
219 object_list
->previous
= object
;
220 object_list
= object
;
222 object_list_end
= object
;
225 object
->previous
= NULL
;
227 object
->abort_data
= NULL
;
229 object
->message
= NULL
;
230 initCondition(&object
->condition
);
231 object
->headers
= NULL
;
233 object
->numchunks
= 0;
234 object
->chunks
= NULL
;
238 object
->expires
= -1;
239 object
->last_modified
= -1;
242 object
->cache_control
= 0;
243 object
->max_age
= -1;
244 object
->s_maxage
= -1;
246 object
->requestor
= NULL
;
247 object
->disk_entry
= NULL
;
248 if(object
->flags
& OBJECT_PUBLIC
)
251 privateObjectCount
++;
252 object
->refcount
= 1;
254 if(public && fromdisk
)
255 objectGetFromDisk(object
);
260 objectMetadataChanged(ObjectPtr object
, int revalidate
)
263 revalidateDiskEntry(object
);
265 object
->flags
&= ~OBJECT_DISK_ENTRY_COMPLETE
;
266 dirtyDiskEntry(object
);
272 retainObject(ObjectPtr object
)
274 do_log(D_REFCOUNT
, "O 0x%lx %d++\n",
275 (unsigned long)object
, object
->refcount
);
281 releaseObject(ObjectPtr object
)
283 do_log(D_REFCOUNT
, "O 0x%lx %d--\n",
284 (unsigned long)object
, object
->refcount
);
286 if(object
->refcount
== 0) {
287 assert(!object
->condition
.handlers
&&
288 !(object
->flags
& OBJECT_INPROGRESS
));
289 if(!(object
->flags
& OBJECT_PUBLIC
))
290 destroyObject(object
);
295 releaseNotifyObject(ObjectPtr object
)
297 do_log(D_REFCOUNT
, "O 0x%lx %d--\n",
298 (unsigned long)object
, object
->refcount
);
300 if(object
->refcount
> 0) {
301 notifyObject(object
);
303 assert(!object
->condition
.handlers
&&
304 !(object
->flags
& OBJECT_INPROGRESS
));
305 if(!(object
->flags
& OBJECT_PUBLIC
))
306 destroyObject(object
);
311 lockChunk(ObjectPtr object
, int i
)
313 do_log(D_LOCK
, "Lock 0x%lx[%d]: ", (unsigned long)object
, i
);
315 if(i
>= object
->numchunks
)
316 objectSetChunks(object
, i
+ 1);
317 object
->chunks
[i
].locked
++;
318 do_log(D_LOCK
, "%d\n", object
->chunks
[i
].locked
);
322 unlockChunk(ObjectPtr object
, int i
)
324 do_log(D_LOCK
, "Unlock 0x%lx[%d]: ", (unsigned long)object
, i
);
325 assert(i
>= 0 && i
< object
->numchunks
);
326 assert(object
->chunks
[i
].locked
> 0);
327 object
->chunks
[i
].locked
--;
328 do_log(D_LOCK
, "%d\n", object
->chunks
[i
].locked
);
332 objectSetChunks(ObjectPtr object
, int numchunks
)
336 if(numchunks
<= object
->numchunks
)
339 if(object
->length
>= 0)
340 n
= MAX(numchunks
, (object
->length
+ (CHUNK_SIZE
- 1)) / CHUNK_SIZE
);
343 MAX(object
->numchunks
+ 2, object
->numchunks
* 5 / 4));
346 assert(object
->chunks
== NULL
);
347 } else if(object
->numchunks
== 0) {
348 object
->chunks
= calloc(n
, sizeof(ChunkRec
));
349 if(object
->chunks
== NULL
) {
352 object
->numchunks
= n
;
355 newchunks
= realloc(object
->chunks
, n
* sizeof(ChunkRec
));
356 if(newchunks
== NULL
)
358 memset(newchunks
+ object
->numchunks
, 0,
359 (n
- object
->numchunks
) * sizeof(ChunkRec
));
360 object
->chunks
= newchunks
;
361 object
->numchunks
= n
;
367 objectPartial(ObjectPtr object
, int length
, struct _Atom
*headers
)
369 object
->headers
= headers
;
372 if(object
->size
> length
) {
373 abortObject(object
, 502,
374 internAtom("Inconsistent Content-Length"));
375 notifyObject(object
);
381 object
->length
= length
;
383 object
->flags
&= ~OBJECT_INITIAL
;
384 revalidateDiskEntry(object
);
385 notifyObject(object
);
390 objectAddChunk(ObjectPtr object
, const char *data
, int offset
, int plen
)
392 int i
= offset
/ CHUNK_SIZE
;
395 assert(offset
% CHUNK_SIZE
== 0);
396 assert(plen
<= CHUNK_SIZE
);
398 if(object
->numchunks
<= i
) {
399 rc
= objectSetChunks(object
, i
+ 1);
404 lockChunk(object
, i
);
406 if(object
->chunks
[i
].data
== NULL
) {
407 object
->chunks
[i
].data
= get_chunk();
408 if(object
->chunks
[i
].data
== NULL
)
412 if(object
->chunks
[i
].size
>= plen
) {
413 unlockChunk(object
, i
);
417 if(object
->size
< offset
+ plen
)
418 object
->size
= offset
+ plen
;
419 object
->chunks
[i
].size
= plen
;
420 memcpy(object
->chunks
[i
].data
, data
, plen
);
421 unlockChunk(object
, i
);
425 unlockChunk(object
, i
);
430 objectAddChunkEnd(ObjectPtr object
, const char *data
, int offset
, int plen
)
432 int i
= offset
/ CHUNK_SIZE
;
435 assert(offset
% CHUNK_SIZE
!= 0 &&
436 offset
% CHUNK_SIZE
+ plen
<= CHUNK_SIZE
);
438 if(object
->numchunks
<= i
) {
439 rc
= objectSetChunks(object
, i
+ 1);
444 lockChunk(object
, i
);
446 if(object
->chunks
[i
].data
== NULL
)
447 object
->chunks
[i
].data
= get_chunk();
448 if(object
->chunks
[i
].data
== NULL
)
451 if(offset
> object
->size
) {
455 if(object
->chunks
[i
].size
< offset
% CHUNK_SIZE
) {
459 if(object
->size
< offset
+ plen
)
460 object
->size
= offset
+ plen
;
461 object
->chunks
[i
].size
= offset
% CHUNK_SIZE
+ plen
;
462 memcpy(object
->chunks
[i
].data
+ (offset
% CHUNK_SIZE
),
465 unlockChunk(object
, i
);
469 unlockChunk(object
, i
);
474 objectAddData(ObjectPtr object
, const char *data
, int offset
, int len
)
478 do_log(D_OBJECT_DATA
, "Adding data to 0x%lx (%d) at %d: %d bytes\n",
479 (unsigned long)object
, object
->length
, offset
, len
);
484 if(object
->length
>= 0) {
485 if(offset
+ len
> object
->length
) {
487 "Inconsistent object length (%d, should be at least %d).\n",
488 object
->length
, offset
+ len
);
489 object
->length
= offset
+ len
;
493 object
->flags
&= ~OBJECT_FAILED
;
495 if(offset
+ len
>= object
->numchunks
* CHUNK_SIZE
) {
496 rc
= objectSetChunks(object
, (offset
+ len
- 1) / CHUNK_SIZE
+ 1);
502 if(offset
% CHUNK_SIZE
!= 0) {
503 int plen
= CHUNK_SIZE
- offset
% CHUNK_SIZE
;
506 rc
= objectAddChunkEnd(object
, data
, offset
, plen
);
516 int plen
= (len
>= CHUNK_SIZE
) ? CHUNK_SIZE
: len
;
517 rc
= objectAddChunk(object
, data
, offset
, plen
);
530 objectPrintf(ObjectPtr object
, int offset
, const char *format
, ...)
536 va_start(args
, format
);
537 buf
= vsprintf_a(format
, args
);
541 abortObject(object
, 500, internAtom("Couldn't allocate string"));
545 rc
= objectAddData(object
, buf
, offset
, strlen(buf
));
548 abortObject(object
, 500, internAtom("Couldn't add data to object"));
552 objectHoleSize(ObjectPtr object
, int offset
)
556 if(offset
< 0 || offset
/ CHUNK_SIZE
>= object
->numchunks
)
559 if(offset
% CHUNK_SIZE
!= 0) {
560 if(object
->chunks
[offset
/ CHUNK_SIZE
].size
> offset
% CHUNK_SIZE
)
563 size
+= CHUNK_SIZE
- offset
% CHUNK_SIZE
;
564 offset
+= CHUNK_SIZE
- offset
% CHUNK_SIZE
;
568 for(i
= offset
/ CHUNK_SIZE
; i
< object
->numchunks
; i
++) {
569 if(object
->chunks
[i
].size
== 0)
574 if(i
>= object
->numchunks
)
580 /* Returns 2 if the data is wholly in memory, 1 if it's available on disk */
582 objectHasData(ObjectPtr object
, int from
, int to
)
584 int first
= from
/ CHUNK_SIZE
;
585 int last
= to
/ CHUNK_SIZE
;
589 if(object
->length
>= 0)
598 if(to
> object
->size
) {
603 if(last
> object
->numchunks
||
604 object
->chunks
[last
].size
> to
% CHUNK_SIZE
) {
609 for(i
= last
- 1; i
>= first
; i
--) {
610 if(object
->chunks
[i
].size
< CHUNK_SIZE
) {
611 upto
= (i
+ 1) * CHUNK_SIZE
;
619 if(object
->flags
& OBJECT_DISK_ENTRY_COMPLETE
)
622 if(diskEntrySize(object
) >= upto
)
629 destroyObject(ObjectPtr object
)
633 assert(object
->refcount
== 0 && !object
->requestor
);
634 assert(!object
->condition
.handlers
&&
635 (object
->flags
& OBJECT_INPROGRESS
) == 0);
637 if(object
->disk_entry
)
638 destroyDiskEntry(object
, 0);
640 if(object
->flags
& OBJECT_PUBLIC
) {
641 privatiseObject(object
, 0);
644 if(object
->message
) releaseAtom(object
->message
);
645 if(object
->key
) free(object
->key
);
646 if(object
->headers
) releaseAtom(object
->headers
);
647 if(object
->etag
) free(object
->etag
);
648 if(object
->via
) releaseAtom(object
->via
);
649 for(i
= 0; i
< object
->numchunks
; i
++) {
650 assert(!object
->chunks
[i
].locked
);
651 if(object
->chunks
[i
].data
)
652 dispose_chunk(object
->chunks
[i
].data
);
653 object
->chunks
[i
].data
= NULL
;
654 object
->chunks
[i
].size
= 0;
656 if(object
->chunks
) free(object
->chunks
);
657 privateObjectCount
--;
663 privatiseObject(ObjectPtr object
, int linear
)
666 if(!(object
->flags
& OBJECT_PUBLIC
)) {
668 object
->flags
|= OBJECT_LINEAR
;
672 if(object
->disk_entry
)
673 destroyDiskEntry(object
, 0);
674 object
->flags
&= ~OBJECT_PUBLIC
;
676 for(i
= 0; i
< object
->numchunks
; i
++) {
677 if(object
->chunks
[i
].locked
)
679 if(object
->chunks
[i
].data
) {
680 object
->chunks
[i
].size
= 0;
681 dispose_chunk(object
->chunks
[i
].data
);
682 object
->chunks
[i
].data
= NULL
;
686 h
= hash(object
->type
, object
->key
, object
->key_size
,
687 log2ObjectHashTableSize
);
688 assert(objectHashTable
[h
] == object
);
689 objectHashTable
[h
] = NULL
;
692 object
->previous
->next
= object
->next
;
693 if(object_list
== object
)
694 object_list
= object
->next
;
696 object
->next
->previous
= object
->previous
;
697 if(object_list_end
== object
)
698 object_list_end
= object
->previous
;
699 object
->previous
= NULL
;
703 privateObjectCount
++;
705 if(object
->refcount
== 0)
706 destroyObject(object
);
709 object
->flags
|= OBJECT_LINEAR
;
714 abortObject(ObjectPtr object
, int code
, AtomPtr message
)
720 object
->flags
&= ~(OBJECT_INITIAL
| OBJECT_VALIDATING
);
721 object
->flags
|= OBJECT_ABORTED
;
723 if(object
->message
) releaseAtom(object
->message
);
724 object
->message
= message
;
726 object
->date
= object
->age
;
727 object
->expires
= object
->age
;
728 object
->last_modified
= -1;
729 if(object
->etag
) free(object
->etag
);
731 if(object
->headers
) releaseAtom(object
->headers
);
732 object
->headers
= NULL
;
734 for(i
= 0; i
< object
->numchunks
; i
++) {
735 if(object
->chunks
[i
].data
) {
736 if(!object
->chunks
[i
].locked
) {
737 dispose_chunk(object
->chunks
[i
].data
);
738 object
->chunks
[i
].data
= NULL
;
739 object
->chunks
[i
].size
= 0;
743 privatiseObject(object
, 0);
747 supersedeObject(ObjectPtr object
)
749 object
->flags
|= OBJECT_SUPERSEDED
;
750 destroyDiskEntry(object
, 1);
751 privatiseObject(object
, 0);
752 notifyObject(object
);
756 notifyObject(ObjectPtr object
)
758 retainObject(object
);
759 signalCondition(&object
->condition
);
760 releaseObject(object
);
764 discardObjectsHandler(TimeEventHandlerPtr event
)
766 return discardObjects(0, 0);
770 writeoutObjects(int all
)
772 ObjectPtr object
= object_list
;
777 if(diskIsClean
) return;
784 if(objects
>= maxObjectsWhenIdle
||
785 bytes
>= maxWriteoutWhenIdle
) {
786 if(workToDo()) return;
791 n
= writeoutToDisk(object
, -1, all
? -1 : maxWriteoutWhenIdle
);
793 } while(!all
&& n
== maxWriteoutWhenIdle
);
795 object
= object
->next
;
801 discardObjects(int all
, int force
)
805 static int in_discardObjects
= 0;
806 TimeEventHandlerPtr event
;
808 if(in_discardObjects
)
811 in_discardObjects
= 1;
813 if(all
|| force
|| used_chunks
>= CHUNKS(chunkHighMark
) ||
814 publicObjectCount
>= publicObjectLowMark
||
815 publicObjectCount
+ privateObjectCount
>= objectHighMark
) {
816 object
= object_list_end
;
818 (all
|| force
|| used_chunks
>= CHUNKS(chunkLowMark
))) {
819 if(force
|| ((object
->flags
& OBJECT_PUBLIC
) &&
820 object
->numchunks
> CHUNKS(chunkLowMark
) / 4)) {
822 for(j
= 0; j
< object
->numchunks
; j
++) {
823 if(object
->chunks
[j
].locked
) {
826 if(object
->chunks
[j
].size
< CHUNK_SIZE
) {
829 writeoutToDisk(object
, (j
+ 1) * CHUNK_SIZE
, -1);
830 dispose_chunk(object
->chunks
[j
].data
);
831 object
->chunks
[j
].data
= NULL
;
832 object
->chunks
[j
].size
= 0;
836 object
= object
->previous
;
840 object
= object_list_end
;
843 used_chunks
- i
> CHUNKS(chunkLowMark
) ||
844 used_chunks
> CHUNKS(chunkCriticalMark
) ||
845 publicObjectCount
> publicObjectLowMark
)) {
846 ObjectPtr next_object
= object
->previous
;
847 if(object
->refcount
== 0) {
848 i
+= object
->numchunks
;
849 writeoutToDisk(object
, object
->size
, -1);
850 privatiseObject(object
, 0);
851 } else if(all
|| force
) {
852 writeoutToDisk(object
, object
->size
, -1);
853 destroyDiskEntry(object
, 0);
855 object
= next_object
;
858 object
= object_list_end
;
859 if(force
|| used_chunks
> CHUNKS(chunkCriticalMark
)) {
860 if(used_chunks
> CHUNKS(chunkCriticalMark
)) {
862 "Short on chunk memory -- "
863 "attempting to punch holes "
864 "in the middle of objects.\n");
867 (force
|| used_chunks
> CHUNKS(chunkCriticalMark
))) {
868 if(force
|| (object
->flags
& OBJECT_PUBLIC
)) {
870 for(j
= object
->numchunks
- 1; j
>= 0; j
--) {
871 if(object
->chunks
[j
].locked
)
873 if(object
->chunks
[j
].size
< CHUNK_SIZE
)
875 writeoutToDisk(object
, (j
+ 1) * CHUNK_SIZE
, -1);
876 dispose_chunk(object
->chunks
[j
].data
);
877 object
->chunks
[j
].data
= NULL
;
878 object
->chunks
[j
].size
= 0;
881 object
= object
->previous
;
884 event
= scheduleTimeEvent(2, discardObjectsHandler
, 0, NULL
);
886 objectExpiryScheduled
= 1;
888 objectExpiryScheduled
= 0;
889 do_log(L_ERROR
, "Couldn't schedule object expiry.\n");
892 objectExpiryScheduled
= 0;
896 if(privateObjectCount
+ publicObjectCount
!= 0) {
898 "Discarded all objects, "
899 "%d + %d objects left (%d chunks and %d atoms used).\n",
900 publicObjectCount
, privateObjectCount
,
901 used_chunks
, used_atoms
);
902 } else if(used_chunks
!= 0) {
904 "Discarded all objects, "
905 "%d chunks and %d atoms left.\n",
906 used_chunks
, used_atoms
);
911 in_discardObjects
= 0;
915 CacheControlRec no_cache_control
= {0, -1, -1, -1, -1};
918 objectIsStale(ObjectPtr object
, CacheControlPtr cache_control
)
920 int stale
= 0x7FFFFFFF;
922 int max_age
, s_maxage
;
925 if(object
->flags
& OBJECT_INITIAL
)
928 if(object
->date
>= 0)
930 else if(object
->age
>= 0)
933 date
= current_time
.tv_sec
;
935 if(cache_control
== NULL
)
936 cache_control
= &no_cache_control
;
937 flags
= object
->cache_control
| cache_control
->flags
;
939 if(cache_control
->max_age
>= 0) {
940 if(object
->max_age
>= 0)
941 max_age
= MIN(cache_control
->max_age
, object
->max_age
);
943 max_age
= cache_control
->max_age
;
945 max_age
= object
->max_age
;
947 if(cache_control
->s_maxage
>= 0) {
948 if(object
->s_maxage
>= 0)
949 s_maxage
= MIN(cache_control
->s_maxage
, object
->s_maxage
);
951 s_maxage
= cache_control
->s_maxage
;
953 s_maxage
= object
->s_maxage
;
956 stale
= MIN(stale
, object
->age
+ max_age
);
958 if(cacheIsShared
&& s_maxage
>= 0)
959 stale
= MIN(stale
, object
->age
+ s_maxage
);
961 if(object
->expires
>= 0 || object
->max_age
>= 0)
962 stale
= MIN(stale
, object
->age
+ maxExpiresAge
);
964 stale
= MIN(stale
, object
->age
+ maxAge
);
966 /* RFC 2616 14.9.3: server-side max-age overrides expires */
968 if(object
->expires
>= 0 && object
->max_age
< 0) {
969 /* This protects against clock skew */
970 stale
= MIN(stale
, object
->age
+ object
->expires
- date
);
973 if(object
->expires
< 0 && object
->max_age
< 0) {
974 /* No server-side information -- heuristic expiration */
975 if(object
->last_modified
>= 0)
976 /* Again, take care of clock skew */
979 (date
- object
->last_modified
) * maxAgeFraction
);
981 stale
= MIN(stale
, object
->age
+ maxNoModifiedAge
);
984 if(!(flags
& CACHE_MUST_REVALIDATE
) &&
985 !(cacheIsShared
&& (flags
& CACHE_PROXY_REVALIDATE
))) {
986 /* Client side can relax transparency */
987 if(cache_control
->min_fresh
>= 0) {
988 if(cache_control
->max_stale
>= 0)
989 stale
= MIN(stale
- cache_control
->min_fresh
,
990 stale
+ cache_control
->max_stale
);
992 stale
= stale
- cache_control
->min_fresh
;
993 } else if(cache_control
->max_stale
>= 0) {
994 stale
= stale
+ cache_control
->max_stale
;
998 return current_time
.tv_sec
> stale
;
1002 objectMustRevalidate(ObjectPtr object
, CacheControlPtr cache_control
)
1006 if(cache_control
== NULL
)
1007 cache_control
= &no_cache_control
;
1009 flags
= object
->cache_control
| cache_control
->flags
;
1011 flags
= cache_control
->flags
;
1013 if(flags
& (CACHE_NO
| CACHE_NO_HIDDEN
| CACHE_NO_STORE
))
1016 if(cacheIsShared
&& (flags
& CACHE_PRIVATE
))
1019 if(!mindlesslyCacheVary
&& (flags
& CACHE_VARY
))
1022 if(dontCacheCookies
&& (flags
& CACHE_COOKIE
))
1026 return objectIsStale(object
, cache_control
);