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
+ 1);
201 if(object
->key
== NULL
) {
205 memcpy(object
->key
, key
, key_size
);
206 /* In order to make it more convenient to use keys as strings,
207 they are NUL-terminated. */
208 object
->key
[key_size
] = '\0';
209 object
->key_size
= key_size
;
210 object
->flags
= (public?OBJECT_PUBLIC
:0) | OBJECT_INITIAL
;
212 h
= hash(object
->type
, object
->key
, object
->key_size
,
213 log2ObjectHashTableSize
);
214 if(objectHashTable
[h
]) {
215 writeoutToDisk(objectHashTable
[h
], objectHashTable
[h
]->size
, -1);
216 privatiseObject(objectHashTable
[h
], 0);
217 assert(!objectHashTable
[h
]);
219 objectHashTable
[h
] = object
;
220 object
->next
= object_list
;
221 object
->previous
= NULL
;
223 object_list
->previous
= object
;
224 object_list
= object
;
226 object_list_end
= object
;
229 object
->previous
= NULL
;
231 object
->abort_data
= NULL
;
233 object
->message
= NULL
;
234 initCondition(&object
->condition
);
235 object
->headers
= NULL
;
237 object
->numchunks
= 0;
238 object
->chunks
= NULL
;
242 object
->expires
= -1;
243 object
->last_modified
= -1;
246 object
->cache_control
= 0;
247 object
->max_age
= -1;
248 object
->s_maxage
= -1;
250 object
->requestor
= NULL
;
251 object
->disk_entry
= NULL
;
252 if(object
->flags
& OBJECT_PUBLIC
)
255 privateObjectCount
++;
256 object
->refcount
= 1;
258 if(public && fromdisk
)
259 objectGetFromDisk(object
);
264 objectMetadataChanged(ObjectPtr object
, int revalidate
)
267 revalidateDiskEntry(object
);
269 object
->flags
&= ~OBJECT_DISK_ENTRY_COMPLETE
;
270 dirtyDiskEntry(object
);
276 retainObject(ObjectPtr object
)
278 do_log(D_REFCOUNT
, "O 0x%lx %d++\n",
279 (unsigned long)object
, object
->refcount
);
285 releaseObject(ObjectPtr object
)
287 do_log(D_REFCOUNT
, "O 0x%lx %d--\n",
288 (unsigned long)object
, object
->refcount
);
290 if(object
->refcount
== 0) {
291 assert(!object
->condition
.handlers
&&
292 !(object
->flags
& OBJECT_INPROGRESS
));
293 if(!(object
->flags
& OBJECT_PUBLIC
))
294 destroyObject(object
);
299 releaseNotifyObject(ObjectPtr object
)
301 do_log(D_REFCOUNT
, "O 0x%lx %d--\n",
302 (unsigned long)object
, object
->refcount
);
304 if(object
->refcount
> 0) {
305 notifyObject(object
);
307 assert(!object
->condition
.handlers
&&
308 !(object
->flags
& OBJECT_INPROGRESS
));
309 if(!(object
->flags
& OBJECT_PUBLIC
))
310 destroyObject(object
);
315 lockChunk(ObjectPtr object
, int i
)
317 do_log(D_LOCK
, "Lock 0x%lx[%d]: ", (unsigned long)object
, i
);
319 if(i
>= object
->numchunks
)
320 objectSetChunks(object
, i
+ 1);
321 object
->chunks
[i
].locked
++;
322 do_log(D_LOCK
, "%d\n", object
->chunks
[i
].locked
);
326 unlockChunk(ObjectPtr object
, int i
)
328 do_log(D_LOCK
, "Unlock 0x%lx[%d]: ", (unsigned long)object
, i
);
329 assert(i
>= 0 && i
< object
->numchunks
);
330 assert(object
->chunks
[i
].locked
> 0);
331 object
->chunks
[i
].locked
--;
332 do_log(D_LOCK
, "%d\n", object
->chunks
[i
].locked
);
336 objectSetChunks(ObjectPtr object
, int numchunks
)
340 if(numchunks
<= object
->numchunks
)
343 if(object
->length
>= 0)
344 n
= MAX(numchunks
, (object
->length
+ (CHUNK_SIZE
- 1)) / CHUNK_SIZE
);
347 MAX(object
->numchunks
+ 2, object
->numchunks
* 5 / 4));
350 assert(object
->chunks
== NULL
);
351 } else if(object
->numchunks
== 0) {
352 object
->chunks
= calloc(n
, sizeof(ChunkRec
));
353 if(object
->chunks
== NULL
) {
356 object
->numchunks
= n
;
359 newchunks
= realloc(object
->chunks
, n
* sizeof(ChunkRec
));
360 if(newchunks
== NULL
)
362 memset(newchunks
+ object
->numchunks
, 0,
363 (n
- object
->numchunks
) * sizeof(ChunkRec
));
364 object
->chunks
= newchunks
;
365 object
->numchunks
= n
;
371 objectPartial(ObjectPtr object
, int length
, struct _Atom
*headers
)
373 object
->headers
= headers
;
376 if(object
->size
> length
) {
377 abortObject(object
, 502,
378 internAtom("Inconsistent Content-Length"));
379 notifyObject(object
);
385 object
->length
= length
;
387 object
->flags
&= ~OBJECT_INITIAL
;
388 revalidateDiskEntry(object
);
389 notifyObject(object
);
394 objectAddChunk(ObjectPtr object
, const char *data
, int offset
, int plen
)
396 int i
= offset
/ CHUNK_SIZE
;
399 assert(offset
% CHUNK_SIZE
== 0);
400 assert(plen
<= CHUNK_SIZE
);
402 if(object
->numchunks
<= i
) {
403 rc
= objectSetChunks(object
, i
+ 1);
408 lockChunk(object
, i
);
410 if(object
->chunks
[i
].data
== NULL
) {
411 object
->chunks
[i
].data
= get_chunk();
412 if(object
->chunks
[i
].data
== NULL
)
416 if(object
->chunks
[i
].size
>= plen
) {
417 unlockChunk(object
, i
);
421 if(object
->size
< offset
+ plen
)
422 object
->size
= offset
+ plen
;
423 object
->chunks
[i
].size
= plen
;
424 memcpy(object
->chunks
[i
].data
, data
, plen
);
425 unlockChunk(object
, i
);
429 unlockChunk(object
, i
);
434 objectAddChunkEnd(ObjectPtr object
, const char *data
, int offset
, int plen
)
436 int i
= offset
/ CHUNK_SIZE
;
439 assert(offset
% CHUNK_SIZE
!= 0 &&
440 offset
% CHUNK_SIZE
+ plen
<= CHUNK_SIZE
);
442 if(object
->numchunks
<= i
) {
443 rc
= objectSetChunks(object
, i
+ 1);
448 lockChunk(object
, i
);
450 if(object
->chunks
[i
].data
== NULL
)
451 object
->chunks
[i
].data
= get_chunk();
452 if(object
->chunks
[i
].data
== NULL
)
455 if(offset
> object
->size
) {
459 if(object
->chunks
[i
].size
< offset
% CHUNK_SIZE
) {
463 if(object
->size
< offset
+ plen
)
464 object
->size
= offset
+ plen
;
465 object
->chunks
[i
].size
= offset
% CHUNK_SIZE
+ plen
;
466 memcpy(object
->chunks
[i
].data
+ (offset
% CHUNK_SIZE
),
469 unlockChunk(object
, i
);
473 unlockChunk(object
, i
);
478 objectAddData(ObjectPtr object
, const char *data
, int offset
, int len
)
482 do_log(D_OBJECT_DATA
, "Adding data to 0x%lx (%d) at %d: %d bytes\n",
483 (unsigned long)object
, object
->length
, offset
, len
);
488 if(object
->length
>= 0) {
489 if(offset
+ len
> object
->length
) {
491 "Inconsistent object length (%d, should be at least %d).\n",
492 object
->length
, offset
+ len
);
493 object
->length
= offset
+ len
;
497 object
->flags
&= ~OBJECT_FAILED
;
499 if(offset
+ len
>= object
->numchunks
* CHUNK_SIZE
) {
500 rc
= objectSetChunks(object
, (offset
+ len
- 1) / CHUNK_SIZE
+ 1);
506 if(offset
% CHUNK_SIZE
!= 0) {
507 int plen
= CHUNK_SIZE
- offset
% CHUNK_SIZE
;
510 rc
= objectAddChunkEnd(object
, data
, offset
, plen
);
520 int plen
= (len
>= CHUNK_SIZE
) ? CHUNK_SIZE
: len
;
521 rc
= objectAddChunk(object
, data
, offset
, plen
);
534 objectPrintf(ObjectPtr object
, int offset
, const char *format
, ...)
540 va_start(args
, format
);
541 buf
= vsprintf_a(format
, args
);
545 abortObject(object
, 500, internAtom("Couldn't allocate string"));
549 rc
= objectAddData(object
, buf
, offset
, strlen(buf
));
552 abortObject(object
, 500, internAtom("Couldn't add data to object"));
556 objectHoleSize(ObjectPtr object
, int offset
)
560 if(offset
< 0 || offset
/ CHUNK_SIZE
>= object
->numchunks
)
563 if(offset
% CHUNK_SIZE
!= 0) {
564 if(object
->chunks
[offset
/ CHUNK_SIZE
].size
> offset
% CHUNK_SIZE
)
567 size
+= CHUNK_SIZE
- offset
% CHUNK_SIZE
;
568 offset
+= CHUNK_SIZE
- offset
% CHUNK_SIZE
;
576 for(i
= offset
/ CHUNK_SIZE
; i
< object
->numchunks
; i
++) {
577 if(object
->chunks
[i
].size
== 0)
582 if(i
>= object
->numchunks
)
588 /* Returns 2 if the data is wholly in memory, 1 if it's available on disk */
590 objectHasData(ObjectPtr object
, int from
, int to
)
592 int first
= from
/ CHUNK_SIZE
;
593 int last
= to
/ CHUNK_SIZE
;
597 if(object
->length
>= 0)
606 if(to
> object
->size
) {
611 if(last
> object
->numchunks
||
612 object
->chunks
[last
].size
> to
% CHUNK_SIZE
) {
617 for(i
= last
- 1; i
>= first
; i
--) {
618 if(object
->chunks
[i
].size
< CHUNK_SIZE
) {
619 upto
= (i
+ 1) * CHUNK_SIZE
;
627 if(object
->flags
& OBJECT_DISK_ENTRY_COMPLETE
)
630 if(diskEntrySize(object
) >= upto
)
637 destroyObject(ObjectPtr object
)
641 assert(object
->refcount
== 0 && !object
->requestor
);
642 assert(!object
->condition
.handlers
&&
643 (object
->flags
& OBJECT_INPROGRESS
) == 0);
645 if(object
->disk_entry
)
646 destroyDiskEntry(object
, 0);
648 if(object
->flags
& OBJECT_PUBLIC
) {
649 privatiseObject(object
, 0);
652 if(object
->message
) releaseAtom(object
->message
);
653 if(object
->key
) free(object
->key
);
654 if(object
->headers
) releaseAtom(object
->headers
);
655 if(object
->etag
) free(object
->etag
);
656 if(object
->via
) releaseAtom(object
->via
);
657 for(i
= 0; i
< object
->numchunks
; i
++) {
658 assert(!object
->chunks
[i
].locked
);
659 if(object
->chunks
[i
].data
)
660 dispose_chunk(object
->chunks
[i
].data
);
661 object
->chunks
[i
].data
= NULL
;
662 object
->chunks
[i
].size
= 0;
664 if(object
->chunks
) free(object
->chunks
);
665 privateObjectCount
--;
671 privatiseObject(ObjectPtr object
, int linear
)
674 if(!(object
->flags
& OBJECT_PUBLIC
)) {
676 object
->flags
|= OBJECT_LINEAR
;
680 if(object
->disk_entry
)
681 destroyDiskEntry(object
, 0);
682 object
->flags
&= ~OBJECT_PUBLIC
;
684 for(i
= 0; i
< object
->numchunks
; i
++) {
685 if(object
->chunks
[i
].locked
)
687 if(object
->chunks
[i
].data
) {
688 object
->chunks
[i
].size
= 0;
689 dispose_chunk(object
->chunks
[i
].data
);
690 object
->chunks
[i
].data
= NULL
;
694 h
= hash(object
->type
, object
->key
, object
->key_size
,
695 log2ObjectHashTableSize
);
696 assert(objectHashTable
[h
] == object
);
697 objectHashTable
[h
] = NULL
;
700 object
->previous
->next
= object
->next
;
701 if(object_list
== object
)
702 object_list
= object
->next
;
704 object
->next
->previous
= object
->previous
;
705 if(object_list_end
== object
)
706 object_list_end
= object
->previous
;
707 object
->previous
= NULL
;
711 privateObjectCount
++;
713 if(object
->refcount
== 0)
714 destroyObject(object
);
717 object
->flags
|= OBJECT_LINEAR
;
722 abortObject(ObjectPtr object
, int code
, AtomPtr message
)
728 object
->flags
&= ~(OBJECT_INITIAL
| OBJECT_VALIDATING
);
729 object
->flags
|= OBJECT_ABORTED
;
731 if(object
->message
) releaseAtom(object
->message
);
732 object
->message
= message
;
734 object
->date
= object
->age
;
735 object
->expires
= object
->age
;
736 object
->last_modified
= -1;
737 if(object
->etag
) free(object
->etag
);
739 if(object
->headers
) releaseAtom(object
->headers
);
740 object
->headers
= NULL
;
742 for(i
= 0; i
< object
->numchunks
; i
++) {
743 if(object
->chunks
[i
].data
) {
744 if(!object
->chunks
[i
].locked
) {
745 dispose_chunk(object
->chunks
[i
].data
);
746 object
->chunks
[i
].data
= NULL
;
747 object
->chunks
[i
].size
= 0;
751 privatiseObject(object
, 0);
755 supersedeObject(ObjectPtr object
)
757 object
->flags
|= OBJECT_SUPERSEDED
;
758 destroyDiskEntry(object
, 1);
759 privatiseObject(object
, 0);
760 notifyObject(object
);
764 notifyObject(ObjectPtr object
)
766 retainObject(object
);
767 signalCondition(&object
->condition
);
768 releaseObject(object
);
772 discardObjectsHandler(TimeEventHandlerPtr event
)
774 return discardObjects(0, 0);
778 writeoutObjects(int all
)
780 ObjectPtr object
= object_list
;
785 if(diskIsClean
) return;
792 if(objects
>= maxObjectsWhenIdle
||
793 bytes
>= maxWriteoutWhenIdle
) {
794 if(workToDo()) return;
799 n
= writeoutToDisk(object
, -1, all
? -1 : maxWriteoutWhenIdle
);
801 } while(!all
&& n
== maxWriteoutWhenIdle
);
803 object
= object
->next
;
809 discardObjects(int all
, int force
)
813 static int in_discardObjects
= 0;
814 TimeEventHandlerPtr event
;
816 if(in_discardObjects
)
819 in_discardObjects
= 1;
821 if(all
|| force
|| used_chunks
>= CHUNKS(chunkHighMark
) ||
822 publicObjectCount
>= publicObjectLowMark
||
823 publicObjectCount
+ privateObjectCount
>= objectHighMark
) {
824 object
= object_list_end
;
826 (all
|| force
|| used_chunks
>= CHUNKS(chunkLowMark
))) {
827 if(force
|| ((object
->flags
& OBJECT_PUBLIC
) &&
828 object
->numchunks
> CHUNKS(chunkLowMark
) / 4)) {
830 for(j
= 0; j
< object
->numchunks
; j
++) {
831 if(object
->chunks
[j
].locked
) {
834 if(object
->chunks
[j
].size
< CHUNK_SIZE
) {
837 writeoutToDisk(object
, (j
+ 1) * CHUNK_SIZE
, -1);
838 dispose_chunk(object
->chunks
[j
].data
);
839 object
->chunks
[j
].data
= NULL
;
840 object
->chunks
[j
].size
= 0;
844 object
= object
->previous
;
848 object
= object_list_end
;
851 used_chunks
- i
> CHUNKS(chunkLowMark
) ||
852 used_chunks
> CHUNKS(chunkCriticalMark
) ||
853 publicObjectCount
> publicObjectLowMark
)) {
854 ObjectPtr next_object
= object
->previous
;
855 if(object
->refcount
== 0) {
856 i
+= object
->numchunks
;
857 writeoutToDisk(object
, object
->size
, -1);
858 privatiseObject(object
, 0);
859 } else if(all
|| force
) {
860 writeoutToDisk(object
, object
->size
, -1);
861 destroyDiskEntry(object
, 0);
863 object
= next_object
;
866 object
= object_list_end
;
867 if(force
|| used_chunks
> CHUNKS(chunkCriticalMark
)) {
868 if(used_chunks
> CHUNKS(chunkCriticalMark
)) {
870 "Short on chunk memory -- "
871 "attempting to punch holes "
872 "in the middle of objects.\n");
875 (force
|| used_chunks
> CHUNKS(chunkCriticalMark
))) {
876 if(force
|| (object
->flags
& OBJECT_PUBLIC
)) {
878 for(j
= object
->numchunks
- 1; j
>= 0; j
--) {
879 if(object
->chunks
[j
].locked
)
881 if(object
->chunks
[j
].size
< CHUNK_SIZE
)
883 writeoutToDisk(object
, (j
+ 1) * CHUNK_SIZE
, -1);
884 dispose_chunk(object
->chunks
[j
].data
);
885 object
->chunks
[j
].data
= NULL
;
886 object
->chunks
[j
].size
= 0;
889 object
= object
->previous
;
892 event
= scheduleTimeEvent(2, discardObjectsHandler
, 0, NULL
);
894 objectExpiryScheduled
= 1;
896 objectExpiryScheduled
= 0;
897 do_log(L_ERROR
, "Couldn't schedule object expiry.\n");
900 objectExpiryScheduled
= 0;
904 if(privateObjectCount
+ publicObjectCount
!= 0) {
906 "Discarded all objects, "
907 "%d + %d objects left (%d chunks and %d atoms used).\n",
908 publicObjectCount
, privateObjectCount
,
909 used_chunks
, used_atoms
);
910 } else if(used_chunks
!= 0) {
912 "Discarded all objects, "
913 "%d chunks and %d atoms left.\n",
914 used_chunks
, used_atoms
);
919 in_discardObjects
= 0;
923 CacheControlRec no_cache_control
= {0, -1, -1, -1, -1};
926 objectIsStale(ObjectPtr object
, CacheControlPtr cache_control
)
928 int stale
= 0x7FFFFFFF;
930 int max_age
, s_maxage
;
933 if(object
->flags
& OBJECT_INITIAL
)
936 if(object
->date
>= 0)
938 else if(object
->age
>= 0)
941 date
= current_time
.tv_sec
;
943 if(cache_control
== NULL
)
944 cache_control
= &no_cache_control
;
945 flags
= object
->cache_control
| cache_control
->flags
;
947 if(cache_control
->max_age
>= 0) {
948 if(object
->max_age
>= 0)
949 max_age
= MIN(cache_control
->max_age
, object
->max_age
);
951 max_age
= cache_control
->max_age
;
953 max_age
= object
->max_age
;
955 if(cache_control
->s_maxage
>= 0) {
956 if(object
->s_maxage
>= 0)
957 s_maxage
= MIN(cache_control
->s_maxage
, object
->s_maxage
);
959 s_maxage
= cache_control
->s_maxage
;
961 s_maxage
= object
->s_maxage
;
964 stale
= MIN(stale
, object
->age
+ max_age
);
966 if(cacheIsShared
&& s_maxage
>= 0)
967 stale
= MIN(stale
, object
->age
+ s_maxage
);
969 if(object
->expires
>= 0 || object
->max_age
>= 0)
970 stale
= MIN(stale
, object
->age
+ maxExpiresAge
);
972 stale
= MIN(stale
, object
->age
+ maxAge
);
974 /* RFC 2616 14.9.3: server-side max-age overrides expires */
976 if(object
->expires
>= 0 && object
->max_age
< 0) {
977 /* This protects against clock skew */
978 stale
= MIN(stale
, object
->age
+ object
->expires
- date
);
981 if(object
->expires
< 0 && object
->max_age
< 0) {
982 /* No server-side information -- heuristic expiration */
983 if(object
->last_modified
>= 0)
984 /* Again, take care of clock skew */
987 (date
- object
->last_modified
) * maxAgeFraction
);
989 stale
= MIN(stale
, object
->age
+ maxNoModifiedAge
);
992 if(!(flags
& CACHE_MUST_REVALIDATE
) &&
993 !(cacheIsShared
&& (flags
& CACHE_PROXY_REVALIDATE
))) {
994 /* Client side can relax transparency */
995 if(cache_control
->min_fresh
>= 0) {
996 if(cache_control
->max_stale
>= 0)
997 stale
= MIN(stale
- cache_control
->min_fresh
,
998 stale
+ cache_control
->max_stale
);
1000 stale
= stale
- cache_control
->min_fresh
;
1001 } else if(cache_control
->max_stale
>= 0) {
1002 stale
= stale
+ cache_control
->max_stale
;
1006 return current_time
.tv_sec
> stale
;
1010 objectMustRevalidate(ObjectPtr object
, CacheControlPtr cache_control
)
1014 if(cache_control
== NULL
)
1015 cache_control
= &no_cache_control
;
1017 flags
= object
->cache_control
| cache_control
->flags
;
1019 flags
= cache_control
->flags
;
1021 if(flags
& (CACHE_NO
| CACHE_NO_HIDDEN
| CACHE_NO_STORE
))
1024 if(cacheIsShared
&& (flags
& CACHE_PRIVATE
))
1027 if(!mindlesslyCacheVary
&& (flags
& CACHE_VARY
))
1030 if(dontCacheCookies
&& (flags
& CACHE_COOKIE
))
1034 return objectIsStale(object
, cache_control
);