Reformat CHANGES.
[polipo.git] / object.c
blobb427504f1302f8329e6163f8c8fdfbf2ccf0860e
1 /*
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
20 THE SOFTWARE.
23 #include "polipo.h"
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;
46 int idleTime = 20;
47 int dontCacheCookies = 0;
49 void
50 preinitObject()
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 "
58 "when idle.");
59 CONFIG_VARIABLE_SETTABLE(cacheIsShared, CONFIG_BOOLEAN, configIntSetter,
60 "If false, ignore s-maxage and private.");
61 CONFIG_VARIABLE_SETTABLE(mindlesslyCacheVary, CONFIG_BOOLEAN,
62 configIntSetter,
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.");
82 void
83 initObject()
85 int q;
86 if(objectHighMark < 16) {
87 objectHighMark = 16;
88 do_log(L_WARN, "Impossibly low objectHighMark -- setting to %d.\n",
89 objectHighMark);
92 q = 0;
93 if(publicObjectLowMark == 0) q = 1;
94 if(publicObjectLowMark < 8 || publicObjectLowMark >= objectHighMark - 4) {
95 publicObjectLowMark = objectHighMark / 2;
96 if(!q)
97 do_log(L_WARN, "Impossible publicObjectLowMark value -- "
98 "setting to %d.\n", publicObjectLowMark);
101 q = 1;
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;
109 if(!q)
110 do_log(L_WARN, "Suspicious objectHashTableSize value -- "
111 "setting to %d.\n", objectHashTableSize);
113 object_list = NULL;
114 object_list_end = NULL;
115 publicObjectCount = 0;
116 privateObjectCount = 0;
117 objectHashTable = calloc(1 << log2ObjectHashTableSize,
118 sizeof(ObjectPtr));
119 if(!objectHashTable) {
120 do_log(L_ERROR, "Couldn't allocate object hash table.\n");
121 exit(1);
125 ObjectPtr
126 findObject(int type, const void *key, int key_size)
128 int h;
129 ObjectPtr object;
131 assert(key_size <= 10000);
133 h = hash(type, key, key_size, log2ObjectHashTableSize);
134 object = objectHashTable[h];
135 if(!object)
136 return NULL;
137 if(object->type != type || object->key_size != key_size ||
138 memcmp(object->key, key, key_size) != 0) {
139 return NULL;
141 if(object->next)
142 object->next->previous = object->previous;
143 if(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;
151 if(object_list)
152 object_list->previous = object;
153 object_list = object;
154 if(!object_list_end)
155 object_list_end = object;
156 return retainObject(object);
159 ObjectPtr
160 makeObject(int type, const void *key, int key_size, int public, int fromdisk,
161 RequestFunction request, void* request_closure)
163 ObjectPtr object;
164 int h;
166 object = findObject(type, key, key_size);
167 if(object != NULL) {
168 if(public)
169 return object;
170 else
171 privatiseObject(object, 0);
174 if(publicObjectCount + privateObjectCount >= objectHighMark) {
175 if(!objectExpiryScheduled)
176 discardObjects(0, 0);
177 if(publicObjectCount + privateObjectCount >= objectHighMark) {
178 return NULL;
182 if(publicObjectCount >= publicObjectLowMark &&
183 !objectExpiryScheduled) {
184 TimeEventHandlerPtr event;
185 event = scheduleTimeEvent(-1, discardObjectsHandler, 0, NULL);
186 if(event)
187 objectExpiryScheduled = 1;
188 else
189 do_log(L_ERROR, "Couldn't schedule object expiry.\n");
192 object = malloc(sizeof(ObjectRec));
193 if(object == NULL)
194 return NULL;
196 object->type = type;
197 object->request = request;
198 object->request_closure = request_closure;
199 object->key = malloc(key_size);
200 if(object->key == NULL) {
201 free(object);
202 return NULL;
204 memcpy(object->key, key, key_size);
205 object->key_size = key_size;
206 object->flags = (public?OBJECT_PUBLIC:0) | OBJECT_INITIAL;
207 if(public) {
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;
218 if(object_list)
219 object_list->previous = object;
220 object_list = object;
221 if(!object_list_end)
222 object_list_end = object;
223 } else {
224 object->next = NULL;
225 object->previous = NULL;
227 object->abort_data = NULL;
228 object->code = 0;
229 object->message = NULL;
230 initCondition(&object->condition);
231 object->headers = NULL;
232 object->via = NULL;
233 object->numchunks = 0;
234 object->chunks = NULL;
235 object->length = -1;
236 object->date = -1;
237 object->age = -1;
238 object->expires = -1;
239 object->last_modified = -1;
240 object->atime = -1;
241 object->etag = NULL;
242 object->cache_control = 0;
243 object->max_age = -1;
244 object->s_maxage = -1;
245 object->size = 0;
246 object->requestor = NULL;
247 object->disk_entry = NULL;
248 if(object->flags & OBJECT_PUBLIC)
249 publicObjectCount++;
250 else
251 privateObjectCount++;
252 object->refcount = 1;
254 if(public && fromdisk)
255 objectGetFromDisk(object);
256 return object;
259 void
260 objectMetadataChanged(ObjectPtr object, int revalidate)
262 if(revalidate) {
263 revalidateDiskEntry(object);
264 } else {
265 object->flags &= ~OBJECT_DISK_ENTRY_COMPLETE;
266 dirtyDiskEntry(object);
268 return;
271 ObjectPtr
272 retainObject(ObjectPtr object)
274 do_log(D_REFCOUNT, "O 0x%lx %d++\n",
275 (unsigned long)object, object->refcount);
276 object->refcount++;
277 return object;
280 void
281 releaseObject(ObjectPtr object)
283 do_log(D_REFCOUNT, "O 0x%lx %d--\n",
284 (unsigned long)object, object->refcount);
285 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);
294 void
295 releaseNotifyObject(ObjectPtr object)
297 do_log(D_REFCOUNT, "O 0x%lx %d--\n",
298 (unsigned long)object, object->refcount);
299 object->refcount--;
300 if(object->refcount > 0) {
301 notifyObject(object);
302 } else {
303 assert(!object->condition.handlers &&
304 !(object->flags & OBJECT_INPROGRESS));
305 if(!(object->flags & OBJECT_PUBLIC))
306 destroyObject(object);
310 void
311 lockChunk(ObjectPtr object, int i)
313 do_log(D_LOCK, "Lock 0x%lx[%d]: ", (unsigned long)object, i);
314 assert(i >= 0);
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);
321 void
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)
334 int n;
336 if(numchunks <= object->numchunks)
337 return 0;
339 if(object->length >= 0)
340 n = MAX(numchunks, (object->length + (CHUNK_SIZE - 1)) / CHUNK_SIZE);
341 else
342 n = MAX(numchunks,
343 MAX(object->numchunks + 2, object->numchunks * 5 / 4));
345 if(n == 0) {
346 assert(object->chunks == NULL);
347 } else if(object->numchunks == 0) {
348 object->chunks = calloc(n, sizeof(ChunkRec));
349 if(object->chunks == NULL) {
350 return -1;
352 object->numchunks = n;
353 } else {
354 ChunkPtr newchunks;
355 newchunks = realloc(object->chunks, n * sizeof(ChunkRec));
356 if(newchunks == NULL)
357 return -1;
358 memset(newchunks + object->numchunks, 0,
359 (n - object->numchunks) * sizeof(ChunkRec));
360 object->chunks = newchunks;
361 object->numchunks = n;
363 return 0;
366 ObjectPtr
367 objectPartial(ObjectPtr object, int length, struct _Atom *headers)
369 object->headers = headers;
371 if(length >= 0) {
372 if(object->size > length) {
373 abortObject(object, 502,
374 internAtom("Inconsistent Content-Length"));
375 notifyObject(object);
376 return object;
380 if(length >= 0)
381 object->length = length;
383 object->flags &= ~OBJECT_INITIAL;
384 revalidateDiskEntry(object);
385 notifyObject(object);
386 return object;
389 static int
390 objectAddChunk(ObjectPtr object, const char *data, int offset, int plen)
392 int i = offset / CHUNK_SIZE;
393 int rc;
395 assert(offset % CHUNK_SIZE == 0);
396 assert(plen <= CHUNK_SIZE);
398 if(object->numchunks <= i) {
399 rc = objectSetChunks(object, i + 1);
400 if(rc < 0)
401 return -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)
409 goto fail;
412 if(object->chunks[i].size >= plen) {
413 unlockChunk(object, i);
414 return 0;
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);
422 return 0;
424 fail:
425 unlockChunk(object, i);
426 return -1;
429 static int
430 objectAddChunkEnd(ObjectPtr object, const char *data, int offset, int plen)
432 int i = offset / CHUNK_SIZE;
433 int rc;
435 assert(offset % CHUNK_SIZE != 0 &&
436 offset % CHUNK_SIZE + plen <= CHUNK_SIZE);
438 if(object->numchunks <= i) {
439 rc = objectSetChunks(object, i + 1);
440 if(rc < 0)
441 return -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)
449 goto fail;
451 if(offset > object->size) {
452 goto fail;
455 if(object->chunks[i].size < offset % CHUNK_SIZE) {
456 goto fail;
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),
463 data, plen);
465 unlockChunk(object, i);
466 return 0;
468 fail:
469 unlockChunk(object, i);
470 return -1;
474 objectAddData(ObjectPtr object, const char *data, int offset, int len)
476 int rc;
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);
481 if(len == 0)
482 return 1;
484 if(object->length >= 0) {
485 if(offset + len > object->length) {
486 do_log(L_ERROR,
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);
497 if(rc < 0) {
498 return -1;
502 if(offset % CHUNK_SIZE != 0) {
503 int plen = CHUNK_SIZE - offset % CHUNK_SIZE;
504 if(plen >= len)
505 plen = len;
506 rc = objectAddChunkEnd(object, data, offset, plen);
507 if(rc < 0) {
508 return -1;
510 offset += plen;
511 data += plen;
512 len -= plen;
515 while(len > 0) {
516 int plen = (len >= CHUNK_SIZE) ? CHUNK_SIZE : len;
517 rc = objectAddChunk(object, data, offset, plen);
518 if(rc < 0) {
519 return -1;
521 offset += plen;
522 data += plen;
523 len -= plen;
526 return 1;
529 void
530 objectPrintf(ObjectPtr object, int offset, const char *format, ...)
532 char *buf;
533 int rc;
535 va_list args;
536 va_start(args, format);
537 buf = vsprintf_a(format, args);
538 va_end(args);
540 if(buf == NULL) {
541 abortObject(object, 500, internAtom("Couldn't allocate string"));
542 return;
545 rc = objectAddData(object, buf, offset, strlen(buf));
546 free(buf);
547 if(rc < 0)
548 abortObject(object, 500, internAtom("Couldn't add data to object"));
551 int
552 objectHoleSize(ObjectPtr object, int offset)
554 int size = 0, i;
556 if(offset < 0 || offset / CHUNK_SIZE >= object->numchunks)
557 return -1;
559 if(offset % CHUNK_SIZE != 0) {
560 if(object->chunks[offset / CHUNK_SIZE].size > offset % CHUNK_SIZE)
561 return 0;
562 else {
563 size += CHUNK_SIZE - offset % CHUNK_SIZE;
564 offset += CHUNK_SIZE - offset % CHUNK_SIZE;
565 if(offset < 0) {
566 /* Overflow */
567 return -1;
572 for(i = offset / CHUNK_SIZE; i < object->numchunks; i++) {
573 if(object->chunks[i].size == 0)
574 size += CHUNK_SIZE;
575 else
576 break;
578 if(i >= object->numchunks)
579 return -1;
580 return size;
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;
590 int i, upto;
592 if(to < 0) {
593 if(object->length >= 0)
594 to = object->length;
595 else
596 return 0;
599 if(from >= to)
600 return 2;
602 if(to > object->size) {
603 upto = to;
604 goto disk;
607 if(last > object->numchunks ||
608 object->chunks[last].size > to % CHUNK_SIZE) {
609 upto = to;
610 goto disk;
613 for(i = last - 1; i >= first; i--) {
614 if(object->chunks[i].size < CHUNK_SIZE) {
615 upto = (i + 1) * CHUNK_SIZE;
616 goto disk;
620 return 2;
622 disk:
623 if(object->flags & OBJECT_DISK_ENTRY_COMPLETE)
624 return 1;
626 if(diskEntrySize(object) >= upto)
627 return 1;
629 return 0;
632 void
633 destroyObject(ObjectPtr object)
635 int i;
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);
646 } else {
647 object->type = -1;
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--;
662 free(object);
666 void
667 privatiseObject(ObjectPtr object, int linear)
669 int i, h;
670 if(!(object->flags & OBJECT_PUBLIC)) {
671 if(linear)
672 object->flags |= OBJECT_LINEAR;
673 return;
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)
682 break;
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;
695 if(object->previous)
696 object->previous->next = object->next;
697 if(object_list == object)
698 object_list = object->next;
699 if(object->next)
700 object->next->previous = object->previous;
701 if(object_list_end == object)
702 object_list_end = object->previous;
703 object->previous = NULL;
704 object->next = NULL;
706 publicObjectCount--;
707 privateObjectCount++;
709 if(object->refcount == 0)
710 destroyObject(object);
711 else {
712 if(linear)
713 object->flags |= OBJECT_LINEAR;
717 void
718 abortObject(ObjectPtr object, int code, AtomPtr message)
720 int i;
722 assert(code != 0);
724 object->flags &= ~(OBJECT_INITIAL | OBJECT_VALIDATING);
725 object->flags |= OBJECT_ABORTED;
726 object->code = code;
727 if(object->message) releaseAtom(object->message);
728 object->message = message;
729 object->length = 0;
730 object->date = object->age;
731 object->expires = object->age;
732 object->last_modified = -1;
733 if(object->etag) free(object->etag);
734 object->etag = NULL;
735 if(object->headers) releaseAtom(object->headers);
736 object->headers = NULL;
737 object->size = 0;
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);
750 void
751 supersedeObject(ObjectPtr object)
753 object->flags |= OBJECT_SUPERSEDED;
754 destroyDiskEntry(object, 1);
755 privatiseObject(object, 0);
756 notifyObject(object);
759 void
760 notifyObject(ObjectPtr object)
762 retainObject(object);
763 signalCondition(&object->condition);
764 releaseObject(object);
768 discardObjectsHandler(TimeEventHandlerPtr event)
770 return discardObjects(0, 0);
773 void
774 writeoutObjects(int all)
776 ObjectPtr object = object_list;
777 int bytes;
778 int objects;
779 int n;
781 if(diskIsClean) return;
783 objects = 0;
784 bytes = 0;
785 while(object) {
786 do {
787 if(!all) {
788 if(objects >= maxObjectsWhenIdle ||
789 bytes >= maxWriteoutWhenIdle) {
790 if(workToDo()) return;
791 objects = 0;
792 bytes = 0;
795 n = writeoutToDisk(object, -1, all ? -1 : maxWriteoutWhenIdle);
796 bytes += n;
797 } while(!all && n == maxWriteoutWhenIdle);
798 objects++;
799 object = object->next;
801 diskIsClean = 1;
805 discardObjects(int all, int force)
807 ObjectPtr object;
808 int i;
809 static int in_discardObjects = 0;
810 TimeEventHandlerPtr event;
812 if(in_discardObjects)
813 return 0;
815 in_discardObjects = 1;
817 if(all || force || used_chunks >= CHUNKS(chunkHighMark) ||
818 publicObjectCount >= publicObjectLowMark ||
819 publicObjectCount + privateObjectCount >= objectHighMark) {
820 object = object_list_end;
821 while(object &&
822 (all || force || used_chunks >= CHUNKS(chunkLowMark))) {
823 if(force || ((object->flags & OBJECT_PUBLIC) &&
824 object->numchunks > CHUNKS(chunkLowMark) / 4)) {
825 int j;
826 for(j = 0; j < object->numchunks; j++) {
827 if(object->chunks[j].locked) {
828 break;
830 if(object->chunks[j].size < CHUNK_SIZE) {
831 continue;
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;
837 i++;
840 object = object->previous;
843 i = 0;
844 object = object_list_end;
845 while(object &&
846 (all || force ||
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)) {
865 do_log(L_WARN,
866 "Short on chunk memory -- "
867 "attempting to punch holes "
868 "in the middle of objects.\n");
870 while(object &&
871 (force || used_chunks > CHUNKS(chunkCriticalMark))) {
872 if(force || (object->flags & OBJECT_PUBLIC)) {
873 int j;
874 for(j = object->numchunks - 1; j >= 0; j--) {
875 if(object->chunks[j].locked)
876 continue;
877 if(object->chunks[j].size < CHUNK_SIZE)
878 continue;
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);
889 if(event) {
890 objectExpiryScheduled = 1;
891 } else {
892 objectExpiryScheduled = 0;
893 do_log(L_ERROR, "Couldn't schedule object expiry.\n");
895 } else {
896 objectExpiryScheduled = 0;
899 if(all) {
900 if(privateObjectCount + publicObjectCount != 0) {
901 do_log(L_WARN,
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) {
907 do_log(L_WARN,
908 "Discarded all objects, "
909 "%d chunks and %d atoms left.\n",
910 used_chunks, used_atoms);
912 diskIsClean = 1;
915 in_discardObjects = 0;
916 return 1;
919 CacheControlRec no_cache_control = {0, -1, -1, -1, -1};
922 objectIsStale(ObjectPtr object, CacheControlPtr cache_control)
924 int stale = 0x7FFFFFFF;
925 int flags;
926 int max_age, s_maxage;
927 time_t date;
929 if(object->flags & OBJECT_INITIAL)
930 return 0;
932 if(object->date >= 0)
933 date = object->date;
934 else if(object->age >= 0)
935 date = object->age;
936 else
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);
946 else
947 max_age = cache_control->max_age;
948 } else
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);
954 else
955 s_maxage = cache_control->s_maxage;
956 } else
957 s_maxage = object->s_maxage;
959 if(max_age >= 0)
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);
967 else
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 */
981 stale = MIN(stale,
982 object->age +
983 (date - object->last_modified) * maxAgeFraction);
984 else
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);
995 else
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)
1008 int flags;
1010 if(cache_control == NULL)
1011 cache_control = &no_cache_control;
1012 if(object)
1013 flags = object->cache_control | cache_control->flags;
1014 else
1015 flags = cache_control->flags;
1017 if(flags & (CACHE_NO | CACHE_NO_HIDDEN | CACHE_NO_STORE))
1018 return 1;
1020 if(cacheIsShared && (flags & CACHE_PRIVATE))
1021 return 1;
1023 if(!mindlesslyCacheVary && (flags & CACHE_VARY))
1024 return 1;
1026 if(dontCacheCookies && (flags & CACHE_COOKIE))
1027 return 1;
1029 if(object)
1030 return objectIsStale(object, cache_control);
1032 return 0;