Tweak CHANGES.
[polipo.git] / object.c
blobd3088df73e9f9cba09830cb3147f307287470907
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 if(key_size >= 50000)
132 return NULL;
134 h = hash(type, key, key_size, log2ObjectHashTableSize);
135 object = objectHashTable[h];
136 if(!object)
137 return NULL;
138 if(object->type != type || object->key_size != key_size ||
139 memcmp(object->key, key, key_size) != 0) {
140 return NULL;
142 if(object->next)
143 object->next->previous = object->previous;
144 if(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;
152 if(object_list)
153 object_list->previous = object;
154 object_list = object;
155 if(!object_list_end)
156 object_list_end = object;
157 return retainObject(object);
160 ObjectPtr
161 makeObject(int type, const void *key, int key_size, int public, int fromdisk,
162 RequestFunction request, void* request_closure)
164 ObjectPtr object;
165 int h;
167 object = findObject(type, key, key_size);
168 if(object != NULL) {
169 if(public)
170 return object;
171 else
172 privatiseObject(object, 0);
175 if(publicObjectCount + privateObjectCount >= objectHighMark) {
176 if(!objectExpiryScheduled)
177 discardObjects(0, 0);
178 if(publicObjectCount + privateObjectCount >= objectHighMark) {
179 return NULL;
183 if(publicObjectCount >= publicObjectLowMark &&
184 !objectExpiryScheduled) {
185 TimeEventHandlerPtr event;
186 event = scheduleTimeEvent(-1, discardObjectsHandler, 0, NULL);
187 if(event)
188 objectExpiryScheduled = 1;
189 else
190 do_log(L_ERROR, "Couldn't schedule object expiry.\n");
193 object = malloc(sizeof(ObjectRec));
194 if(object == NULL)
195 return NULL;
197 object->type = type;
198 object->request = request;
199 object->request_closure = request_closure;
200 object->key = malloc(key_size);
201 if(object->key == NULL) {
202 free(object);
203 return NULL;
205 memcpy(object->key, key, key_size);
206 object->key_size = key_size;
207 object->flags = (public?OBJECT_PUBLIC:0) | OBJECT_INITIAL;
208 if(public) {
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;
219 if(object_list)
220 object_list->previous = object;
221 object_list = object;
222 if(!object_list_end)
223 object_list_end = object;
224 } else {
225 object->next = NULL;
226 object->previous = NULL;
228 object->abort_data = NULL;
229 object->code = 0;
230 object->message = NULL;
231 initCondition(&object->condition);
232 object->headers = NULL;
233 object->via = NULL;
234 object->numchunks = 0;
235 object->chunks = NULL;
236 object->length = -1;
237 object->date = -1;
238 object->age = -1;
239 object->expires = -1;
240 object->last_modified = -1;
241 object->atime = -1;
242 object->etag = NULL;
243 object->cache_control = 0;
244 object->max_age = -1;
245 object->s_maxage = -1;
246 object->size = 0;
247 object->requestor = NULL;
248 object->disk_entry = NULL;
249 if(object->flags & OBJECT_PUBLIC)
250 publicObjectCount++;
251 else
252 privateObjectCount++;
253 object->refcount = 1;
255 if(public && fromdisk)
256 objectGetFromDisk(object);
257 return object;
260 void
261 objectMetadataChanged(ObjectPtr object, int revalidate)
263 if(revalidate) {
264 revalidateDiskEntry(object);
265 } else {
266 object->flags &= ~OBJECT_DISK_ENTRY_COMPLETE;
267 dirtyDiskEntry(object);
269 return;
272 ObjectPtr
273 retainObject(ObjectPtr object)
275 do_log(D_REFCOUNT, "O 0x%lx %d++\n",
276 (unsigned long)object, object->refcount);
277 object->refcount++;
278 return object;
281 void
282 releaseObject(ObjectPtr object)
284 do_log(D_REFCOUNT, "O 0x%lx %d--\n",
285 (unsigned long)object, object->refcount);
286 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);
295 void
296 releaseNotifyObject(ObjectPtr object)
298 do_log(D_REFCOUNT, "O 0x%lx %d--\n",
299 (unsigned long)object, object->refcount);
300 object->refcount--;
301 if(object->refcount > 0) {
302 notifyObject(object);
303 } else {
304 assert(!object->condition.handlers &&
305 !(object->flags & OBJECT_INPROGRESS));
306 if(!(object->flags & OBJECT_PUBLIC))
307 destroyObject(object);
311 void
312 lockChunk(ObjectPtr object, int i)
314 do_log(D_LOCK, "Lock 0x%lx[%d]: ", (unsigned long)object, i);
315 assert(i >= 0);
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);
322 void
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)
335 int n;
337 if(numchunks <= object->numchunks)
338 return 0;
340 if(object->length >= 0)
341 n = MAX(numchunks, (object->length + (CHUNK_SIZE - 1)) / CHUNK_SIZE);
342 else
343 n = MAX(numchunks,
344 MAX(object->numchunks + 2, object->numchunks * 5 / 4));
346 if(n == 0) {
347 assert(object->chunks == NULL);
348 } else if(object->numchunks == 0) {
349 object->chunks = calloc(n, sizeof(ChunkRec));
350 if(object->chunks == NULL) {
351 return -1;
353 object->numchunks = n;
354 } else {
355 ChunkPtr newchunks;
356 newchunks = realloc(object->chunks, n * sizeof(ChunkRec));
357 if(newchunks == NULL)
358 return -1;
359 memset(newchunks + object->numchunks, 0,
360 (n - object->numchunks) * sizeof(ChunkRec));
361 object->chunks = newchunks;
362 object->numchunks = n;
364 return 0;
367 ObjectPtr
368 objectPartial(ObjectPtr object, int length, struct _Atom *headers)
370 object->headers = headers;
372 if(length >= 0) {
373 if(object->size > length) {
374 abortObject(object, 502,
375 internAtom("Inconsistent Content-Length"));
376 notifyObject(object);
377 return object;
381 if(length >= 0)
382 object->length = length;
384 object->flags &= ~OBJECT_INITIAL;
385 revalidateDiskEntry(object);
386 notifyObject(object);
387 return object;
390 static int
391 objectAddChunk(ObjectPtr object, const char *data, int offset, int plen)
393 int i = offset / CHUNK_SIZE;
394 int rc;
396 assert(offset % CHUNK_SIZE == 0);
397 assert(plen <= CHUNK_SIZE);
399 if(object->numchunks <= i) {
400 rc = objectSetChunks(object, i + 1);
401 if(rc < 0)
402 return -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)
410 goto fail;
413 if(object->chunks[i].size >= plen) {
414 unlockChunk(object, i);
415 return 0;
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);
423 return 0;
425 fail:
426 unlockChunk(object, i);
427 return -1;
430 static int
431 objectAddChunkEnd(ObjectPtr object, const char *data, int offset, int plen)
433 int i = offset / CHUNK_SIZE;
434 int rc;
436 assert(offset % CHUNK_SIZE != 0 &&
437 offset % CHUNK_SIZE + plen <= CHUNK_SIZE);
439 if(object->numchunks <= i) {
440 rc = objectSetChunks(object, i + 1);
441 if(rc < 0)
442 return -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)
450 goto fail;
452 if(offset > object->size) {
453 goto fail;
456 if(object->chunks[i].size < offset % CHUNK_SIZE) {
457 goto fail;
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),
464 data, plen);
466 unlockChunk(object, i);
467 return 0;
469 fail:
470 unlockChunk(object, i);
471 return -1;
475 objectAddData(ObjectPtr object, const char *data, int offset, int len)
477 int rc;
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);
482 if(len == 0)
483 return 1;
485 if(object->length >= 0) {
486 if(offset + len > object->length) {
487 do_log(L_ERROR,
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);
498 if(rc < 0) {
499 return -1;
503 if(offset % CHUNK_SIZE != 0) {
504 int plen = CHUNK_SIZE - offset % CHUNK_SIZE;
505 if(plen >= len)
506 plen = len;
507 rc = objectAddChunkEnd(object, data, offset, plen);
508 if(rc < 0) {
509 return -1;
511 offset += plen;
512 data += plen;
513 len -= plen;
516 while(len > 0) {
517 int plen = (len >= CHUNK_SIZE) ? CHUNK_SIZE : len;
518 rc = objectAddChunk(object, data, offset, plen);
519 if(rc < 0) {
520 return -1;
522 offset += plen;
523 data += plen;
524 len -= plen;
527 return 1;
530 void
531 objectPrintf(ObjectPtr object, int offset, const char *format, ...)
533 char *buf;
534 int rc;
536 va_list args;
537 va_start(args, format);
538 buf = vsprintf_a(format, args);
539 va_end(args);
541 if(buf == NULL) {
542 abortObject(object, 500, internAtom("Couldn't allocate string"));
543 return;
546 rc = objectAddData(object, buf, offset, strlen(buf));
547 free(buf);
548 if(rc < 0)
549 abortObject(object, 500, internAtom("Couldn't add data to object"));
552 int
553 objectHoleSize(ObjectPtr object, int offset)
555 int size = 0, i;
557 if(offset < 0 || offset / CHUNK_SIZE >= object->numchunks)
558 return -1;
560 if(offset % CHUNK_SIZE != 0) {
561 if(object->chunks[offset / CHUNK_SIZE].size > offset % CHUNK_SIZE)
562 return 0;
563 else {
564 size += CHUNK_SIZE - offset % CHUNK_SIZE;
565 offset += CHUNK_SIZE - offset % CHUNK_SIZE;
566 if(offset < 0) {
567 /* Overflow */
568 return -1;
573 for(i = offset / CHUNK_SIZE; i < object->numchunks; i++) {
574 if(object->chunks[i].size == 0)
575 size += CHUNK_SIZE;
576 else
577 break;
579 if(i >= object->numchunks)
580 return -1;
581 return size;
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;
591 int i, upto;
593 if(to < 0) {
594 if(object->length >= 0)
595 to = object->length;
596 else
597 return 0;
600 if(from >= to)
601 return 2;
603 if(to > object->size) {
604 upto = to;
605 goto disk;
608 if(last > object->numchunks ||
609 object->chunks[last].size > to % CHUNK_SIZE) {
610 upto = to;
611 goto disk;
614 for(i = last - 1; i >= first; i--) {
615 if(object->chunks[i].size < CHUNK_SIZE) {
616 upto = (i + 1) * CHUNK_SIZE;
617 goto disk;
621 return 2;
623 disk:
624 if(object->flags & OBJECT_DISK_ENTRY_COMPLETE)
625 return 1;
627 if(diskEntrySize(object) >= upto)
628 return 1;
630 return 0;
633 void
634 destroyObject(ObjectPtr object)
636 int i;
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);
647 } else {
648 object->type = -1;
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--;
663 free(object);
667 void
668 privatiseObject(ObjectPtr object, int linear)
670 int i, h;
671 if(!(object->flags & OBJECT_PUBLIC)) {
672 if(linear)
673 object->flags |= OBJECT_LINEAR;
674 return;
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)
683 break;
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;
696 if(object->previous)
697 object->previous->next = object->next;
698 if(object_list == object)
699 object_list = object->next;
700 if(object->next)
701 object->next->previous = object->previous;
702 if(object_list_end == object)
703 object_list_end = object->previous;
704 object->previous = NULL;
705 object->next = NULL;
707 publicObjectCount--;
708 privateObjectCount++;
710 if(object->refcount == 0)
711 destroyObject(object);
712 else {
713 if(linear)
714 object->flags |= OBJECT_LINEAR;
718 void
719 abortObject(ObjectPtr object, int code, AtomPtr message)
721 int i;
723 assert(code != 0);
725 object->flags &= ~(OBJECT_INITIAL | OBJECT_VALIDATING);
726 object->flags |= OBJECT_ABORTED;
727 object->code = code;
728 if(object->message) releaseAtom(object->message);
729 object->message = message;
730 object->length = 0;
731 object->date = object->age;
732 object->expires = object->age;
733 object->last_modified = -1;
734 if(object->etag) free(object->etag);
735 object->etag = NULL;
736 if(object->headers) releaseAtom(object->headers);
737 object->headers = NULL;
738 object->size = 0;
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);
751 void
752 supersedeObject(ObjectPtr object)
754 object->flags |= OBJECT_SUPERSEDED;
755 destroyDiskEntry(object, 1);
756 privatiseObject(object, 0);
757 notifyObject(object);
760 void
761 notifyObject(ObjectPtr object)
763 retainObject(object);
764 signalCondition(&object->condition);
765 releaseObject(object);
769 discardObjectsHandler(TimeEventHandlerPtr event)
771 return discardObjects(0, 0);
774 void
775 writeoutObjects(int all)
777 ObjectPtr object = object_list;
778 int bytes;
779 int objects;
780 int n;
782 if(diskIsClean) return;
784 objects = 0;
785 bytes = 0;
786 while(object) {
787 do {
788 if(!all) {
789 if(objects >= maxObjectsWhenIdle ||
790 bytes >= maxWriteoutWhenIdle) {
791 if(workToDo()) return;
792 objects = 0;
793 bytes = 0;
796 n = writeoutToDisk(object, -1, all ? -1 : maxWriteoutWhenIdle);
797 bytes += n;
798 } while(!all && n == maxWriteoutWhenIdle);
799 objects++;
800 object = object->next;
802 diskIsClean = 1;
806 discardObjects(int all, int force)
808 ObjectPtr object;
809 int i;
810 static int in_discardObjects = 0;
811 TimeEventHandlerPtr event;
813 if(in_discardObjects)
814 return 0;
816 in_discardObjects = 1;
818 if(all || force || used_chunks >= CHUNKS(chunkHighMark) ||
819 publicObjectCount >= publicObjectLowMark ||
820 publicObjectCount + privateObjectCount >= objectHighMark) {
821 object = object_list_end;
822 while(object &&
823 (all || force || used_chunks >= CHUNKS(chunkLowMark))) {
824 if(force || ((object->flags & OBJECT_PUBLIC) &&
825 object->numchunks > CHUNKS(chunkLowMark) / 4)) {
826 int j;
827 for(j = 0; j < object->numchunks; j++) {
828 if(object->chunks[j].locked) {
829 break;
831 if(object->chunks[j].size < CHUNK_SIZE) {
832 continue;
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;
838 i++;
841 object = object->previous;
844 i = 0;
845 object = object_list_end;
846 while(object &&
847 (all || force ||
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)) {
866 do_log(L_WARN,
867 "Short on chunk memory -- "
868 "attempting to punch holes "
869 "in the middle of objects.\n");
871 while(object &&
872 (force || used_chunks > CHUNKS(chunkCriticalMark))) {
873 if(force || (object->flags & OBJECT_PUBLIC)) {
874 int j;
875 for(j = object->numchunks - 1; j >= 0; j--) {
876 if(object->chunks[j].locked)
877 continue;
878 if(object->chunks[j].size < CHUNK_SIZE)
879 continue;
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);
890 if(event) {
891 objectExpiryScheduled = 1;
892 } else {
893 objectExpiryScheduled = 0;
894 do_log(L_ERROR, "Couldn't schedule object expiry.\n");
896 } else {
897 objectExpiryScheduled = 0;
900 if(all) {
901 if(privateObjectCount + publicObjectCount != 0) {
902 do_log(L_WARN,
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) {
908 do_log(L_WARN,
909 "Discarded all objects, "
910 "%d chunks and %d atoms left.\n",
911 used_chunks, used_atoms);
913 diskIsClean = 1;
916 in_discardObjects = 0;
917 return 1;
920 CacheControlRec no_cache_control = {0, -1, -1, -1, -1};
923 objectIsStale(ObjectPtr object, CacheControlPtr cache_control)
925 int stale = 0x7FFFFFFF;
926 int flags;
927 int max_age, s_maxage;
928 time_t date;
930 if(object->flags & OBJECT_INITIAL)
931 return 0;
933 if(object->date >= 0)
934 date = object->date;
935 else if(object->age >= 0)
936 date = object->age;
937 else
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);
947 else
948 max_age = cache_control->max_age;
949 } else
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);
955 else
956 s_maxage = cache_control->s_maxage;
957 } else
958 s_maxage = object->s_maxage;
960 if(max_age >= 0)
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);
968 else
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 */
982 stale = MIN(stale,
983 object->age +
984 (date - object->last_modified) * maxAgeFraction);
985 else
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);
996 else
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)
1009 int flags;
1011 if(cache_control == NULL)
1012 cache_control = &no_cache_control;
1013 if(object)
1014 flags = object->cache_control | cache_control->flags;
1015 else
1016 flags = cache_control->flags;
1018 if(flags & (CACHE_NO | CACHE_NO_HIDDEN | CACHE_NO_STORE))
1019 return 1;
1021 if(cacheIsShared && (flags & CACHE_PRIVATE))
1022 return 1;
1024 if(!mindlesslyCacheVary && (flags & CACHE_VARY))
1025 return 1;
1027 if(dontCacheCookies && (flags & CACHE_COOKIE))
1028 return 1;
1030 if(object)
1031 return objectIsStale(object, cache_control);
1033 return 0;