Use DO_NTOHS when parsing DNS id.
[polipo.git] / object.c
blobc739649ad33a1f9e1a4a7c91a0d9e53a44abb13d
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;
568 for(i = offset / CHUNK_SIZE; i < object->numchunks; i++) {
569 if(object->chunks[i].size == 0)
570 size += CHUNK_SIZE;
571 else
572 break;
574 if(i >= object->numchunks)
575 return -1;
576 return size;
580 /* Returns 2 if the data is wholly in memory, 1 if it's available on disk */
582 objectHasData(ObjectPtr object, int from, int to)
584 int first = from / CHUNK_SIZE;
585 int last = to / CHUNK_SIZE;
586 int i, upto;
588 if(to < 0) {
589 if(object->length >= 0)
590 to = object->length;
591 else
592 return 0;
595 if(from >= to)
596 return 2;
598 if(to > object->size) {
599 upto = to;
600 goto disk;
603 if(last > object->numchunks ||
604 object->chunks[last].size > to % CHUNK_SIZE) {
605 upto = to;
606 goto disk;
609 for(i = last - 1; i >= first; i--) {
610 if(object->chunks[i].size < CHUNK_SIZE) {
611 upto = (i + 1) * CHUNK_SIZE;
612 goto disk;
616 return 2;
618 disk:
619 if(object->flags & OBJECT_DISK_ENTRY_COMPLETE)
620 return 1;
622 if(diskEntrySize(object) >= upto)
623 return 1;
625 return 0;
628 void
629 destroyObject(ObjectPtr object)
631 int i;
633 assert(object->refcount == 0 && !object->requestor);
634 assert(!object->condition.handlers &&
635 (object->flags & OBJECT_INPROGRESS) == 0);
637 if(object->disk_entry)
638 destroyDiskEntry(object, 0);
640 if(object->flags & OBJECT_PUBLIC) {
641 privatiseObject(object, 0);
642 } else {
643 object->type = -1;
644 if(object->message) releaseAtom(object->message);
645 if(object->key) free(object->key);
646 if(object->headers) releaseAtom(object->headers);
647 if(object->etag) free(object->etag);
648 if(object->via) releaseAtom(object->via);
649 for(i = 0; i < object->numchunks; i++) {
650 assert(!object->chunks[i].locked);
651 if(object->chunks[i].data)
652 dispose_chunk(object->chunks[i].data);
653 object->chunks[i].data = NULL;
654 object->chunks[i].size = 0;
656 if(object->chunks) free(object->chunks);
657 privateObjectCount--;
658 free(object);
662 void
663 privatiseObject(ObjectPtr object, int linear)
665 int i, h;
666 if(!(object->flags & OBJECT_PUBLIC)) {
667 if(linear)
668 object->flags |= OBJECT_LINEAR;
669 return;
672 if(object->disk_entry)
673 destroyDiskEntry(object, 0);
674 object->flags &= ~OBJECT_PUBLIC;
676 for(i = 0; i < object->numchunks; i++) {
677 if(object->chunks[i].locked)
678 break;
679 if(object->chunks[i].data) {
680 object->chunks[i].size = 0;
681 dispose_chunk(object->chunks[i].data);
682 object->chunks[i].data = NULL;
686 h = hash(object->type, object->key, object->key_size,
687 log2ObjectHashTableSize);
688 assert(objectHashTable[h] == object);
689 objectHashTable[h] = NULL;
691 if(object->previous)
692 object->previous->next = object->next;
693 if(object_list == object)
694 object_list = object->next;
695 if(object->next)
696 object->next->previous = object->previous;
697 if(object_list_end == object)
698 object_list_end = object->previous;
699 object->previous = NULL;
700 object->next = NULL;
702 publicObjectCount--;
703 privateObjectCount++;
705 if(object->refcount == 0)
706 destroyObject(object);
707 else {
708 if(linear)
709 object->flags |= OBJECT_LINEAR;
713 void
714 abortObject(ObjectPtr object, int code, AtomPtr message)
716 int i;
718 assert(code != 0);
720 object->flags &= ~(OBJECT_INITIAL | OBJECT_VALIDATING);
721 object->flags |= OBJECT_ABORTED;
722 object->code = code;
723 if(object->message) releaseAtom(object->message);
724 object->message = message;
725 object->length = 0;
726 object->date = object->age;
727 object->expires = object->age;
728 object->last_modified = -1;
729 if(object->etag) free(object->etag);
730 object->etag = NULL;
731 if(object->headers) releaseAtom(object->headers);
732 object->headers = NULL;
733 object->size = 0;
734 for(i = 0; i < object->numchunks; i++) {
735 if(object->chunks[i].data) {
736 if(!object->chunks[i].locked) {
737 dispose_chunk(object->chunks[i].data);
738 object->chunks[i].data = NULL;
739 object->chunks[i].size = 0;
743 privatiseObject(object, 0);
746 void
747 supersedeObject(ObjectPtr object)
749 object->flags |= OBJECT_SUPERSEDED;
750 destroyDiskEntry(object, 1);
751 privatiseObject(object, 0);
752 notifyObject(object);
755 void
756 notifyObject(ObjectPtr object)
758 retainObject(object);
759 signalCondition(&object->condition);
760 releaseObject(object);
764 discardObjectsHandler(TimeEventHandlerPtr event)
766 return discardObjects(0, 0);
769 void
770 writeoutObjects(int all)
772 ObjectPtr object = object_list;
773 int bytes;
774 int objects;
775 int n;
777 if(diskIsClean) return;
779 objects = 0;
780 bytes = 0;
781 while(object) {
782 do {
783 if(!all) {
784 if(objects >= maxObjectsWhenIdle ||
785 bytes >= maxWriteoutWhenIdle) {
786 if(workToDo()) return;
787 objects = 0;
788 bytes = 0;
791 n = writeoutToDisk(object, -1, all ? -1 : maxWriteoutWhenIdle);
792 bytes += n;
793 } while(!all && n == maxWriteoutWhenIdle);
794 objects++;
795 object = object->next;
797 diskIsClean = 1;
801 discardObjects(int all, int force)
803 ObjectPtr object;
804 int i;
805 static int in_discardObjects = 0;
806 TimeEventHandlerPtr event;
808 if(in_discardObjects)
809 return 0;
811 in_discardObjects = 1;
813 if(all || force || used_chunks >= CHUNKS(chunkHighMark) ||
814 publicObjectCount >= publicObjectLowMark ||
815 publicObjectCount + privateObjectCount >= objectHighMark) {
816 object = object_list_end;
817 while(object &&
818 (all || force || used_chunks >= CHUNKS(chunkLowMark))) {
819 if(force || ((object->flags & OBJECT_PUBLIC) &&
820 object->numchunks > CHUNKS(chunkLowMark) / 4)) {
821 int j;
822 for(j = 0; j < object->numchunks; j++) {
823 if(object->chunks[j].locked) {
824 break;
826 if(object->chunks[j].size < CHUNK_SIZE) {
827 continue;
829 writeoutToDisk(object, (j + 1) * CHUNK_SIZE, -1);
830 dispose_chunk(object->chunks[j].data);
831 object->chunks[j].data = NULL;
832 object->chunks[j].size = 0;
833 i++;
836 object = object->previous;
839 i = 0;
840 object = object_list_end;
841 while(object &&
842 (all || force ||
843 used_chunks - i > CHUNKS(chunkLowMark) ||
844 used_chunks > CHUNKS(chunkCriticalMark) ||
845 publicObjectCount > publicObjectLowMark)) {
846 ObjectPtr next_object = object->previous;
847 if(object->refcount == 0) {
848 i += object->numchunks;
849 writeoutToDisk(object, object->size, -1);
850 privatiseObject(object, 0);
851 } else if(all || force) {
852 writeoutToDisk(object, object->size, -1);
853 destroyDiskEntry(object, 0);
855 object = next_object;
858 object = object_list_end;
859 if(force || used_chunks > CHUNKS(chunkCriticalMark)) {
860 if(used_chunks > CHUNKS(chunkCriticalMark)) {
861 do_log(L_WARN,
862 "Short on chunk memory -- "
863 "attempting to punch holes "
864 "in the middle of objects.\n");
866 while(object &&
867 (force || used_chunks > CHUNKS(chunkCriticalMark))) {
868 if(force || (object->flags & OBJECT_PUBLIC)) {
869 int j;
870 for(j = object->numchunks - 1; j >= 0; j--) {
871 if(object->chunks[j].locked)
872 continue;
873 if(object->chunks[j].size < CHUNK_SIZE)
874 continue;
875 writeoutToDisk(object, (j + 1) * CHUNK_SIZE, -1);
876 dispose_chunk(object->chunks[j].data);
877 object->chunks[j].data = NULL;
878 object->chunks[j].size = 0;
881 object = object->previous;
884 event = scheduleTimeEvent(2, discardObjectsHandler, 0, NULL);
885 if(event) {
886 objectExpiryScheduled = 1;
887 } else {
888 objectExpiryScheduled = 0;
889 do_log(L_ERROR, "Couldn't schedule object expiry.\n");
891 } else {
892 objectExpiryScheduled = 0;
895 if(all) {
896 if(privateObjectCount + publicObjectCount != 0) {
897 do_log(L_WARN,
898 "Discarded all objects, "
899 "%d + %d objects left (%d chunks and %d atoms used).\n",
900 publicObjectCount, privateObjectCount,
901 used_chunks, used_atoms);
902 } else if(used_chunks != 0) {
903 do_log(L_WARN,
904 "Discarded all objects, "
905 "%d chunks and %d atoms left.\n",
906 used_chunks, used_atoms);
908 diskIsClean = 1;
911 in_discardObjects = 0;
912 return 1;
915 CacheControlRec no_cache_control = {0, -1, -1, -1, -1};
918 objectIsStale(ObjectPtr object, CacheControlPtr cache_control)
920 int stale = 0x7FFFFFFF;
921 int flags;
922 int max_age, s_maxage;
923 time_t date;
925 if(object->flags & OBJECT_INITIAL)
926 return 0;
928 if(object->date >= 0)
929 date = object->date;
930 else if(object->age >= 0)
931 date = object->age;
932 else
933 date = current_time.tv_sec;
935 if(cache_control == NULL)
936 cache_control = &no_cache_control;
937 flags = object->cache_control | cache_control->flags;
939 if(cache_control->max_age >= 0) {
940 if(object->max_age >= 0)
941 max_age = MIN(cache_control->max_age, object->max_age);
942 else
943 max_age = cache_control->max_age;
944 } else
945 max_age = object->max_age;
947 if(cache_control->s_maxage >= 0) {
948 if(object->s_maxage >= 0)
949 s_maxage = MIN(cache_control->s_maxage, object->s_maxage);
950 else
951 s_maxage = cache_control->s_maxage;
952 } else
953 s_maxage = object->s_maxage;
955 if(max_age >= 0)
956 stale = MIN(stale, object->age + max_age);
958 if(cacheIsShared && s_maxage >= 0)
959 stale = MIN(stale, object->age + s_maxage);
961 if(object->expires >= 0 || object->max_age >= 0)
962 stale = MIN(stale, object->age + maxExpiresAge);
963 else
964 stale = MIN(stale, object->age + maxAge);
966 /* RFC 2616 14.9.3: server-side max-age overrides expires */
968 if(object->expires >= 0 && object->max_age < 0) {
969 /* This protects against clock skew */
970 stale = MIN(stale, object->age + object->expires - date);
973 if(object->expires < 0 && object->max_age < 0) {
974 /* No server-side information -- heuristic expiration */
975 if(object->last_modified >= 0)
976 /* Again, take care of clock skew */
977 stale = MIN(stale,
978 object->age +
979 (date - object->last_modified) * maxAgeFraction);
980 else
981 stale = MIN(stale, object->age + maxNoModifiedAge);
984 if(!(flags & CACHE_MUST_REVALIDATE) &&
985 !(cacheIsShared && (flags & CACHE_PROXY_REVALIDATE))) {
986 /* Client side can relax transparency */
987 if(cache_control->min_fresh >= 0) {
988 if(cache_control->max_stale >= 0)
989 stale = MIN(stale - cache_control->min_fresh,
990 stale + cache_control->max_stale);
991 else
992 stale = stale - cache_control->min_fresh;
993 } else if(cache_control->max_stale >= 0) {
994 stale = stale + cache_control->max_stale;
998 return current_time.tv_sec > stale;
1002 objectMustRevalidate(ObjectPtr object, CacheControlPtr cache_control)
1004 int flags;
1006 if(cache_control == NULL)
1007 cache_control = &no_cache_control;
1008 if(object)
1009 flags = object->cache_control | cache_control->flags;
1010 else
1011 flags = cache_control->flags;
1013 if(flags & (CACHE_NO | CACHE_NO_HIDDEN | CACHE_NO_STORE))
1014 return 1;
1016 if(cacheIsShared && (flags & CACHE_PRIVATE))
1017 return 1;
1019 if(!mindlesslyCacheVary && (flags & CACHE_VARY))
1020 return 1;
1022 if(dontCacheCookies && (flags & CACHE_COOKIE))
1023 return 1;
1025 if(object)
1026 return objectIsStale(object, cache_control);
1028 return 0;