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 if(key_size
>= 50000)
134 h
= hash(type
, key
, key_size
, log2ObjectHashTableSize
);
135 object
= objectHashTable
[h
];
138 if(object
->type
!= type
|| object
->key_size
!= key_size
||
139 memcmp(object
->key
, key
, key_size
) != 0) {
143 object
->next
->previous
= object
->previous
;
145 object
->previous
->next
= object
->next
;
146 if(object_list
== object
)
147 object_list
= object
->next
;
148 if(object_list_end
== object
)
149 object_list_end
= object
->previous
;
150 object
->previous
= NULL
;
151 object
->next
= object_list
;
153 object_list
->previous
= object
;
154 object_list
= object
;
156 object_list_end
= object
;
157 return retainObject(object
);
161 makeObject(int type
, const void *key
, int key_size
, int public, int fromdisk
,
162 RequestFunction request
, void* request_closure
)
167 object
= findObject(type
, key
, key_size
);
172 privatiseObject(object
, 0);
175 if(publicObjectCount
+ privateObjectCount
>= objectHighMark
) {
176 if(!objectExpiryScheduled
)
177 discardObjects(0, 0);
178 if(publicObjectCount
+ privateObjectCount
>= objectHighMark
) {
183 if(publicObjectCount
>= publicObjectLowMark
&&
184 !objectExpiryScheduled
) {
185 TimeEventHandlerPtr event
;
186 event
= scheduleTimeEvent(-1, discardObjectsHandler
, 0, NULL
);
188 objectExpiryScheduled
= 1;
190 do_log(L_ERROR
, "Couldn't schedule object expiry.\n");
193 object
= malloc(sizeof(ObjectRec
));
198 object
->request
= request
;
199 object
->request_closure
= request_closure
;
200 object
->key
= malloc(key_size
);
201 if(object
->key
== NULL
) {
205 memcpy(object
->key
, key
, key_size
);
206 object
->key_size
= key_size
;
207 object
->flags
= (public?OBJECT_PUBLIC
:0) | OBJECT_INITIAL
;
209 h
= hash(object
->type
, object
->key
, object
->key_size
,
210 log2ObjectHashTableSize
);
211 if(objectHashTable
[h
]) {
212 writeoutToDisk(objectHashTable
[h
], objectHashTable
[h
]->size
, -1);
213 privatiseObject(objectHashTable
[h
], 0);
214 assert(!objectHashTable
[h
]);
216 objectHashTable
[h
] = object
;
217 object
->next
= object_list
;
218 object
->previous
= NULL
;
220 object_list
->previous
= object
;
221 object_list
= object
;
223 object_list_end
= object
;
226 object
->previous
= NULL
;
228 object
->abort_data
= NULL
;
230 object
->message
= NULL
;
231 initCondition(&object
->condition
);
232 object
->headers
= NULL
;
234 object
->numchunks
= 0;
235 object
->chunks
= NULL
;
239 object
->expires
= -1;
240 object
->last_modified
= -1;
243 object
->cache_control
= 0;
244 object
->max_age
= -1;
245 object
->s_maxage
= -1;
247 object
->requestor
= NULL
;
248 object
->disk_entry
= NULL
;
249 if(object
->flags
& OBJECT_PUBLIC
)
252 privateObjectCount
++;
253 object
->refcount
= 1;
255 if(public && fromdisk
)
256 objectGetFromDisk(object
);
261 objectMetadataChanged(ObjectPtr object
, int revalidate
)
264 revalidateDiskEntry(object
);
266 object
->flags
&= ~OBJECT_DISK_ENTRY_COMPLETE
;
267 dirtyDiskEntry(object
);
273 retainObject(ObjectPtr object
)
275 do_log(D_REFCOUNT
, "O 0x%lx %d++\n",
276 (unsigned long)object
, object
->refcount
);
282 releaseObject(ObjectPtr object
)
284 do_log(D_REFCOUNT
, "O 0x%lx %d--\n",
285 (unsigned long)object
, object
->refcount
);
287 if(object
->refcount
== 0) {
288 assert(!object
->condition
.handlers
&&
289 !(object
->flags
& OBJECT_INPROGRESS
));
290 if(!(object
->flags
& OBJECT_PUBLIC
))
291 destroyObject(object
);
296 releaseNotifyObject(ObjectPtr object
)
298 do_log(D_REFCOUNT
, "O 0x%lx %d--\n",
299 (unsigned long)object
, object
->refcount
);
301 if(object
->refcount
> 0) {
302 notifyObject(object
);
304 assert(!object
->condition
.handlers
&&
305 !(object
->flags
& OBJECT_INPROGRESS
));
306 if(!(object
->flags
& OBJECT_PUBLIC
))
307 destroyObject(object
);
312 lockChunk(ObjectPtr object
, int i
)
314 do_log(D_LOCK
, "Lock 0x%lx[%d]: ", (unsigned long)object
, i
);
316 if(i
>= object
->numchunks
)
317 objectSetChunks(object
, i
+ 1);
318 object
->chunks
[i
].locked
++;
319 do_log(D_LOCK
, "%d\n", object
->chunks
[i
].locked
);
323 unlockChunk(ObjectPtr object
, int i
)
325 do_log(D_LOCK
, "Unlock 0x%lx[%d]: ", (unsigned long)object
, i
);
326 assert(i
>= 0 && i
< object
->numchunks
);
327 assert(object
->chunks
[i
].locked
> 0);
328 object
->chunks
[i
].locked
--;
329 do_log(D_LOCK
, "%d\n", object
->chunks
[i
].locked
);
333 objectSetChunks(ObjectPtr object
, int numchunks
)
337 if(numchunks
<= object
->numchunks
)
340 if(object
->length
>= 0)
341 n
= MAX(numchunks
, (object
->length
+ (CHUNK_SIZE
- 1)) / CHUNK_SIZE
);
344 MAX(object
->numchunks
+ 2, object
->numchunks
* 5 / 4));
347 assert(object
->chunks
== NULL
);
348 } else if(object
->numchunks
== 0) {
349 object
->chunks
= calloc(n
, sizeof(ChunkRec
));
350 if(object
->chunks
== NULL
) {
353 object
->numchunks
= n
;
356 newchunks
= realloc(object
->chunks
, n
* sizeof(ChunkRec
));
357 if(newchunks
== NULL
)
359 memset(newchunks
+ object
->numchunks
, 0,
360 (n
- object
->numchunks
) * sizeof(ChunkRec
));
361 object
->chunks
= newchunks
;
362 object
->numchunks
= n
;
368 objectPartial(ObjectPtr object
, int length
, struct _Atom
*headers
)
370 object
->headers
= headers
;
373 if(object
->size
> length
) {
374 abortObject(object
, 502,
375 internAtom("Inconsistent Content-Length"));
376 notifyObject(object
);
382 object
->length
= length
;
384 object
->flags
&= ~OBJECT_INITIAL
;
385 revalidateDiskEntry(object
);
386 notifyObject(object
);
391 objectAddChunk(ObjectPtr object
, const char *data
, int offset
, int plen
)
393 int i
= offset
/ CHUNK_SIZE
;
396 assert(offset
% CHUNK_SIZE
== 0);
397 assert(plen
<= CHUNK_SIZE
);
399 if(object
->numchunks
<= i
) {
400 rc
= objectSetChunks(object
, i
+ 1);
405 lockChunk(object
, i
);
407 if(object
->chunks
[i
].data
== NULL
) {
408 object
->chunks
[i
].data
= get_chunk();
409 if(object
->chunks
[i
].data
== NULL
)
413 if(object
->chunks
[i
].size
>= plen
) {
414 unlockChunk(object
, i
);
418 if(object
->size
< offset
+ plen
)
419 object
->size
= offset
+ plen
;
420 object
->chunks
[i
].size
= plen
;
421 memcpy(object
->chunks
[i
].data
, data
, plen
);
422 unlockChunk(object
, i
);
426 unlockChunk(object
, i
);
431 objectAddChunkEnd(ObjectPtr object
, const char *data
, int offset
, int plen
)
433 int i
= offset
/ CHUNK_SIZE
;
436 assert(offset
% CHUNK_SIZE
!= 0 &&
437 offset
% CHUNK_SIZE
+ plen
<= CHUNK_SIZE
);
439 if(object
->numchunks
<= i
) {
440 rc
= objectSetChunks(object
, i
+ 1);
445 lockChunk(object
, i
);
447 if(object
->chunks
[i
].data
== NULL
)
448 object
->chunks
[i
].data
= get_chunk();
449 if(object
->chunks
[i
].data
== NULL
)
452 if(offset
> object
->size
) {
456 if(object
->chunks
[i
].size
< offset
% CHUNK_SIZE
) {
460 if(object
->size
< offset
+ plen
)
461 object
->size
= offset
+ plen
;
462 object
->chunks
[i
].size
= offset
% CHUNK_SIZE
+ plen
;
463 memcpy(object
->chunks
[i
].data
+ (offset
% CHUNK_SIZE
),
466 unlockChunk(object
, i
);
470 unlockChunk(object
, i
);
475 objectAddData(ObjectPtr object
, const char *data
, int offset
, int len
)
479 do_log(D_OBJECT_DATA
, "Adding data to 0x%lx (%d) at %d: %d bytes\n",
480 (unsigned long)object
, object
->length
, offset
, len
);
485 if(object
->length
>= 0) {
486 if(offset
+ len
> object
->length
) {
488 "Inconsistent object length (%d, should be at least %d).\n",
489 object
->length
, offset
+ len
);
490 object
->length
= offset
+ len
;
494 object
->flags
&= ~OBJECT_FAILED
;
496 if(offset
+ len
>= object
->numchunks
* CHUNK_SIZE
) {
497 rc
= objectSetChunks(object
, (offset
+ len
- 1) / CHUNK_SIZE
+ 1);
503 if(offset
% CHUNK_SIZE
!= 0) {
504 int plen
= CHUNK_SIZE
- offset
% CHUNK_SIZE
;
507 rc
= objectAddChunkEnd(object
, data
, offset
, plen
);
517 int plen
= (len
>= CHUNK_SIZE
) ? CHUNK_SIZE
: len
;
518 rc
= objectAddChunk(object
, data
, offset
, plen
);
531 objectPrintf(ObjectPtr object
, int offset
, const char *format
, ...)
537 va_start(args
, format
);
538 buf
= vsprintf_a(format
, args
);
542 abortObject(object
, 500, internAtom("Couldn't allocate string"));
546 rc
= objectAddData(object
, buf
, offset
, strlen(buf
));
549 abortObject(object
, 500, internAtom("Couldn't add data to object"));
553 objectHoleSize(ObjectPtr object
, int offset
)
557 if(offset
< 0 || offset
/ CHUNK_SIZE
>= object
->numchunks
)
560 if(offset
% CHUNK_SIZE
!= 0) {
561 if(object
->chunks
[offset
/ CHUNK_SIZE
].size
> offset
% CHUNK_SIZE
)
564 size
+= CHUNK_SIZE
- offset
% CHUNK_SIZE
;
565 offset
+= CHUNK_SIZE
- offset
% CHUNK_SIZE
;
573 for(i
= offset
/ CHUNK_SIZE
; i
< object
->numchunks
; i
++) {
574 if(object
->chunks
[i
].size
== 0)
579 if(i
>= object
->numchunks
)
585 /* Returns 2 if the data is wholly in memory, 1 if it's available on disk */
587 objectHasData(ObjectPtr object
, int from
, int to
)
589 int first
= from
/ CHUNK_SIZE
;
590 int last
= to
/ CHUNK_SIZE
;
594 if(object
->length
>= 0)
603 if(to
> object
->size
) {
608 if(last
> object
->numchunks
||
609 object
->chunks
[last
].size
> to
% CHUNK_SIZE
) {
614 for(i
= last
- 1; i
>= first
; i
--) {
615 if(object
->chunks
[i
].size
< CHUNK_SIZE
) {
616 upto
= (i
+ 1) * CHUNK_SIZE
;
624 if(object
->flags
& OBJECT_DISK_ENTRY_COMPLETE
)
627 if(diskEntrySize(object
) >= upto
)
634 destroyObject(ObjectPtr object
)
638 assert(object
->refcount
== 0 && !object
->requestor
);
639 assert(!object
->condition
.handlers
&&
640 (object
->flags
& OBJECT_INPROGRESS
) == 0);
642 if(object
->disk_entry
)
643 destroyDiskEntry(object
, 0);
645 if(object
->flags
& OBJECT_PUBLIC
) {
646 privatiseObject(object
, 0);
649 if(object
->message
) releaseAtom(object
->message
);
650 if(object
->key
) free(object
->key
);
651 if(object
->headers
) releaseAtom(object
->headers
);
652 if(object
->etag
) free(object
->etag
);
653 if(object
->via
) releaseAtom(object
->via
);
654 for(i
= 0; i
< object
->numchunks
; i
++) {
655 assert(!object
->chunks
[i
].locked
);
656 if(object
->chunks
[i
].data
)
657 dispose_chunk(object
->chunks
[i
].data
);
658 object
->chunks
[i
].data
= NULL
;
659 object
->chunks
[i
].size
= 0;
661 if(object
->chunks
) free(object
->chunks
);
662 privateObjectCount
--;
668 privatiseObject(ObjectPtr object
, int linear
)
671 if(!(object
->flags
& OBJECT_PUBLIC
)) {
673 object
->flags
|= OBJECT_LINEAR
;
677 if(object
->disk_entry
)
678 destroyDiskEntry(object
, 0);
679 object
->flags
&= ~OBJECT_PUBLIC
;
681 for(i
= 0; i
< object
->numchunks
; i
++) {
682 if(object
->chunks
[i
].locked
)
684 if(object
->chunks
[i
].data
) {
685 object
->chunks
[i
].size
= 0;
686 dispose_chunk(object
->chunks
[i
].data
);
687 object
->chunks
[i
].data
= NULL
;
691 h
= hash(object
->type
, object
->key
, object
->key_size
,
692 log2ObjectHashTableSize
);
693 assert(objectHashTable
[h
] == object
);
694 objectHashTable
[h
] = NULL
;
697 object
->previous
->next
= object
->next
;
698 if(object_list
== object
)
699 object_list
= object
->next
;
701 object
->next
->previous
= object
->previous
;
702 if(object_list_end
== object
)
703 object_list_end
= object
->previous
;
704 object
->previous
= NULL
;
708 privateObjectCount
++;
710 if(object
->refcount
== 0)
711 destroyObject(object
);
714 object
->flags
|= OBJECT_LINEAR
;
719 abortObject(ObjectPtr object
, int code
, AtomPtr message
)
725 object
->flags
&= ~(OBJECT_INITIAL
| OBJECT_VALIDATING
);
726 object
->flags
|= OBJECT_ABORTED
;
728 if(object
->message
) releaseAtom(object
->message
);
729 object
->message
= message
;
731 object
->date
= object
->age
;
732 object
->expires
= object
->age
;
733 object
->last_modified
= -1;
734 if(object
->etag
) free(object
->etag
);
736 if(object
->headers
) releaseAtom(object
->headers
);
737 object
->headers
= NULL
;
739 for(i
= 0; i
< object
->numchunks
; i
++) {
740 if(object
->chunks
[i
].data
) {
741 if(!object
->chunks
[i
].locked
) {
742 dispose_chunk(object
->chunks
[i
].data
);
743 object
->chunks
[i
].data
= NULL
;
744 object
->chunks
[i
].size
= 0;
748 privatiseObject(object
, 0);
752 supersedeObject(ObjectPtr object
)
754 object
->flags
|= OBJECT_SUPERSEDED
;
755 destroyDiskEntry(object
, 1);
756 privatiseObject(object
, 0);
757 notifyObject(object
);
761 notifyObject(ObjectPtr object
)
763 retainObject(object
);
764 signalCondition(&object
->condition
);
765 releaseObject(object
);
769 discardObjectsHandler(TimeEventHandlerPtr event
)
771 return discardObjects(0, 0);
775 writeoutObjects(int all
)
777 ObjectPtr object
= object_list
;
782 if(diskIsClean
) return;
789 if(objects
>= maxObjectsWhenIdle
||
790 bytes
>= maxWriteoutWhenIdle
) {
791 if(workToDo()) return;
796 n
= writeoutToDisk(object
, -1, all
? -1 : maxWriteoutWhenIdle
);
798 } while(!all
&& n
== maxWriteoutWhenIdle
);
800 object
= object
->next
;
806 discardObjects(int all
, int force
)
810 static int in_discardObjects
= 0;
811 TimeEventHandlerPtr event
;
813 if(in_discardObjects
)
816 in_discardObjects
= 1;
818 if(all
|| force
|| used_chunks
>= CHUNKS(chunkHighMark
) ||
819 publicObjectCount
>= publicObjectLowMark
||
820 publicObjectCount
+ privateObjectCount
>= objectHighMark
) {
821 object
= object_list_end
;
823 (all
|| force
|| used_chunks
>= CHUNKS(chunkLowMark
))) {
824 if(force
|| ((object
->flags
& OBJECT_PUBLIC
) &&
825 object
->numchunks
> CHUNKS(chunkLowMark
) / 4)) {
827 for(j
= 0; j
< object
->numchunks
; j
++) {
828 if(object
->chunks
[j
].locked
) {
831 if(object
->chunks
[j
].size
< CHUNK_SIZE
) {
834 writeoutToDisk(object
, (j
+ 1) * CHUNK_SIZE
, -1);
835 dispose_chunk(object
->chunks
[j
].data
);
836 object
->chunks
[j
].data
= NULL
;
837 object
->chunks
[j
].size
= 0;
841 object
= object
->previous
;
845 object
= object_list_end
;
848 used_chunks
- i
> CHUNKS(chunkLowMark
) ||
849 used_chunks
> CHUNKS(chunkCriticalMark
) ||
850 publicObjectCount
> publicObjectLowMark
)) {
851 ObjectPtr next_object
= object
->previous
;
852 if(object
->refcount
== 0) {
853 i
+= object
->numchunks
;
854 writeoutToDisk(object
, object
->size
, -1);
855 privatiseObject(object
, 0);
856 } else if(all
|| force
) {
857 writeoutToDisk(object
, object
->size
, -1);
858 destroyDiskEntry(object
, 0);
860 object
= next_object
;
863 object
= object_list_end
;
864 if(force
|| used_chunks
> CHUNKS(chunkCriticalMark
)) {
865 if(used_chunks
> CHUNKS(chunkCriticalMark
)) {
867 "Short on chunk memory -- "
868 "attempting to punch holes "
869 "in the middle of objects.\n");
872 (force
|| used_chunks
> CHUNKS(chunkCriticalMark
))) {
873 if(force
|| (object
->flags
& OBJECT_PUBLIC
)) {
875 for(j
= object
->numchunks
- 1; j
>= 0; j
--) {
876 if(object
->chunks
[j
].locked
)
878 if(object
->chunks
[j
].size
< CHUNK_SIZE
)
880 writeoutToDisk(object
, (j
+ 1) * CHUNK_SIZE
, -1);
881 dispose_chunk(object
->chunks
[j
].data
);
882 object
->chunks
[j
].data
= NULL
;
883 object
->chunks
[j
].size
= 0;
886 object
= object
->previous
;
889 event
= scheduleTimeEvent(2, discardObjectsHandler
, 0, NULL
);
891 objectExpiryScheduled
= 1;
893 objectExpiryScheduled
= 0;
894 do_log(L_ERROR
, "Couldn't schedule object expiry.\n");
897 objectExpiryScheduled
= 0;
901 if(privateObjectCount
+ publicObjectCount
!= 0) {
903 "Discarded all objects, "
904 "%d + %d objects left (%d chunks and %d atoms used).\n",
905 publicObjectCount
, privateObjectCount
,
906 used_chunks
, used_atoms
);
907 } else if(used_chunks
!= 0) {
909 "Discarded all objects, "
910 "%d chunks and %d atoms left.\n",
911 used_chunks
, used_atoms
);
916 in_discardObjects
= 0;
920 CacheControlRec no_cache_control
= {0, -1, -1, -1, -1};
923 objectIsStale(ObjectPtr object
, CacheControlPtr cache_control
)
925 int stale
= 0x7FFFFFFF;
927 int max_age
, s_maxage
;
930 if(object
->flags
& OBJECT_INITIAL
)
933 if(object
->date
>= 0)
935 else if(object
->age
>= 0)
938 date
= current_time
.tv_sec
;
940 if(cache_control
== NULL
)
941 cache_control
= &no_cache_control
;
942 flags
= object
->cache_control
| cache_control
->flags
;
944 if(cache_control
->max_age
>= 0) {
945 if(object
->max_age
>= 0)
946 max_age
= MIN(cache_control
->max_age
, object
->max_age
);
948 max_age
= cache_control
->max_age
;
950 max_age
= object
->max_age
;
952 if(cache_control
->s_maxage
>= 0) {
953 if(object
->s_maxage
>= 0)
954 s_maxage
= MIN(cache_control
->s_maxage
, object
->s_maxage
);
956 s_maxage
= cache_control
->s_maxage
;
958 s_maxage
= object
->s_maxage
;
961 stale
= MIN(stale
, object
->age
+ max_age
);
963 if(cacheIsShared
&& s_maxage
>= 0)
964 stale
= MIN(stale
, object
->age
+ s_maxage
);
966 if(object
->expires
>= 0 || object
->max_age
>= 0)
967 stale
= MIN(stale
, object
->age
+ maxExpiresAge
);
969 stale
= MIN(stale
, object
->age
+ maxAge
);
971 /* RFC 2616 14.9.3: server-side max-age overrides expires */
973 if(object
->expires
>= 0 && object
->max_age
< 0) {
974 /* This protects against clock skew */
975 stale
= MIN(stale
, object
->age
+ object
->expires
- date
);
978 if(object
->expires
< 0 && object
->max_age
< 0) {
979 /* No server-side information -- heuristic expiration */
980 if(object
->last_modified
>= 0)
981 /* Again, take care of clock skew */
984 (date
- object
->last_modified
) * maxAgeFraction
);
986 stale
= MIN(stale
, object
->age
+ maxNoModifiedAge
);
989 if(!(flags
& CACHE_MUST_REVALIDATE
) &&
990 !(cacheIsShared
&& (flags
& CACHE_PROXY_REVALIDATE
))) {
991 /* Client side can relax transparency */
992 if(cache_control
->min_fresh
>= 0) {
993 if(cache_control
->max_stale
>= 0)
994 stale
= MIN(stale
- cache_control
->min_fresh
,
995 stale
+ cache_control
->max_stale
);
997 stale
= stale
- cache_control
->min_fresh
;
998 } else if(cache_control
->max_stale
>= 0) {
999 stale
= stale
+ cache_control
->max_stale
;
1003 return current_time
.tv_sec
> stale
;
1007 objectMustRevalidate(ObjectPtr object
, CacheControlPtr cache_control
)
1011 if(cache_control
== NULL
)
1012 cache_control
= &no_cache_control
;
1014 flags
= object
->cache_control
| cache_control
->flags
;
1016 flags
= cache_control
->flags
;
1018 if(flags
& (CACHE_NO
| CACHE_NO_HIDDEN
| CACHE_NO_STORE
))
1021 if(cacheIsShared
&& (flags
& CACHE_PRIVATE
))
1024 if(!mindlesslyCacheVary
&& (flags
& CACHE_VARY
))
1027 if(dontCacheCookies
&& (flags
& CACHE_COOKIE
))
1031 return objectIsStale(object
, cache_control
);