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
;
572 for(i
= offset
/ CHUNK_SIZE
; i
< object
->numchunks
; i
++) {
573 if(object
->chunks
[i
].size
== 0)
578 if(i
>= object
->numchunks
)
584 /* Returns 2 if the data is wholly in memory, 1 if it's available on disk */
586 objectHasData(ObjectPtr object
, int from
, int to
)
588 int first
= from
/ CHUNK_SIZE
;
589 int last
= to
/ CHUNK_SIZE
;
593 if(object
->length
>= 0)
602 if(to
> object
->size
) {
607 if(last
> object
->numchunks
||
608 object
->chunks
[last
].size
> to
% CHUNK_SIZE
) {
613 for(i
= last
- 1; i
>= first
; i
--) {
614 if(object
->chunks
[i
].size
< CHUNK_SIZE
) {
615 upto
= (i
+ 1) * CHUNK_SIZE
;
623 if(object
->flags
& OBJECT_DISK_ENTRY_COMPLETE
)
626 if(diskEntrySize(object
) >= upto
)
633 destroyObject(ObjectPtr object
)
637 assert(object
->refcount
== 0 && !object
->requestor
);
638 assert(!object
->condition
.handlers
&&
639 (object
->flags
& OBJECT_INPROGRESS
) == 0);
641 if(object
->disk_entry
)
642 destroyDiskEntry(object
, 0);
644 if(object
->flags
& OBJECT_PUBLIC
) {
645 privatiseObject(object
, 0);
648 if(object
->message
) releaseAtom(object
->message
);
649 if(object
->key
) free(object
->key
);
650 if(object
->headers
) releaseAtom(object
->headers
);
651 if(object
->etag
) free(object
->etag
);
652 if(object
->via
) releaseAtom(object
->via
);
653 for(i
= 0; i
< object
->numchunks
; i
++) {
654 assert(!object
->chunks
[i
].locked
);
655 if(object
->chunks
[i
].data
)
656 dispose_chunk(object
->chunks
[i
].data
);
657 object
->chunks
[i
].data
= NULL
;
658 object
->chunks
[i
].size
= 0;
660 if(object
->chunks
) free(object
->chunks
);
661 privateObjectCount
--;
667 privatiseObject(ObjectPtr object
, int linear
)
670 if(!(object
->flags
& OBJECT_PUBLIC
)) {
672 object
->flags
|= OBJECT_LINEAR
;
676 if(object
->disk_entry
)
677 destroyDiskEntry(object
, 0);
678 object
->flags
&= ~OBJECT_PUBLIC
;
680 for(i
= 0; i
< object
->numchunks
; i
++) {
681 if(object
->chunks
[i
].locked
)
683 if(object
->chunks
[i
].data
) {
684 object
->chunks
[i
].size
= 0;
685 dispose_chunk(object
->chunks
[i
].data
);
686 object
->chunks
[i
].data
= NULL
;
690 h
= hash(object
->type
, object
->key
, object
->key_size
,
691 log2ObjectHashTableSize
);
692 assert(objectHashTable
[h
] == object
);
693 objectHashTable
[h
] = NULL
;
696 object
->previous
->next
= object
->next
;
697 if(object_list
== object
)
698 object_list
= object
->next
;
700 object
->next
->previous
= object
->previous
;
701 if(object_list_end
== object
)
702 object_list_end
= object
->previous
;
703 object
->previous
= NULL
;
707 privateObjectCount
++;
709 if(object
->refcount
== 0)
710 destroyObject(object
);
713 object
->flags
|= OBJECT_LINEAR
;
718 abortObject(ObjectPtr object
, int code
, AtomPtr message
)
724 object
->flags
&= ~(OBJECT_INITIAL
| OBJECT_VALIDATING
);
725 object
->flags
|= OBJECT_ABORTED
;
727 if(object
->message
) releaseAtom(object
->message
);
728 object
->message
= message
;
730 object
->date
= object
->age
;
731 object
->expires
= object
->age
;
732 object
->last_modified
= -1;
733 if(object
->etag
) free(object
->etag
);
735 if(object
->headers
) releaseAtom(object
->headers
);
736 object
->headers
= NULL
;
738 for(i
= 0; i
< object
->numchunks
; i
++) {
739 if(object
->chunks
[i
].data
) {
740 if(!object
->chunks
[i
].locked
) {
741 dispose_chunk(object
->chunks
[i
].data
);
742 object
->chunks
[i
].data
= NULL
;
743 object
->chunks
[i
].size
= 0;
747 privatiseObject(object
, 0);
751 supersedeObject(ObjectPtr object
)
753 object
->flags
|= OBJECT_SUPERSEDED
;
754 destroyDiskEntry(object
, 1);
755 privatiseObject(object
, 0);
756 notifyObject(object
);
760 notifyObject(ObjectPtr object
)
762 retainObject(object
);
763 signalCondition(&object
->condition
);
764 releaseObject(object
);
768 discardObjectsHandler(TimeEventHandlerPtr event
)
770 return discardObjects(0, 0);
774 writeoutObjects(int all
)
776 ObjectPtr object
= object_list
;
781 if(diskIsClean
) return;
788 if(objects
>= maxObjectsWhenIdle
||
789 bytes
>= maxWriteoutWhenIdle
) {
790 if(workToDo()) return;
795 n
= writeoutToDisk(object
, -1, all
? -1 : maxWriteoutWhenIdle
);
797 } while(!all
&& n
== maxWriteoutWhenIdle
);
799 object
= object
->next
;
805 discardObjects(int all
, int force
)
809 static int in_discardObjects
= 0;
810 TimeEventHandlerPtr event
;
812 if(in_discardObjects
)
815 in_discardObjects
= 1;
817 if(all
|| force
|| used_chunks
>= CHUNKS(chunkHighMark
) ||
818 publicObjectCount
>= publicObjectLowMark
||
819 publicObjectCount
+ privateObjectCount
>= objectHighMark
) {
820 object
= object_list_end
;
822 (all
|| force
|| used_chunks
>= CHUNKS(chunkLowMark
))) {
823 if(force
|| ((object
->flags
& OBJECT_PUBLIC
) &&
824 object
->numchunks
> CHUNKS(chunkLowMark
) / 4)) {
826 for(j
= 0; j
< object
->numchunks
; j
++) {
827 if(object
->chunks
[j
].locked
) {
830 if(object
->chunks
[j
].size
< CHUNK_SIZE
) {
833 writeoutToDisk(object
, (j
+ 1) * CHUNK_SIZE
, -1);
834 dispose_chunk(object
->chunks
[j
].data
);
835 object
->chunks
[j
].data
= NULL
;
836 object
->chunks
[j
].size
= 0;
840 object
= object
->previous
;
844 object
= object_list_end
;
847 used_chunks
- i
> CHUNKS(chunkLowMark
) ||
848 used_chunks
> CHUNKS(chunkCriticalMark
) ||
849 publicObjectCount
> publicObjectLowMark
)) {
850 ObjectPtr next_object
= object
->previous
;
851 if(object
->refcount
== 0) {
852 i
+= object
->numchunks
;
853 writeoutToDisk(object
, object
->size
, -1);
854 privatiseObject(object
, 0);
855 } else if(all
|| force
) {
856 writeoutToDisk(object
, object
->size
, -1);
857 destroyDiskEntry(object
, 0);
859 object
= next_object
;
862 object
= object_list_end
;
863 if(force
|| used_chunks
> CHUNKS(chunkCriticalMark
)) {
864 if(used_chunks
> CHUNKS(chunkCriticalMark
)) {
866 "Short on chunk memory -- "
867 "attempting to punch holes "
868 "in the middle of objects.\n");
871 (force
|| used_chunks
> CHUNKS(chunkCriticalMark
))) {
872 if(force
|| (object
->flags
& OBJECT_PUBLIC
)) {
874 for(j
= object
->numchunks
- 1; j
>= 0; j
--) {
875 if(object
->chunks
[j
].locked
)
877 if(object
->chunks
[j
].size
< CHUNK_SIZE
)
879 writeoutToDisk(object
, (j
+ 1) * CHUNK_SIZE
, -1);
880 dispose_chunk(object
->chunks
[j
].data
);
881 object
->chunks
[j
].data
= NULL
;
882 object
->chunks
[j
].size
= 0;
885 object
= object
->previous
;
888 event
= scheduleTimeEvent(2, discardObjectsHandler
, 0, NULL
);
890 objectExpiryScheduled
= 1;
892 objectExpiryScheduled
= 0;
893 do_log(L_ERROR
, "Couldn't schedule object expiry.\n");
896 objectExpiryScheduled
= 0;
900 if(privateObjectCount
+ publicObjectCount
!= 0) {
902 "Discarded all objects, "
903 "%d + %d objects left (%d chunks and %d atoms used).\n",
904 publicObjectCount
, privateObjectCount
,
905 used_chunks
, used_atoms
);
906 } else if(used_chunks
!= 0) {
908 "Discarded all objects, "
909 "%d chunks and %d atoms left.\n",
910 used_chunks
, used_atoms
);
915 in_discardObjects
= 0;
919 CacheControlRec no_cache_control
= {0, -1, -1, -1, -1};
922 objectIsStale(ObjectPtr object
, CacheControlPtr cache_control
)
924 int stale
= 0x7FFFFFFF;
926 int max_age
, s_maxage
;
929 if(object
->flags
& OBJECT_INITIAL
)
932 if(object
->date
>= 0)
934 else if(object
->age
>= 0)
937 date
= current_time
.tv_sec
;
939 if(cache_control
== NULL
)
940 cache_control
= &no_cache_control
;
941 flags
= object
->cache_control
| cache_control
->flags
;
943 if(cache_control
->max_age
>= 0) {
944 if(object
->max_age
>= 0)
945 max_age
= MIN(cache_control
->max_age
, object
->max_age
);
947 max_age
= cache_control
->max_age
;
949 max_age
= object
->max_age
;
951 if(cache_control
->s_maxage
>= 0) {
952 if(object
->s_maxage
>= 0)
953 s_maxage
= MIN(cache_control
->s_maxage
, object
->s_maxage
);
955 s_maxage
= cache_control
->s_maxage
;
957 s_maxage
= object
->s_maxage
;
960 stale
= MIN(stale
, object
->age
+ max_age
);
962 if(cacheIsShared
&& s_maxage
>= 0)
963 stale
= MIN(stale
, object
->age
+ s_maxage
);
965 if(object
->expires
>= 0 || object
->max_age
>= 0)
966 stale
= MIN(stale
, object
->age
+ maxExpiresAge
);
968 stale
= MIN(stale
, object
->age
+ maxAge
);
970 /* RFC 2616 14.9.3: server-side max-age overrides expires */
972 if(object
->expires
>= 0 && object
->max_age
< 0) {
973 /* This protects against clock skew */
974 stale
= MIN(stale
, object
->age
+ object
->expires
- date
);
977 if(object
->expires
< 0 && object
->max_age
< 0) {
978 /* No server-side information -- heuristic expiration */
979 if(object
->last_modified
>= 0)
980 /* Again, take care of clock skew */
983 (date
- object
->last_modified
) * maxAgeFraction
);
985 stale
= MIN(stale
, object
->age
+ maxNoModifiedAge
);
988 if(!(flags
& CACHE_MUST_REVALIDATE
) &&
989 !(cacheIsShared
&& (flags
& CACHE_PROXY_REVALIDATE
))) {
990 /* Client side can relax transparency */
991 if(cache_control
->min_fresh
>= 0) {
992 if(cache_control
->max_stale
>= 0)
993 stale
= MIN(stale
- cache_control
->min_fresh
,
994 stale
+ cache_control
->max_stale
);
996 stale
= stale
- cache_control
->min_fresh
;
997 } else if(cache_control
->max_stale
>= 0) {
998 stale
= stale
+ cache_control
->max_stale
;
1002 return current_time
.tv_sec
> stale
;
1006 objectMustRevalidate(ObjectPtr object
, CacheControlPtr cache_control
)
1010 if(cache_control
== NULL
)
1011 cache_control
= &no_cache_control
;
1013 flags
= object
->cache_control
| cache_control
->flags
;
1015 flags
= cache_control
->flags
;
1017 if(flags
& (CACHE_NO
| CACHE_NO_HIDDEN
| CACHE_NO_STORE
))
1020 if(cacheIsShared
&& (flags
& CACHE_PRIVATE
))
1023 if(!mindlesslyCacheVary
&& (flags
& CACHE_VARY
))
1026 if(dontCacheCookies
&& (flags
& CACHE_COOKIE
))
1030 return objectIsStale(object
, cache_control
);