Annotation of libwww/Library/src/HTCache.c, revision 2.42
2.1 frystyk 1: /* HTCache.c
2: ** CACHE WRITER
3: **
4: ** (c) COPYRIGHT MIT 1995.
5: ** Please first read the full copyright statement in the file COPYRIGH.
2.42 ! frystyk 6: ** @(#) $Id: HTCache.c,v 2.41 1997/02/16 17:49:08 frystyk Exp $
2.1 frystyk 7: **
8: ** This modules manages the cache
9: **
10: ** History:
11: ** HFN: spawned from HTFwrite
12: ** HWL: converted the caching scheme to be hierachical by taking
13: ** AL code from Deamon
14: **
15: */
16:
17: /* Library include files */
2.15 frystyk 18: #include "sysdep.h"
2.19 frystyk 19: #include "WWWUtil.h"
20: #include "WWWCore.h"
2.22 frystyk 21: #include "WWWTrans.h"
22: #include "WWWApp.h"
2.1 frystyk 23: #include "HTCache.h" /* Implemented here */
24:
2.7 frystyk 25: /* This is the default cache directory: */
2.36 frystyk 26: #define HT_CACHE_ROOT "/tmp/w3c-cache"
2.22 frystyk 27: #define HT_CACHE_INDEX ".index"
28: #define HT_CACHE_META ".meta"
2.34 frystyk 29: #define HT_CACHE_ETAG "@w3c@"
2.22 frystyk 30:
2.31 frystyk 31: /* Default heuristics cache expirations - thanks to Jeff Mogul for good comments! */
32: #define NO_LM_EXPIRATION 24*3600 /* 24 hours */
33: #define MAX_LM_EXPIRATION 48*3600 /* Max expiration from LM */
34:
35: /*
36: ** If using LM to find the expiration then take 10% and no more than
37: ** MAX_LM_EXPIRATION.
38: */
39: #ifndef LM_EXPIRATION
40: #define LM_EXPIRATION(t) (HTMIN((MAX_LM_EXPIRATION), (t) / 10))
41: #endif
42:
43: #define WARN_HEURISTICS 24*3600 /* When to issue a warning */
44:
2.22 frystyk 45: #define HASH_SIZE 67
2.41 frystyk 46: #define DUMP_FREQUENCY 20 /* Dump index after 20 loads */
2.22 frystyk 47:
48: #define MEGA 0x100000L
49: #define CACHE_SIZE (20*MEGA) /* Default cache size is 20M */
2.23 frystyk 50: #define MIN_CACHE_SIZE (5*MEGA) /* Min cache size */
2.22 frystyk 51: #define SIZE_BUFFER (1*MEGA) /* Buffer for metainfo and directories */
52:
53: /* Final states have negative value */
54: typedef enum _CacheState {
55: CL_ERROR = -3,
56: CL_NO_DATA = -2,
57: CL_GOT_DATA = -1,
58: CL_BEGIN = 0,
59: CL_NEED_BODY,
60: CL_NEED_OPEN_FILE,
61: CL_NEED_CONTENT
62: } CacheState;
63:
64: /* This is the context structure for the this module */
65: typedef struct _cache_info {
66: CacheState state; /* Current state of the connection */
67: char * local; /* Local representation of file name */
68: struct stat stat_info; /* Contains actual file chosen */
2.29 frystyk 69: HTNet * net;
2.22 frystyk 70: } cache_info;
71:
72: struct _HTCache {
2.24 frystyk 73: /* Location */
2.22 frystyk 74: int hash;
75: char * url;
76: char * cachename;
2.24 frystyk 77:
78: /* GC parameters */
2.35 frystyk 79: char * etag;
2.27 frystyk 80: BOOL range; /* Is this the full part or a subpart? */
2.35 frystyk 81: BOOL must_revalidate;
2.26 frystyk 82: int hits; /* Hit counts */
2.35 frystyk 83: long size; /* Size of cached entity body */
2.34 frystyk 84: time_t lm; /* Last modified */
2.35 frystyk 85: time_t expires;
2.22 frystyk 86: time_t freshness_lifetime;
87: time_t response_time;
88: time_t corrected_initial_age;
89: HTRequest * lock;
90: };
2.1 frystyk 91:
92: struct _HTStream {
2.15 frystyk 93: const HTStreamClass * isa;
2.1 frystyk 94: FILE * fp;
2.26 frystyk 95: long bytes_written; /* Number of bytes written */
2.7 frystyk 96: HTCache * cache;
2.1 frystyk 97: HTRequest * request;
2.26 frystyk 98: HTResponse * response;
2.22 frystyk 99: HTChunk * buffer; /* For index reading */
100: HTEOLState EOLstate;
2.27 frystyk 101: BOOL append; /* Creating or appending? */
2.22 frystyk 102: };
103:
104: struct _HTInputStream {
105: const HTInputStreamClass * isa;
2.1 frystyk 106: };
107:
2.22 frystyk 108: /* Cache parameters */
109: PRIVATE BOOL HTCacheEnable = NO; /* Disabled by default */
2.1 frystyk 110: PRIVATE char * HTCacheRoot = NULL; /* Destination for cache */
2.22 frystyk 111: PRIVATE HTExpiresMode HTExpMode = HT_EXPIRES_IGNORE;
112: PRIVATE HTDisconnectedMode DisconnectedMode = HT_DISCONNECT_NONE;
2.1 frystyk 113:
2.31 frystyk 114: /* Heuristic expiration parameters */
115: PRIVATE int DefaultExpiration = NO_LM_EXPIRATION;
116:
2.22 frystyk 117: /* List of cache entries */
118: PRIVATE HTList ** CacheTable = NULL;
119:
120: /* Cache size variables */
121: PRIVATE long HTCacheSize = CACHE_SIZE;
122: PRIVATE long HTTotalSize = 0L;
123:
124: PRIVATE int new_entries = 0; /* Number of new entries */
2.2 frystyk 125:
2.1 frystyk 126: /* ------------------------------------------------------------------------- */
2.22 frystyk 127: /* CACHE GARBAGE COLLECTOR */
2.1 frystyk 128: /* ------------------------------------------------------------------------- */
129:
2.22 frystyk 130: PRIVATE BOOL HTCacheGarbage (void)
2.1 frystyk 131: {
2.22 frystyk 132: long old_size = HTTotalSize;
133: if (CACHE_TRACE) HTTrace("Cache....... Garbage collecting\n");
134: if (CacheTable) {
135: time_t cur_time = time(NULL);
136: HTList * cur;
137: int cnt;
2.23 frystyk 138: int hits;
2.22 frystyk 139:
140: /*
141: ** Tell the use that we're gc'ing.
142: */
2.1 frystyk 143: {
2.22 frystyk 144: HTAlertCallback * cbf = HTAlert_find(HT_PROG_GC);
145: if (cbf) (*cbf)(NULL, HT_PROG_GC, HT_MSG_NULL,NULL, NULL, NULL);
146: }
147:
148: /*
149: ** Walk through and delete all the expired entries. If this is not
150: ** sufficient then take the fresh ones which have the lowest cache
151: ** hit count. This algorithm could be made a lot fancier by including
152: ** the size and also the pain it took to get the document in the first
153: ** case. It could also include max_stale.
154: */
2.23 frystyk 155: if (CACHE_TRACE) HTTrace("Cache....... Collecting Stale entries\n");
2.22 frystyk 156: for (cnt=0; cnt<HASH_SIZE; cnt++) {
157: if ((cur = CacheTable[cnt])) {
2.35 frystyk 158: HTList * old_cur = cur;
2.22 frystyk 159: HTCache * pres;
160: while ((pres = (HTCache *) HTList_nextObject(cur)) != NULL) {
161: time_t resident_time = cur_time - pres->response_time;
162: time_t current_age = pres->corrected_initial_age +
163: resident_time;
164: if (pres->freshness_lifetime < current_age) {
165: HTCache_remove(pres);
166: cur = old_cur;
167: } else {
168: old_cur = cur;
169: }
2.1 frystyk 170: }
171: }
172: }
2.23 frystyk 173:
174: /*
175: ** We must at least free the min buffer size so that we don't
176: ** dead lock ourselves. We start from the bottom up by taking
177: ** all the documents with 0 hits, 1 hits, 2 hits, etc.
178: */
179: hits = 0;
180: while (1) {
181: BOOL removed = NO;
182: if (CACHE_TRACE)
183: HTTrace("Cache....... Collecting entries with %d hits\n",hits);
184: if (HTTotalSize + SIZE_BUFFER > HTCacheSize) {
185: for (cnt=0; cnt<HASH_SIZE; cnt++) {
186: if ((cur = CacheTable[cnt])) {
2.35 frystyk 187: HTList * old_cur = cur;
2.23 frystyk 188: HTCache * pres;
189: while ((pres = (HTCache *) HTList_nextObject(cur))) {
190: if (pres->hits <= hits) {
191: HTCache_remove(pres);
192: cur = old_cur;
193: removed = YES;
194: } else {
195: old_cur = cur;
196: }
197: }
198: }
199: }
200: } else
201: break;
202: if (!removed) break;
203: hits++;
204: }
2.22 frystyk 205: if (CACHE_TRACE)
206: HTTrace("Cache....... Size reduced from %ld to %ld\n",
207: old_size, HTTotalSize);
2.23 frystyk 208: /*
209: ** Dump the new content to the index file
210: */
211: HTCacheIndex_write(HTCacheRoot);
212: new_entries = 0;
2.22 frystyk 213: return YES;
2.1 frystyk 214: }
2.22 frystyk 215: return NO;
2.1 frystyk 216: }
217:
2.22 frystyk 218: /* ------------------------------------------------------------------------- */
219: /* CACHE INDEX */
220: /* ------------------------------------------------------------------------- */
2.1 frystyk 221:
2.22 frystyk 222: PRIVATE char * cache_index_name (const char * cache_root)
2.1 frystyk 223: {
2.22 frystyk 224: if (cache_root) {
225: char * location = NULL;
226: if ((location = (char *)
227: HT_MALLOC(strlen(cache_root) + strlen(HT_CACHE_INDEX) + 1)) == NULL)
228: HT_OUTOFMEM("cache_index_name");
229: strcpy(location, cache_root);
230: strcat(location, HT_CACHE_INDEX);
231: return location;
2.1 frystyk 232: }
2.22 frystyk 233: return NULL;
2.1 frystyk 234: }
235:
236: /*
2.22 frystyk 237: ** Remove the cache index file
2.1 frystyk 238: */
2.22 frystyk 239: PUBLIC BOOL HTCacheIndex_delete (const char * cache_root)
2.1 frystyk 240: {
2.22 frystyk 241: if (cache_root) {
242: char * index = cache_index_name(cache_root);
243: REMOVE(index);
244: HT_FREE(index);
2.1 frystyk 245: return YES;
2.22 frystyk 246: }
247: return NO;
248: }
2.1 frystyk 249:
2.22 frystyk 250: /*
251: ** Walk through the list of cached objects and save them to disk.
252: ** We override any existing version but that is normally OK as we have
253: ** already read its contents.
254: */
255: PUBLIC BOOL HTCacheIndex_write (const char * cache_root)
256: {
257: if (cache_root && CacheTable) {
258: char * index = cache_index_name(cache_root);
259: FILE * fp = NULL;
260: if (CACHE_TRACE) HTTrace("Cache Index. Writing index `%s\'\n", index);
261:
262: /*
263: ** Open the file for writing. Note - we don't take a backup!
264: ** This should probably be fixed!
265: */
266: if (!index) return NO;
267: if ((fp = fopen(index, "wb")) == NULL) {
268: if (CACHE_TRACE)
269: HTTrace("Cache Index. Can't open `%s\' for writing\n", index);
270: HT_FREE(index);
271: return NO;
272: }
2.1 frystyk 273:
2.22 frystyk 274: /*
275: ** Walk through the list and write it out. The format is really
2.25 frystyk 276: ** simple as we keep it all in ASCII.
2.22 frystyk 277: */
278: {
279: HTList * cur;
280: int cnt;
281: for (cnt=0; cnt<HASH_SIZE; cnt++) {
282: if ((cur = CacheTable[cnt])) {
283: HTCache * pres;
2.24 frystyk 284: while ((pres = (HTCache *) HTList_nextObject(cur))) {
2.34 frystyk 285: if (fprintf(fp, "%s %s %s %ld %ld %ld %c %d %d %ld %ld %ld %c\r\n",
2.24 frystyk 286: pres->url,
287: pres->cachename,
2.34 frystyk 288: pres->etag ? pres->etag : HT_CACHE_ETAG,
289: pres->lm,
2.24 frystyk 290: pres->expires,
291: pres->size,
2.27 frystyk 292: pres->range+0x30,
2.24 frystyk 293: pres->hash,
294: pres->hits,
295: pres->freshness_lifetime,
296: pres->response_time,
2.22 frystyk 297: pres->corrected_initial_age,
298: pres->must_revalidate+0x30) < 0) {
299: if (CACHE_TRACE)
300: HTTrace("Cache Index. Error writing cache index\n");
301: return NO;
302: }
303: }
304: }
305: }
306: }
2.1 frystyk 307:
2.22 frystyk 308: /* Done writing */
309: fclose(fp);
310: HT_FREE(index);
311: }
2.1 frystyk 312: return NO;
313: }
314:
315: /*
2.22 frystyk 316: ** Load one line of index file
317: ** Returns YES if line OK, else NO
2.2 frystyk 318: */
2.22 frystyk 319: PRIVATE BOOL HTCacheIndex_parseLine (char * line)
2.2 frystyk 320: {
2.22 frystyk 321: HTCache * cache = NULL;
322: if (line) {
323: char validate;
2.27 frystyk 324: char range;
2.22 frystyk 325: if ((cache = (HTCache *) HT_CALLOC(1, sizeof(HTCache))) == NULL)
326: HT_OUTOFMEM("HTCacheIndex_parseLine");
327:
328: /*
329: ** Read the line and create the cache object
330: */
331: {
332: char * url = HTNextField(&line);
333: char * cachename = HTNextField(&line);
2.34 frystyk 334: char * etag = HTNextField(&line);
2.22 frystyk 335: StrAllocCopy(cache->url, url);
336: StrAllocCopy(cache->cachename, cachename);
2.34 frystyk 337: if (strcmp(etag, HT_CACHE_ETAG)) StrAllocCopy(cache->etag, etag);
2.22 frystyk 338: }
2.35 frystyk 339: #if SIZEOF_LONG==8
340: /*
341: ** On some 64 bit machines (alpha) time_t is of type int and not long.
342: ** This means that we have to adjust sscanf accordingly so that we
343: ** know what we are looking for. Otherwise er may get unalignment
344: ** problems.
345: */
346: if (sscanf(line, "%d %d %d %c %d %d %d %d %d %c",
347: #else
2.34 frystyk 348: if (sscanf(line, "%ld %ld %ld %c %d %d %ld %ld %ld %c",
2.35 frystyk 349: #endif
2.34 frystyk 350: &cache->lm,
2.24 frystyk 351: &cache->expires,
352: &cache->size,
2.28 frystyk 353: &range,
2.24 frystyk 354: &cache->hash,
355: &cache->hits,
2.22 frystyk 356: &cache->freshness_lifetime,
357: &cache->response_time,
358: &cache->corrected_initial_age,
359: &validate) < 0) {
360: if (CACHE_TRACE) HTTrace("Cache Index. Error reading cache index\n");
361: return NO;
362: }
2.27 frystyk 363: cache->range = range-0x30;
2.22 frystyk 364: cache->must_revalidate = validate-0x30;
365:
366: /*
2.26 frystyk 367: ** Create the new anchor and fill in the expire information we have read
368: ** in the index.
2.25 frystyk 369: */
370: if (cache) {
371: HTAnchor * anchor = HTAnchor_findAddress(cache->url);
372: HTParentAnchor * parent = HTAnchor_parent(anchor);
2.34 frystyk 373: HTAnchor_setExpires(parent, cache->expires);
374: HTAnchor_setLastModified(parent, cache->lm);
375: if (cache->etag) HTAnchor_setEtag(parent, cache->etag);
2.25 frystyk 376: }
377:
378: /*
2.22 frystyk 379: ** Create the cache table if not already existent and add the new
380: ** entry. Also check that the hash is still within bounds
381: */
382: if (!CacheTable) {
383: if ((CacheTable = (HTList **) HT_CALLOC(HASH_SIZE,
384: sizeof(HTList *))) == NULL)
385: HT_OUTOFMEM("HTCache_parseLine");
386: }
387: if (cache->hash >= 0 && cache->hash < HASH_SIZE) {
388: int hash = cache->hash;
389: if (!CacheTable[hash]) CacheTable[hash] = HTList_new();
390: HTList_addObject(CacheTable[hash], (void *) cache);
2.2 frystyk 391: }
2.22 frystyk 392:
393: /* Update the total cache size */
394: HTTotalSize += cache->size;
395:
396: return YES;
2.2 frystyk 397: }
2.22 frystyk 398: return NO;
2.2 frystyk 399: }
400:
401: /*
2.22 frystyk 402: ** Folding is either of CF LWS, LF LWS, CRLF LWS
2.2 frystyk 403: */
2.22 frystyk 404: PRIVATE int HTCacheIndex_put_block (HTStream * me, const char * b, int l)
2.2 frystyk 405: {
2.22 frystyk 406: while (l > 0) {
407: if (me->EOLstate == EOL_FCR) {
408: if (*b == LF) /* CRLF */
409: me->EOLstate = EOL_FLF;
410: else if (WHITE(*b)) /* Folding: CR SP */
411: me->EOLstate = EOL_DOT;
412: else { /* New line */
413: HTCacheIndex_parseLine(HTChunk_data(me->buffer));
414: me->EOLstate = EOL_BEGIN;
415: HTChunk_clear(me->buffer);
416: continue;
417: }
418: } else if (me->EOLstate == EOL_FLF) {
419: if (WHITE(*b)) /* Folding: LF SP or CR LF SP */
420: me->EOLstate = EOL_DOT;
421: else { /* New line */
422: HTCacheIndex_parseLine(HTChunk_data(me->buffer));
423: me->EOLstate = EOL_BEGIN;
424: HTChunk_clear(me->buffer);
425: continue;
426: }
427: } else if (me->EOLstate == EOL_DOT) {
428: if (WHITE(*b)) {
429: me->EOLstate = EOL_BEGIN;
430: HTChunk_putc(me->buffer, ' ');
431: } else {
432: HTCacheIndex_parseLine(HTChunk_data(me->buffer));
433: me->EOLstate = EOL_BEGIN;
434: HTChunk_clear(me->buffer);
435: continue;
436: }
437: } else if (*b == CR) {
438: me->EOLstate = EOL_FCR;
439: } else if (*b == LF) {
440: me->EOLstate = EOL_FLF; /* Line found */
441: } else
442: HTChunk_putc(me->buffer, *b);
443: l--; b++;
2.2 frystyk 444: }
2.22 frystyk 445: return HT_OK;
2.2 frystyk 446: }
447:
2.22 frystyk 448: PRIVATE int HTCacheIndex_put_character (HTStream * me, char c)
449: {
450: return HTCacheIndex_put_block(me, &c, 1);
451: }
2.2 frystyk 452:
2.22 frystyk 453: PRIVATE int HTCacheIndex_put_string (HTStream * me, const char * s)
2.1 frystyk 454: {
2.22 frystyk 455: return HTCacheIndex_put_block(me, s, (int) strlen(s));
456: }
2.1 frystyk 457:
2.22 frystyk 458: PRIVATE int HTCacheIndex_flush (HTStream * me)
459: {
460: if (me) {
461: char * flush = HTChunk_data(me->buffer);
462: if (flush) HTCacheIndex_parseLine(flush);
463: HTChunk_clear(me->buffer);
464: }
465: return HT_OK;
466: }
2.1 frystyk 467:
2.22 frystyk 468: PRIVATE int HTCacheIndex_free (HTStream * me)
469: {
470: if (me) {
471: int status = HTCacheIndex_flush(me);
472: if (APP_TRACE) HTTrace("Cache Index. FREEING....\n");
473: HTChunk_delete(me->buffer);
474: HT_FREE(me);
475: return status;
476: }
477: return HT_ERROR;
478: }
2.1 frystyk 479:
2.22 frystyk 480: PRIVATE int HTCacheIndex_abort (HTStream * me, HTList * e)
481: {
482: if (me) {
483: int status = HT_ERROR;
484: if (APP_TRACE) HTTrace("Cache Index. ABORTING...\n");
485: HTChunk_delete(me->buffer);
486: HT_FREE(me);
487: return status;
2.1 frystyk 488: }
2.22 frystyk 489: return HT_ERROR;
490: }
2.1 frystyk 491:
2.22 frystyk 492: /* Structured Object Class
493: ** -----------------------
494: */
495: PRIVATE const HTStreamClass HTCacheIndexClass =
496: {
497: "CacheIndexParser",
498: HTCacheIndex_flush,
499: HTCacheIndex_free,
500: HTCacheIndex_abort,
501: HTCacheIndex_put_character,
502: HTCacheIndex_put_string,
503: HTCacheIndex_put_block
504: };
2.1 frystyk 505:
2.22 frystyk 506: PRIVATE HTStream * HTCacheIndexReader (HTRequest * request)
507: {
508: HTStream * me;
509: if ((me = (HTStream *) HT_CALLOC(1, sizeof(HTStream))) == NULL)
510: HT_OUTOFMEM("HTCacheIndexs");
511: me->isa = &HTCacheIndexClass;
512: me->request = request;
513: me->buffer = HTChunk_new(512);
514: me->EOLstate = EOL_BEGIN;
515: return me;
516: }
517:
518: /*
519: ** Read the saved set of cached entries from disk. we only allow the index
520: ** ro be read when there is no entries in memory. That way we can ensure
521: ** consistancy.
522: */
523: PUBLIC BOOL HTCacheIndex_read (const char * cache_root)
524: {
525: BOOL status = NO;
526: if (cache_root && CacheTable == NULL) {
2.33 eric 527: BOOL wasInteractive;
2.22 frystyk 528: char * file = cache_index_name(cache_root);
529: char * index = HTParse(file, "cache:", PARSE_ALL);
2.34 frystyk 530: HTAnchor * anchor = HTAnchor_findAddress(index);
2.22 frystyk 531: HTRequest * request = HTRequest_new();
532: HTRequest_setPreemptive(request, YES);
533: HTRequest_setOutputFormat(request, WWW_SOURCE);
534: HTRequest_setOutputStream(request, HTCacheIndexReader(request));
2.23 frystyk 535: HTRequest_setAnchor(request, anchor);
2.34 frystyk 536: HTAnchor_setFormat((HTParentAnchor *) anchor, HTAtom_for("www/cache-index"));
2.33 eric 537: wasInteractive = HTAlert_interactive();
2.23 frystyk 538: HTAlert_setInteractive(NO);
539: status = HTLoad(request, NO);
2.33 eric 540: HTAlert_setInteractive(wasInteractive);
2.22 frystyk 541: HTRequest_delete(request);
542: HT_FREE(file);
543: HT_FREE(index);
2.1 frystyk 544: }
2.22 frystyk 545: return status;
2.1 frystyk 546: }
547:
2.22 frystyk 548: /* ------------------------------------------------------------------------- */
549: /* CACHE PARAMETERS */
550: /* ------------------------------------------------------------------------- */
2.1 frystyk 551:
2.22 frystyk 552: PRIVATE BOOL create_cache_root (const char * cache_root)
2.1 frystyk 553: {
554: struct stat stat_info;
2.22 frystyk 555: char * loc = NULL;
2.1 frystyk 556: char * cur = NULL;
557: BOOL create = NO;
2.22 frystyk 558: if (!cache_root) return NO;
559: StrAllocCopy(loc, cache_root); /* Get our own copy */
560: cur = loc+1;
2.1 frystyk 561: while ((cur = strchr(cur, '/'))) {
2.22 frystyk 562: *cur = '\0';
563: if (create || HT_STAT(loc, &stat_info) == -1) {
564: create = YES; /* To avoid doing stat()s in vain */
565: if (CACHE_TRACE) HTTrace("Cache....... Creating dir `%s\'\n", loc);
566: if (MKDIR(loc, 0777) < 0) {
567: if (CACHE_TRACE) HTTrace("Cache....... can't create\n");
568: HT_FREE(loc);
2.1 frystyk 569: return NO;
570: }
571: } else {
2.22 frystyk 572: if (CACHE_TRACE)
573: HTTrace("Cache....... dir `%s\' already exists\n", loc);
2.1 frystyk 574: }
2.22 frystyk 575: *cur++ = '/';
2.1 frystyk 576: }
2.22 frystyk 577: HT_FREE(loc);
2.1 frystyk 578: return YES;
579: }
580:
2.22 frystyk 581: /*
582: ** If `cache_root' is NULL then the current value (might be a define)
583: ** Should we check if the cache_root is actually OK? I think not!
2.1 frystyk 584: */
2.22 frystyk 585: PRIVATE BOOL HTCacheMode_setRoot (const char * cache_root)
2.1 frystyk 586: {
2.22 frystyk 587: StrAllocCopy(HTCacheRoot, cache_root ? cache_root : HT_CACHE_ROOT);
588: if (*(HTCacheRoot+strlen(HTCacheRoot)-1) != '/')
589: StrAllocCat(HTCacheRoot, "/");
590: if (create_cache_root(HTCacheRoot) == NO) return NO;
591: if (CACHE_TRACE) HTTrace("Cache Root.. Root set to `%s\'\n", HTCacheRoot);
592: return YES;
2.1 frystyk 593: }
594:
595: /*
2.22 frystyk 596: ** Return the value of the cache root. The cache root can only be
597: ** set through the HTCacheInit() function
598: */
599: PUBLIC const char * HTCacheMode_getRoot (void)
600: {
601: return HTCacheRoot;
2.1 frystyk 602: }
603:
2.22 frystyk 604: /*
2.1 frystyk 605: ** If `cache_root' is NULL then reuse old value or use HT_CACHE_ROOT.
606: ** An empty string will make '/' as cache root
2.12 frystyk 607: ** We can only enable the cache if the HTSecure flag is not set. This
608: ** is for example the case if using an application as a telnet shell.
2.1 frystyk 609: */
2.22 frystyk 610: PUBLIC BOOL HTCacheInit (const char * cache_root, int size)
2.1 frystyk 611: {
2.22 frystyk 612: if (!HTLib_secure() && !HTCacheRoot) {
613:
614: /*
615: ** Find an appropriate root for the cache
616: */
617: if (HTCacheMode_setRoot(cache_root) != YES) return NO;
618:
619: /*
620: ** Set the max size of the cache
621: */
622: HTCacheMode_setMaxSize(size);
623:
624: /*
625: ** Look for the cache index and read the contents
626: */
627: HTCacheIndex_read(HTCacheRoot);
628:
629: /*
630: ** Do caching from now on
631: */
2.12 frystyk 632: HTCacheEnable = YES;
633: return YES;
634: }
635: return NO;
2.1 frystyk 636: }
637:
2.22 frystyk 638: /*
639: ** Turns off the cache and updates entries on disk.
2.1 frystyk 640: */
2.22 frystyk 641: PUBLIC BOOL HTCacheTerminate (void)
2.1 frystyk 642: {
2.22 frystyk 643: /*
644: ** Write the index to file
645: */
646: HTCacheIndex_write(HTCacheRoot);
647:
648: /*
649: ** Cleanup memory by deleting all HTCache objects
650: */
651: HTCache_deleteAll();
652:
653: /*
654: ** Don't do anymore caching from now on
655: */
656: HT_FREE(HTCacheRoot);
2.1 frystyk 657: HTCacheEnable = NO;
658: return YES;
659: }
660:
2.22 frystyk 661: /*
662: ** The cache can be temporarily suspended by using the enable/disable
663: ** flag. This does not prevent the cache from being enabled/disable at
664: ** a later point in time.
2.1 frystyk 665: */
2.22 frystyk 666: PUBLIC void HTCacheMode_setEnabled (BOOL mode)
667: {
668: HTCacheEnable = mode;
669: }
670:
671: PUBLIC BOOL HTCacheMode_enabled (void)
2.1 frystyk 672: {
2.12 frystyk 673: return HTCacheEnable;
2.1 frystyk 674: }
675:
2.22 frystyk 676: /*
677: ** We can set the cache to operate in disconnected mode in which we only
678: ** return (valid) responses from the cache. Disconnected mode does not
679: ** automatically deliver stale documents as this must be declared
680: ** explicitly.
2.1 frystyk 681: */
2.22 frystyk 682: PUBLIC void HTCacheMode_setDisconnected (HTDisconnectedMode mode)
2.1 frystyk 683: {
2.22 frystyk 684: DisconnectedMode = mode;
2.1 frystyk 685: }
686:
2.22 frystyk 687: PUBLIC HTDisconnectedMode HTCacheMode_disconnected (void)
2.1 frystyk 688: {
2.22 frystyk 689: return DisconnectedMode;
2.1 frystyk 690: }
691:
2.22 frystyk 692: PUBLIC BOOL HTCacheMode_isDisconnected (HTReload mode)
2.1 frystyk 693: {
2.22 frystyk 694: return (DisconnectedMode != HT_DISCONNECT_NONE);
2.1 frystyk 695: }
696:
2.7 frystyk 697: /*
698: ** Set the mode for how we handle Expires header from the local history
699: ** list. The following modes are available:
700: **
701: ** HT_EXPIRES_IGNORE : No update in the history list
702: ** HT_EXPIRES_NOTIFY : The user is notified but no reload
703: ** HT_EXPIRES_AUTO : Automatic reload
704: */
2.22 frystyk 705: PUBLIC void HTCacheMode_setExpires (HTExpiresMode mode)
2.7 frystyk 706: {
707: HTExpMode = mode;
708: }
709:
2.22 frystyk 710: PUBLIC HTExpiresMode HTCacheMode_expires (void)
2.7 frystyk 711: {
712: return HTExpMode;
713: }
714:
2.22 frystyk 715: /*
716: ** Cache size management. We set the default cache size to 20M.
717: ** We set the minimum size to 5M in order not to get into weird
718: ** problems while writing the cache. The size is indicated in Mega
719: ** bytes
720: */
721: PUBLIC BOOL HTCacheMode_setMaxSize (int size)
722: {
723: long new_size = size < 5 ? MIN_CACHE_SIZE : size * MEGA;
724: if (new_size < HTTotalSize) HTCacheGarbage();
725: HTCacheSize = new_size - SIZE_BUFFER;
726: if (CACHE_TRACE) HTTrace("Cache...... Total cache size: %ld\n", new_size);
727: return YES;
728: }
729:
730: PUBLIC int HTCacheMode_maxSize (void)
731: {
732: return HTCacheSize / MEGA;
733: }
734:
2.7 frystyk 735: /* ------------------------------------------------------------------------- */
2.22 frystyk 736: /* CACHE OBJECT */
2.2 frystyk 737: /* ------------------------------------------------------------------------- */
738:
2.22 frystyk 739: PRIVATE BOOL free_object (HTCache * me)
740: {
741: HT_FREE(me->url);
742: HT_FREE(me->cachename);
2.34 frystyk 743: HT_FREE(me->etag);
2.22 frystyk 744: HT_FREE(me);
745: return YES;
746: }
747:
748: PRIVATE BOOL delete_object (HTList * list, HTCache * me)
749: {
750: if (CACHE_TRACE) HTTrace("Cache....... delete %p from list %p\n",me, list);
751: HTList_removeObject(list, (void *) me);
752: HTTotalSize -= me->size;
753: free_object(me);
754: return YES;
755: }
756:
757: /*
758: ** Create directory path for cache file
759: **
760: ** On exit:
761: ** return YES
762: ** if directories created -- after that caller
763: ** can rely on fopen(cfn,"w") succeeding.
764: **
765: */
766: PRIVATE BOOL HTCache_createLocation (HTCache * me)
767: {
768: if (me && HTCacheRoot) {
769: BOOL status = YES;
770: char * path = NULL;
771: struct stat stat_info;
772: if ((path = (char *) HT_MALLOC(strlen(HTCacheRoot) + 10)) == NULL)
773: HT_OUTOFMEM("HTCache_createLocation");
774: sprintf(path, "%s%d", HTCacheRoot, me->hash);
775: if (HT_STAT(path, &stat_info) == -1) {
776: if (CACHE_TRACE) HTTrace("Cache....... Create dir `%s\'\n", path);
777: if (MKDIR(path, 0777) < 0) {
778: if (CACHE_TRACE) HTTrace("Cache....... Can't create...\n");
779: status = NO;
780: }
781: } else {
782: if (CACHE_TRACE)
783: HTTrace("Cache....... Directory `%s\' already exists\n", path);
784: }
785: HT_FREE(path);
786: return status;
787: }
788: return NO;
789: }
790:
791: /*
792: ** Find a cache filename for this cache object.
793: */
794: PRIVATE BOOL HTCache_findName (HTCache * me)
795: {
796: if (me) {
797: /*
798: ** Create path for this cache entry. We base the cache location on the
799: ** hash calculated as a function of the URL. That way, we ensure a
800: ** resonably uniform distribution.
801: */
802: me->cachename = HTGetTmpFileName(NULL);
803: return HTCache_createLocation(me);
804: }
805: return NO;
806: }
807:
808: /*
2.24 frystyk 809: ** Calculate the corrected_initial_age of the object. We use the time
810: ** when this function is called as the response_time as this is when
811: ** we have received the complete response. This may cause a delay if
2.26 frystyk 812: ** the reponse header is very big but should not cause any incorrect
2.24 frystyk 813: ** behavior.
814: */
2.26 frystyk 815: PRIVATE BOOL calculate_time (HTCache * me, HTRequest * request,
816: HTResponse * response)
2.24 frystyk 817: {
818: if (me && request) {
819: HTParentAnchor * anchor = HTRequest_anchor(request);
2.26 frystyk 820: time_t date = HTAnchor_date(anchor);
2.24 frystyk 821: me->response_time = time(NULL);
822: me->expires = HTAnchor_expires(anchor);
823: {
2.26 frystyk 824: time_t apparent_age = HTMAX(0, me->response_time - date);
2.24 frystyk 825: time_t corrected_received_age = HTMAX(apparent_age, HTAnchor_age(anchor));
826: time_t response_delay = me->response_time - HTRequest_date(request);
827: me->corrected_initial_age = corrected_received_age + response_delay;
828: }
829:
830: /*
831: ** Estimate an expires time using the max-age and expires time. If we
832: ** don't have an explicit expires time then set it to 10% of the LM
2.31 frystyk 833: ** date (although max 24 h). If no LM date is available then use 24 hours.
2.24 frystyk 834: */
835: {
2.26 frystyk 836: time_t freshness_lifetime = HTResponse_maxAge(response);
2.24 frystyk 837: if (freshness_lifetime < 0) {
838: if (me->expires < 0) {
839: time_t lm = HTAnchor_lastModified(anchor);
2.31 frystyk 840: if (lm < 0) {
841: freshness_lifetime = DefaultExpiration;
842: } else {
843: freshness_lifetime = LM_EXPIRATION(date - lm);
844: if (freshness_lifetime > WARN_HEURISTICS)
845: HTRequest_addError(request, ERR_WARN, NO,
846: HTERR_HEURISTIC_EXPIRATION, NULL, 0,
847: "calculate_time");
848: }
2.24 frystyk 849: } else
2.26 frystyk 850: freshness_lifetime = me->expires - date;
2.24 frystyk 851: }
852: me->freshness_lifetime = HTMAX(0, freshness_lifetime);
853: }
854: if (CACHE_TRACE) {
855: HTTrace("Cache....... Received Age %d, corrected %d, freshness lifetime %d\n",
856: HTAnchor_age(anchor),
857: me->corrected_initial_age,
858: me->freshness_lifetime);
859: }
860: return YES;
861: }
862: return NO;
863: }
864:
865: /*
2.22 frystyk 866: ** Create a new cache entry and add it to the list
867: */
2.26 frystyk 868: PRIVATE HTCache * HTCache_new (HTRequest * request, HTResponse * response,
869: HTParentAnchor * anchor)
2.22 frystyk 870: {
871: HTList * list = NULL; /* Current list in cache */
872: HTCache * pres = NULL;
873: int hash = 0;
874: char * url = NULL;
2.26 frystyk 875: if (!request || !response || !anchor) {
2.22 frystyk 876: if (CORE_TRACE) HTTrace("Cache....... Bad argument\n");
877: return NULL;
878: }
879:
880: /* Find a hash for this anchor */
881: if ((url = HTAnchor_address((HTAnchor *) anchor))) {
882: char * ptr;
883: for (ptr=url; *ptr; ptr++)
884: hash = (int) ((hash * 3 + (*(unsigned char *) ptr)) % HASH_SIZE);
885: if (!CacheTable) {
886: if ((CacheTable = (HTList **) HT_CALLOC(HASH_SIZE,
887: sizeof(HTList *))) == NULL)
888: HT_OUTOFMEM("HTCache_new");
889: }
890: if (!CacheTable[hash]) CacheTable[hash] = HTList_new();
891: list = CacheTable[hash];
892: } else
893: return NULL;
894:
895: /* Search the cache */
896: {
897: HTList * cur = list;
898: while ((pres = (HTCache *) HTList_nextObject(cur))) {
899: if (!strcmp(pres->url, url)) break;
900: }
901: }
902:
903: /* If not found then create new cache object, else use existing one */
904: if (!pres) {
905: if ((pres = (HTCache *) HT_CALLOC(1, sizeof(HTCache))) == NULL)
906: HT_OUTOFMEM("HTCache_new");
907: pres->hash = hash;
908: pres->url = url;
2.27 frystyk 909: pres->range = NO;
2.22 frystyk 910: HTCache_findName(pres);
911: HTList_addObject(list, (void *) pres);
912: new_entries++;
2.24 frystyk 913: } else
914: HT_FREE(url);
2.22 frystyk 915:
916: if (HTCache_hasLock(pres)) {
2.39 frystyk 917: if (HTCache_breakLock(pres, request) == NO) {
918: if (CACHE_TRACE) HTTrace("Cache....... Entry %p already in use\n");
919: return pres;
920: }
2.22 frystyk 921: }
2.39 frystyk 922: HTCache_getLock(pres, request);
2.22 frystyk 923:
2.39 frystyk 924:
2.24 frystyk 925: /* Calculate the various times */
2.26 frystyk 926: calculate_time(pres, request, response);
2.24 frystyk 927:
2.34 frystyk 928: /* Get the last-modified and etag values if any */
929: {
930: char * etag = HTAnchor_etag(anchor);
931: if (etag) StrAllocCopy(pres->etag, etag);
932: pres->lm = HTAnchor_lastModified(anchor);
933: }
934:
2.26 frystyk 935: /* Must we revalidate this every time? */
936: pres->must_revalidate = HTResponse_mustRevalidate(response);
2.22 frystyk 937: return pres;
938: }
939:
940: /*
941: ** Set the size of a cached object. We don't consider the metainformation as
942: ** part of the size which is the the reason for why the min cache size should
943: ** not be less than 5M. When we set the cache size we also check whether we
944: ** should run the gc or not.
945: */
2.27 frystyk 946: PRIVATE BOOL HTCache_setSize (HTCache * cache, long written, BOOL append)
2.22 frystyk 947: {
948: if (cache) {
2.26 frystyk 949: /*
950: ** First look to see if we already have registered this cache entry
951: ** with a certain size. This size may be a subpart of the total entity
952: ** (in case the download was interrupted)
953: */
2.27 frystyk 954: if (cache->size > 0 && !append) HTTotalSize -= cache->size;
955: cache->size = written;
956: HTTotalSize += written;
2.26 frystyk 957:
958: /*
959: ** Now add the new size to the total cache size. If the new size is
960: ** bigger than the legal cache size then start the gc.
961: */
2.22 frystyk 962: if (CACHE_TRACE) HTTrace("Cache....... Total size %ld\n", HTTotalSize);
963: if (HTTotalSize > HTCacheSize) HTCacheGarbage();
964: return YES;
965: }
966: return NO;
967: }
968:
2.2 frystyk 969: /*
970: ** Verifies if a cache object exists for this URL and if so returns a URL
971: ** for the cached object. It does not verify whether the object is valid or
972: ** not, for example it might have expired.
973: **
2.17 frystyk 974: ** Returns: file name If OK (must be freed by caller)
2.2 frystyk 975: ** NULL If no cache object found
976: */
2.22 frystyk 977: PUBLIC HTCache * HTCache_find (HTParentAnchor * anchor)
978: {
979: HTList * list = NULL;
980: HTCache * pres = NULL;
981:
982: /* Find a hash entry for this URL */
983: if (HTCacheMode_enabled() && anchor && CacheTable) {
984: char * url = HTAnchor_address((HTAnchor *) anchor);
985: int hash = 0;
986: char * ptr = url;
987: for (; *ptr; ptr++)
988: hash = (int) ((hash * 3 + (*(unsigned char *) ptr)) % HASH_SIZE);
989: if (!CacheTable[hash]) {
990: HT_FREE(url);
991: return NULL;
992: }
993: list = CacheTable[hash];
994:
995: /* Search the cache */
996: {
997: HTList * cur = list;
998: while ((pres = (HTCache *) HTList_nextObject(cur))) {
999: if (!strcmp(pres->url, url)) {
1000: if (CACHE_TRACE) HTTrace("Cache....... Found %p hits %d\n",
1001: pres, pres->hits);
1002: break;
1003: }
1004: }
1005: }
1006: HT_FREE(url);
1007: }
1008: return pres;
1009: }
1010:
1011: /* HTCache_delete
1012: ** --------------
1013: ** Deletes a cache entry
1014: */
1015: PRIVATE BOOL HTCache_delete (HTCache * cache)
2.2 frystyk 1016: {
2.22 frystyk 1017: if (cache && CacheTable) {
1018: HTList * cur = CacheTable[cache->hash];
1019: return cur && delete_object(cur, cache);
1020: }
1021: return NO;
1022: }
1023:
1024: /* HTCache_deleteAll
1025: ** -----------------
1026: ** Destroys all cache entried in memory but does not write anything to
1027: ** disk
1028: */
1029: PUBLIC BOOL HTCache_deleteAll (void)
1030: {
1031: if (CacheTable) {
1032: HTList * cur;
1033: int cnt;
1034:
1035: /* Delete the rest */
1036: for (cnt=0; cnt<HASH_SIZE; cnt++) {
1037: if ((cur = CacheTable[cnt])) {
1038: HTCache * pres;
1039: while ((pres = (HTCache *) HTList_nextObject(cur)) != NULL)
1040: free_object(pres);
1041: }
1042: HTList_delete(CacheTable[cnt]);
2.2 frystyk 1043: }
2.22 frystyk 1044: HT_FREE(CacheTable);
1045: HTTotalSize = 0L;
1046: return YES;
2.2 frystyk 1047: }
2.22 frystyk 1048: return NO;
2.2 frystyk 1049: }
1050:
1051: /*
2.25 frystyk 1052: ** Is we have a valid entry in the cache then we also need a location
1053: ** where we can get it. Hopefully, we may be able to access it
1054: ** thourgh one of our protocol modules, for example the local file
1055: ** module. The name returned is in URL syntax and must be freed by
1056: ** the caller
1057: */
1058: PRIVATE char * HTCache_location (HTCache * cache, BOOL meta)
1059: {
1060: char * local = NULL;
1061: if (cache && HTCacheRoot) {
1062: if (meta) {
1063: if ((local = (char *) HT_MALLOC(strlen(HTCacheRoot) + 10 +
1064: strlen(HT_CACHE_META) +
1065: strlen(cache->cachename))) == NULL)
1066: HT_OUTOFMEM("HTCache_location");
1067: sprintf(local, "%s%d/%s%s", HTCacheRoot, cache->hash, cache->cachename,
1068: HT_CACHE_META);
1069: } else {
1070: if ((local = (char *) HT_MALLOC(strlen(HTCacheRoot) + 10 +
1071: strlen(cache->cachename))) == NULL)
1072: HT_OUTOFMEM("HTCache_location");
1073: sprintf(local, "%s%d/%s", HTCacheRoot, cache->hash, cache->cachename);
1074: }
1075: }
1076: return local;
1077: }
1078:
1079: /*
2.34 frystyk 1080: ** Walk through the set of headers and write those out that we are allowed
1081: ** to store in the cache. We look into the connection header to see what the
1082: ** hop-by-hop headers are and also into the cache-control directive to see what
1083: ** headers should not be cached.
1084: */
1085: PRIVATE BOOL meta_write (FILE * fp, HTRequest * request, HTResponse * response)
1086: {
1087: if (fp && request && response) {
1088: HTAssocList * headers = HTResponse_header(response);
1089: HTAssocList * connection = HTResponse_connection(response);
1090: char * nocache = HTResponse_noCache(response);
1091:
1092: /*
1093: ** If we don't have any headers then just return now.
1094: */
1095: if (!headers) return NO;
1096:
1097: /*
1098: ** Check whether either the connection header or the cache control
1099: ** header includes header names that we should not cache
1100: */
1101: if (connection || nocache) {
1102:
1103: /*
1104: ** Walk though the cache control no-cache directive
1105: */
1106: if (nocache) {
1107: char * line = NULL;
1108: char * ptr = NULL;
1109: char * field = NULL;
1110: StrAllocCopy(line, nocache); /* Get our own copy */
1111: ptr = line;
1112: while ((field = HTNextField(&ptr)) != NULL)
1113: HTAssocList_removeObject(headers, field);
1114: HT_FREE(line);
1115: }
1116:
1117: /*
1118: ** Walk though the connection header
1119: */
1120: if (connection) {
1121: HTAssoc * pres;
1122: while ((pres=(HTAssoc *) HTAssocList_nextObject(connection))) {
1123: char * field = HTAssoc_name(pres);
1124: HTAssocList_removeObject(headers, field);
1125: }
1126: }
1127: }
1128:
1129: /*
1130: ** Write out the remaining list of headers that we not already store
1131: ** in the index file.
1132: */
1133: {
1134: HTAssocList * cur = headers;
1135: HTAssoc * pres;
1136: while ((pres = (HTAssoc *) HTAssocList_nextObject(cur))) {
1137: if (fprintf(fp, "%s: %s\n", HTAssoc_name(pres), HTAssoc_value(pres))<0) {
1138: if (CACHE_TRACE) HTTrace("Cache....... Error writing metainfo\n");
1139: return NO;
1140: }
1141: }
1142: }
1143:
1144: /*
1145: ** Terminate the header with a newline
1146: */
1147: if (fprintf(fp, "\n") < 0) {
1148: if (CACHE_TRACE) HTTrace("Cache....... Error writing metainfo\n");
1149: return NO;
1150: }
1151: return YES;
1152: }
1153: return NO;
1154: }
1155:
1156: /*
1157: ** Save the metainformation for the data object. If no headers
1158: ** are available then the meta file is empty
1159: */
1160: PUBLIC BOOL HTCache_writeMeta (HTCache * cache, HTRequest * request,
1161: HTResponse * response)
1162: {
1163: if (cache && request && response) {
1164: BOOL status;
1165: FILE * fp;
1166: char * name = HTCache_location(cache, YES);
1167: if (!name) return NO;
1168: if ((fp = fopen(name, "wb")) == NULL) {
1169: if (CACHE_TRACE)
1170: HTTrace("Cache....... Can't open `%s\' for writing\n", name);
1171: HTCache_remove(cache);
1172: HT_FREE(name);
1173: return NO;
1174: }
1175: status = meta_write(fp, request, response);
1176: fclose(fp);
1177: HT_FREE(name);
1178: return status;
1179: }
1180: return NO;
1181: }
1182:
1183: PRIVATE BOOL meta_read (FILE * fp, HTRequest * request, HTStream * target)
1184: {
1185: if (fp && request && target) {
1186: int status;
1187: char buffer[512];
1188: while (1) {
1189: if ((status = fread(buffer, 1, 512, fp)) <= 0) {
1190: if (PROT_TRACE) HTTrace("Cache....... Meta information loaded\n");
1191: return YES;
1192: }
1193:
1194: /* Send the data down the pipe */
1195: status = (*target->isa->put_block)(target, buffer, status);
1196: if (status == HT_LOADED) {
1197: (*target->isa->flush)(target);
1198: return YES;
1199: }
1200: if (status < 0) {
1201: if (PROT_TRACE) HTTrace("Cache....... Target ERROR %d\n", status);
1202: break;
1203: }
1204: }
1205: }
1206: return NO;
1207: }
1208:
1209: /*
1210: ** Read the metainformation for the data object. If no headers are
1211: ** available then the meta file is empty
1212: */
1213: PRIVATE BOOL HTCache_readMeta (HTCache * cache, HTRequest * request)
1214: {
1215: HTParentAnchor * anchor = HTRequest_anchor(request);
1216: if (cache && request && anchor) {
1217: BOOL status;
1218: FILE * fp;
1219: char * name = HTCache_location(cache, YES);
1220: if (!name) return NO;
1221: if (CACHE_TRACE) HTTrace("Cache....... Looking for `%s\'\n", name);
1222: if ((fp = fopen(name, "rb")) == NULL) {
1223: if (CACHE_TRACE)
1224: HTTrace("Cache....... Can't open `%s\' for reading\n", name);
1225: HTCache_remove(cache);
1226: HT_FREE(name);
1227: } else {
1228: HTStream * target = HTStreamStack(WWW_MIME_HEAD, WWW_DEBUG,
1229: HTBlackHole(), request, NO);
2.42 ! frystyk 1230: /*
! 1231: ** Make sure that we save the reponse information in the anchor
! 1232: */
! 1233: HTResponse_setCachable(HTRequest_response(request), YES);
2.34 frystyk 1234: status = meta_read(fp, request, target);
1235: (*target->isa->_free)(target);
1236: fclose(fp);
1237: HT_FREE(name);
1238: return status;
1239: }
1240: }
1241: return NO;
1242: }
1243:
1244: /*
1245: ** Merge metainformation with existing version. This means that we have had a
1246: ** successful validation and hence a true cache hit. We only regard the
1247: ** following headers: Date, content-location, expires, cache-control, and vary.
1248: */
1249: PUBLIC BOOL HTCache_updateMeta (HTCache * cache, HTRequest * request,
1250: HTResponse * response)
1251: {
1252: if (cache && request && response) {
2.36 frystyk 1253: HTParentAnchor * anchor = HTRequest_anchor(request);
2.34 frystyk 1254: cache->hits++;
2.36 frystyk 1255:
1256: /* Calculate the various times */
1257: calculate_time(cache, request, response);
1258:
1259: /* Get the last-modified and etag values if any */
1260: {
1261: char * etag = HTAnchor_etag(anchor);
1262: if (etag) StrAllocCopy(cache->etag, etag);
1263: cache->lm = HTAnchor_lastModified(anchor);
1264: }
1265:
1266: /* Must we revalidate this every time? */
1267: cache->must_revalidate = HTResponse_mustRevalidate(response);
1268:
1269: return YES;
2.34 frystyk 1270: }
1271: return NO;
1272: }
1273:
1274: /*
2.25 frystyk 1275: ** Remove from disk. You must explicitly remove a lock
1276: ** before this operation can succeed
1277: */
1278: PRIVATE BOOL flush_object (HTCache * cache)
1279: {
1280: if (cache && !HTCache_hasLock(cache)) {
1281: char * head = HTCache_location(cache, YES);
1282: char * body = HTCache_location(cache, NO);
1283: REMOVE(head);
1284: REMOVE(body);
1285: HT_FREE(head);
1286: HT_FREE(body);
1287: return YES;
1288: }
1289: return NO;
1290: }
1291:
1292: /* HTCache_flushAll
1293: ** ----------------
1294: ** Destroys all cache entried in memory and on disk. Resets the cache
1295: ** to empty but the cache does not have to be reinitialized before we
1296: ** can use it again.
1297: */
1298: PUBLIC BOOL HTCache_flushAll (void)
1299: {
1300: if (CacheTable) {
1301: HTList * cur;
1302: int cnt;
1303:
1304: /* Delete the rest */
1305: for (cnt=0; cnt<HASH_SIZE; cnt++) {
1306: if ((cur = CacheTable[cnt])) {
1307: HTCache * pres;
1308: while ((pres = (HTCache *) HTList_nextObject(cur)) != NULL) {
1309: flush_object(pres);
1310: free_object(pres);
1311: }
1312: }
1313: HTList_delete(CacheTable[cnt]);
1314: CacheTable[cnt] = NULL;
1315: }
1316:
1317: /* Write the new empty index to disk */
1318: HTCacheIndex_write(HTCacheRoot);
1319: HTTotalSize = 0L;
1320: return YES;
1321: }
1322: return NO;
1323: }
1324:
1325: /*
2.2 frystyk 1326: ** This function checks whether a document has expired or not.
1327: ** The check is based on the metainformation passed in the anchor object
2.22 frystyk 1328: ** The function returns the level of validation needed for getting a fresh
1329: ** version. We also check the cache control directives in the request to
1330: ** see if they change the freshness discission.
1331: */
1332: PUBLIC HTReload HTCache_isFresh (HTCache * cache, HTRequest * request)
1333: {
1334: HTAssocList * cc = HTRequest_cacheControl(request);
1335: if (cache) {
1336: time_t max_age = -1;
1337: time_t max_stale = -1;
1338: time_t min_fresh = -1;
1339:
1340: /*
2.34 frystyk 1341: ** Make sure that we have the metainformation loaded from the
1342: ** persistent cache
1343: */
1344: HTParentAnchor * anchor = HTRequest_anchor(request);
1345: if (!HTAnchor_headerParsed(anchor)) HTCache_readMeta(cache, request);
1346:
1347: /*
2.26 frystyk 1348: ** If we only have a part of this request then make a range request
1349: ** using the If-Range condition GET request
1350: */
2.27 frystyk 1351: if (cache->range) {
2.26 frystyk 1352: char buf[20];
2.27 frystyk 1353: sprintf(buf, "%ld-", cache->size);
2.26 frystyk 1354: if (CACHE_TRACE) HTTrace("Cache....... Asking for range `%s\'\n", buf);
1355: HTRequest_addRange(request, "bytes", buf);
1356: HTRequest_addRqHd(request, HT_C_RANGE);
1357: return HT_CACHE_RANGE_VALIDATE;
1358: }
1359:
1360: /*
2.22 frystyk 1361: ** In case this entry is of type "must-revalidate" then we just
1362: ** go ahead and validate it.
1363: */
1364: if (cache->must_revalidate)
1365: return HT_CACHE_VALIDATE;
1366: /*
1367: ** Check whether we have any special constraints like min-fresh in
1368: ** the cache control
1369: */
1370: if (cc) {
1371: char * token = NULL;
1372: if ((token = HTAssocList_findObject(cc, "max-age")))
1373: max_age = atol(token);
1374: if ((token = HTAssocList_findObject(cc, "max-stale")))
1375: max_stale = atol(token);
1376: if ((token = HTAssocList_findObject(cc, "min-fresh")))
1377: min_fresh = atol(token);
1378: }
1379:
1380: /*
1381: ** Now do the checking against the age constraints that we've got
1382: */
1383: {
1384: time_t resident_time = time(NULL) - cache->response_time;
1385: time_t current_age = cache->corrected_initial_age + resident_time;
1386:
1387: /*
1388: ** Check that the max-age, max-stale, and min-fresh directives
1389: ** given in the request cache control header is followed.
1390: */
1391: if (max_age >= 0 && current_age > max_age) {
1392: if (CACHE_TRACE) HTTrace("Cache....... Max-age validation\n");
1393: return HT_CACHE_VALIDATE;
1394: }
1395: if (min_fresh >= 0 &&
1396: cache->freshness_lifetime < current_age + min_fresh) {
1397: if (CACHE_TRACE) HTTrace("Cache....... Min-fresh validation\n");
1398: return HT_CACHE_VALIDATE;
1399: }
1400:
1401: return (cache->freshness_lifetime +
1402: (max_stale >= 0 ? max_stale : 0) > current_age) ?
1403: HT_CACHE_OK : HT_CACHE_VALIDATE;
1404: }
1405: }
1406: return HT_CACHE_FLUSH;
1407: }
1408:
1409: /*
1410: ** While we are creating a new cache object or while we are validating an
1411: ** existing one, we must have a lock on the entry so that not other
1412: ** requests can get to it in the mean while.
1413: */
1414: PUBLIC BOOL HTCache_getLock (HTCache * cache, HTRequest * request)
1415: {
1416: if (cache && request) {
1417: if (CACHE_TRACE) HTTrace("Cache....... Locking cache entry %p\n", cache);
1418: cache->lock = request;
1419: return YES;
1420: }
1421: return NO;
1422: }
1423:
1424: PUBLIC BOOL HTCache_releaseLock (HTCache * cache)
1425: {
1426: if (cache) {
1427: if (CACHE_TRACE) HTTrace("Cache....... Unlocking cache entry %p\n", cache);
1428: cache->lock = NULL;
1429: return YES;
1430: }
1431: return NO;
1432: }
1433:
1434: PUBLIC BOOL HTCache_hasLock (HTCache * cache)
1435: {
1436: return cache && cache->lock;
1437: }
1438:
1439: PUBLIC BOOL HTCache_breakLock (HTCache * cache, HTRequest * request)
1440: {
1441: if (cache && cache->lock) {
1442: if (cache->lock == request) {
1443: if (CACHE_TRACE)
1444: HTTrace("Cache....... Breaking lock on entry %p\n", cache);
1445: cache->lock = NULL;
1446: return YES;
1447: }
1448: }
1449: return NO;
1450: }
1451:
1452: /*
1453: ** Is we have a valid entry in the cache then we also need a location
1454: ** where we can get it. Hopefully, we may be able to access it
1455: ** thourgh one of our protocol modules, for example the local file
1456: ** module. The name returned is in URL syntax and must be freed by
1457: ** the caller
1458: */
1459: PUBLIC char * HTCache_name (HTCache * cache)
1460: {
1461: if (cache && HTCacheRoot) {
1462: char * local = HTCache_location(cache, NO);
1463: char * url = HTParse(local, "cache:", PARSE_ALL);
1464: HT_FREE(local);
1465: return url;
1466: }
1467: return NULL;
1468: }
1469:
1470: /*
1471: ** Remove from memory AND from disk. You must explicitly remove a lock
1472: ** before this operation can succeed
2.2 frystyk 1473: */
2.22 frystyk 1474: PUBLIC BOOL HTCache_remove (HTCache * cache)
2.2 frystyk 1475: {
2.25 frystyk 1476: return flush_object(cache) && HTCache_delete(cache);
2.22 frystyk 1477: }
1478:
1479: PUBLIC BOOL HTCache_addHit (HTCache * cache)
1480: {
1481: if (cache) {
1482: cache->hits++;
1483: if (CACHE_TRACE) HTTrace("Cache....... Hits for %p is %d\n",
1484: cache, cache->hits);
1485: return YES;
1486: }
1487: return NO;
1488: }
1489:
2.34 frystyk 1490: /* ------------------------------------------------------------------------- */
1491: /* CACHE WRITER */
1492: /* ------------------------------------------------------------------------- */
2.1 frystyk 1493:
2.26 frystyk 1494: PRIVATE BOOL free_stream (HTStream * me, BOOL abort)
2.1 frystyk 1495: {
2.22 frystyk 1496: if (me) {
2.26 frystyk 1497: HTCache * cache = me->cache;
1498:
1499: /*
1500: ** We close the file object. This does not mean that we have the
1501: ** complete object. In case of an "abort" then we only have a part,
1502: ** however, next time we do a load we can use byte ranges to complete
1503: ** the request.
1504: */
2.22 frystyk 1505: if (me->fp) fclose(me->fp);
2.26 frystyk 1506:
2.22 frystyk 1507: /*
1508: ** We are done storing the object body and can update the cache entry.
1509: ** Also update the meta information entry on disk as well. When we
1510: ** are done we don't need the lock anymore.
1511: */
2.26 frystyk 1512: if (cache) {
1513: HTCache_writeMeta(cache, me->request, me->response);
1514: HTCache_releaseLock(cache);
2.22 frystyk 1515:
1516: /*
2.27 frystyk 1517: ** Remember if this is the full entity body or only a subpart
1518: ** We assume that an abort will only give a part of the object.
1519: */
1520: cache->range = abort;
1521:
1522: /*
2.26 frystyk 1523: ** Set the size and maybe do gc. If it is an abort then set the
1524: ** byte range so that we can start from this point next time. We
1525: ** take the byte range as the number of bytes that we have already
1526: ** written to the cache entry.
2.22 frystyk 1527: */
2.27 frystyk 1528: HTCache_setSize(cache, me->bytes_written, me->append);
2.22 frystyk 1529: }
1530:
1531: /*
1532: ** In order not to loose information, we dump the current cache index
1533: ** every time we have created DUMP_FREQUENCY new entries
1534: */
1535: if (new_entries > DUMP_FREQUENCY) {
1536: HTCacheIndex_write(HTCacheRoot);
1537: new_entries = 0;
1538: }
1539: HT_FREE(me);
2.26 frystyk 1540: return YES;
2.22 frystyk 1541: }
2.26 frystyk 1542: return NO;
1543: }
1544:
1545:
1546: PRIVATE int HTCache_free (HTStream * me)
1547: {
2.34 frystyk 1548: return free_stream(me, NO) ? HT_OK : HT_ERROR;
2.1 frystyk 1549: }
1550:
2.12 frystyk 1551: PRIVATE int HTCache_abort (HTStream * me, HTList * e)
2.1 frystyk 1552: {
2.22 frystyk 1553: if (CACHE_TRACE) HTTrace("Cache....... ABORTING\n");
2.34 frystyk 1554: free_stream(me, YES);
1555: return HT_ERROR;
2.1 frystyk 1556: }
1557:
2.34 frystyk 1558: PRIVATE int HTCache_flush (HTStream * me)
1559: {
1560: return (fflush(me->fp) == EOF) ? HT_ERROR : HT_OK;
1561: }
1562:
1563: PRIVATE int HTCache_putBlock (HTStream * me, const char * s, int l)
1564: {
1565: int status = (fwrite(s, 1, l, me->fp) != l) ? HT_ERROR : HT_OK;
1566: if (l > 1 && status == HT_OK) {
1567: HTCache_flush(me);
1568: me->bytes_written += l;
1569: }
2.37 frystyk 1570: return status;
2.34 frystyk 1571: }
1572:
1573: PRIVATE int HTCache_putChar (HTStream * me, char c)
1574: {
1575: return HTCache_putBlock(me, &c, 1);
1576: }
1577:
1578: PRIVATE int HTCache_putString (HTStream * me, const char * s)
1579: {
1580: return HTCache_putBlock(me, s, (int) strlen(s));
1581: }
1582:
2.15 frystyk 1583: PRIVATE const HTStreamClass HTCacheClass =
2.1 frystyk 1584: {
1585: "Cache",
1586: HTCache_flush,
2.17 frystyk 1587: HTCache_free,
2.1 frystyk 1588: HTCache_abort,
1589: HTCache_putChar,
1590: HTCache_putString,
1591: HTCache_putBlock
1592: };
1593:
2.26 frystyk 1594: PRIVATE HTStream * HTCacheStream (HTRequest * request, BOOL append)
2.1 frystyk 1595: {
2.22 frystyk 1596: HTCache * cache = NULL;
1597: FILE * fp = NULL;
2.36 frystyk 1598:
2.26 frystyk 1599: HTResponse * response = HTRequest_response(request);
2.19 frystyk 1600: HTParentAnchor * anchor = HTRequest_anchor(request);
2.12 frystyk 1601: if (!HTCacheEnable) {
2.14 eric 1602: if (CACHE_TRACE) HTTrace("Cache....... Not enabled\n");
2.22 frystyk 1603: return NULL;
2.1 frystyk 1604: }
1605:
2.22 frystyk 1606: /* Get a new cache entry */
2.26 frystyk 1607: if ((cache = HTCache_new(request, response, anchor)) == NULL) {
2.22 frystyk 1608: if (CACHE_TRACE) HTTrace("Cache....... Can't get a cache object\n");
1609: return NULL;
1610: }
1611:
1612: /* Test that the cached object is not locked */
1613: if (HTCache_hasLock(cache)) {
1614: if (HTCache_breakLock(cache, request) == NO) {
1615: if (CACHE_TRACE) HTTrace("Cache....... Entry already in use\n");
1616: return NULL;
1617: }
1618: }
1619: HTCache_getLock(cache, request);
1620:
1621: /*
1622: ** Test that we can actually write to the cache file. If the entry already
1623: ** existed then it will be overridden with the new data.
1624: */
1625: {
1626: char * name = HTCache_location(cache, NO);
2.26 frystyk 1627: if ((fp = fopen(name, append ? "ab" : "wb")) == NULL) {
2.22 frystyk 1628: if (CACHE_TRACE)
1629: HTTrace("Cache....... Can't open `%s\' for writing\n", name);
1630: HTCache_delete(cache);
1631: HT_FREE(name);
1632: return NULL;
2.26 frystyk 1633: } else {
1634: if (CACHE_TRACE)
1635: HTTrace("Cache....... %s file `%s\'\n",
1636: append ? "Append to" : "Creating", name);
1637: }
2.22 frystyk 1638: HT_FREE(name);
1639: }
2.1 frystyk 1640:
1641: /* Set up the stream */
2.22 frystyk 1642: {
1643: HTStream * me = NULL;
1644: if ((me = (HTStream *) HT_CALLOC(1, sizeof(HTStream))) == NULL)
1645: HT_OUTOFMEM("Cache");
1646: me->isa = &HTCacheClass;
1647: me->request = request;
2.26 frystyk 1648: me->response = response;
2.22 frystyk 1649: me->cache = cache;
2.38 frystyk 1650: me->fp = fp;
2.27 frystyk 1651: me->append = append;
2.22 frystyk 1652: return me;
1653: }
1654: return NULL;
1655: }
1656:
2.26 frystyk 1657: PUBLIC HTStream * HTCacheWriter (HTRequest * request,
1658: void * param,
1659: HTFormat input_format,
1660: HTFormat output_format,
1661: HTStream * output_stream)
1662: {
1663: return HTCacheStream(request, NO);
1664: }
1665:
1666: PUBLIC HTStream * HTCacheAppend (HTRequest * request,
1667: void * param,
1668: HTFormat input_format,
1669: HTFormat output_format,
1670: HTStream * output_stream)
1671: {
1672: return HTCacheStream(request, YES);
1673: }
1674:
2.22 frystyk 1675: /* ------------------------------------------------------------------------- */
1676: /* CACHE READER */
1677: /* ------------------------------------------------------------------------- */
1678:
1679: /*
1680: ** This function closes the connection and frees memory.
1681: ** Returns YES on OK, else NO
1682: */
2.32 eric 1683: PRIVATE int CacheCleanup (HTRequest * req, int status)
2.22 frystyk 1684: {
2.32 eric 1685: HTNet * net = HTRequest_net(req);
2.22 frystyk 1686: cache_info * cache = (cache_info *) HTNet_context(net);
2.32 eric 1687: HTStream * input = HTRequest_inputStream(req);
1688:
1689: /* Free stream with data TO Local cache system */
1690: if (input) {
1691: if (status == HT_INTERRUPTED)
1692: (*input->isa->abort)(input, NULL);
1693: else
1694: (*input->isa->_free)(input);
1695: HTRequest_setInputStream(req, NULL);
1696: }
1697:
2.34 frystyk 1698: if (status != HT_IGNORE) {
1699: HTNet_delete(net, status);
1700: if (cache) {
1701: HT_FREE(cache->local);
1702: HT_FREE(cache);
1703: }
2.22 frystyk 1704: }
1705: return YES;
1706: }
1707:
1708: /*
1709: ** This load function loads an object from the cache and puts it to the
1710: ** output defined by the request object. For the moment, this load function
1711: ** handles the persistent cache as if it was on local file but in fact
1712: ** it could be anywhere.
1713: **
1714: ** Returns HT_ERROR Error has occured in call back
1715: ** HT_OK Call back was OK
1716: */
2.29 frystyk 1717: PRIVATE int CacheEvent (SOCKET soc, void * pVoid, HTEventType type);
1718:
1719: PUBLIC int HTLoadCache (SOCKET soc, HTRequest * request)
2.22 frystyk 1720: {
2.29 frystyk 1721: cache_info * cache; /* Specific access information */
1722: HTParentAnchor * anchor = HTRequest_anchor(request);
2.22 frystyk 1723: HTNet * net = HTRequest_net(request);
1724:
1725: /*
1726: ** Initiate a new cache structure and bind to request structure
1727: ** This is actually state CACHE_BEGIN, but it can't be in the state
1728: ** machine as we need the structure first.
1729: */
2.29 frystyk 1730: if (PROT_TRACE) HTTrace("Load Cache.. Looking for `%s\'\n",
1731: HTAnchor_physical(anchor));
1732: if ((cache = (cache_info *) HT_CALLOC(1, sizeof(cache_info))) == NULL)
1733: HT_OUTOFMEM("HTLoadCACHE");
1734: cache->state = CL_BEGIN;
1735: cache->net = net;
1736: HTNet_setContext(net, cache);
1737: HTNet_setEventCallback(net, CacheEvent);
1738: HTNet_setEventParam(net, cache); /* callbacks get http* */
1739:
1740: return CacheEvent(soc, cache, HTEvent_BEGIN); /* get it started - ops is ignored */
1741: }
1742:
1743: PRIVATE int CacheEvent (SOCKET soc, void * pVoid, HTEventType type)
1744: {
1745: cache_info * cache = (cache_info *)pVoid;
1746: int status = HT_ERROR;
1747: HTNet * net = cache->net;
1748: HTRequest * request = HTNet_request(net);
1749: HTParentAnchor * anchor = HTRequest_anchor(request);
1750:
2.32 eric 1751: if (type == HTEvent_BEGIN) {
1752: cache->state = CL_BEGIN;
1753: } else if (type == HTEvent_CLOSE) {
2.22 frystyk 1754: HTRequest_addError(request, ERR_FATAL, NO, HTERR_INTERRUPTED,
1755: NULL, 0, "HTLoadCache");
1756: CacheCleanup(request, HT_INTERRUPTED);
1757: return HT_OK;
2.32 eric 1758: } else if (type == HTEvent_END) {
1759: CacheCleanup(request, HT_OK);
1760: return HT_OK;
1761: } else if (type == HTEvent_RESET) {
1762: CacheCleanup(request, HT_RECOVER_PIPE);
1763: cache->state = CL_BEGIN;
1764: return HT_OK;
1765: }
2.22 frystyk 1766:
1767: /* Now jump into the machine. We know the state from the previous run */
1768: while (1) {
1769: switch (cache->state) {
2.34 frystyk 1770:
2.22 frystyk 1771: case CL_BEGIN:
1772: if (HTLib_secure()) {
1773: if (PROT_TRACE)
1774: HTTrace("Load Cache.. No access to local file system\n");
1775: cache->state = CL_ERROR;
1776: break;
1777: }
2.40 eric 1778: cache->local = HTWWWToLocal(HTAnchor_physical(anchor), "",
1779: HTRequest_userProfile(request));
1780: if (!cache->local) {
1781: cache->state = CL_ERROR;
1782: break;
1783: }
2.42 ! frystyk 1784:
! 1785: /*
! 1786: ** Create a new host object and link it to the net object
! 1787: */
! 1788: {
! 1789: HTHost * host = NULL;
! 1790: if ((host = HTHost_new(cache->local, 0)) == NULL) return HT_ERROR;
! 1791: HTNet_setHost(net, host);
! 1792: if (HTHost_addNet(host, net) == HT_PENDING)
! 1793: if (PROT_TRACE) HTTrace("HTLoadCache. Pending...\n");
! 1794: }
2.34 frystyk 1795: cache->state = CL_NEED_BODY;
2.22 frystyk 1796: break;
1797:
1798: case CL_NEED_BODY:
2.40 eric 1799: if (HT_STAT(cache->local, &cache->stat_info) == -1) {
1800: if (PROT_TRACE)
1801: HTTrace("Load Cache.. Not found `%s\'\n", cache->local);
1802: HTRequest_addError(request, ERR_FATAL, NO, HTERR_NOT_FOUND,
1803: NULL, 0, "HTLoadCache");
1804: cache->state = CL_ERROR;
1805: break;
1806: }
2.22 frystyk 1807:
2.40 eric 1808: /*
1809: ** The cache entry may be empty in which case we just return
1810: */
1811: if (!cache->stat_info.st_size) {
1812: HTRequest_addError(request, ERR_FATAL, NO,HTERR_NO_CONTENT,
1813: NULL, 0, "HTLoadCache");
1814: cache->state = CL_NO_DATA;
1815: } else
1816: cache->state = CL_NEED_OPEN_FILE;
2.22 frystyk 1817: break;
1818:
1819: case CL_NEED_OPEN_FILE:
1820: status = HTFileOpen(net, cache->local, HT_FT_RDONLY);
1821: if (status == HT_OK) {
1822: /*
1823: ** Create the stream pipe FROM the channel to the application.
1824: ** The target for the input stream pipe is set up using the
1825: ** stream stack.
1826: */
2.42 ! frystyk 1827: {
! 1828: HTStream * rstream = HTStreamStack(HTAnchor_format(anchor),
! 1829: HTRequest_outputFormat(request),
! 1830: HTRequest_outputStream(request),
! 1831: request, YES);
! 1832: HTNet_setReadStream(net, rstream);
! 1833: HTRequest_setOutputConnected(request, YES);
! 1834: }
2.32 eric 1835:
2.36 frystyk 1836: /* Set the return code as being OK */
2.34 frystyk 1837: HTRequest_addError(request, ERR_INFO, NO, HTERR_OK,
1838: NULL, 0, "HTLoadCache");
1839: cache->state = CL_NEED_CONTENT;
2.22 frystyk 1840:
1841: #ifndef NO_UNIX_IO
2.34 frystyk 1842: /* If we are _not_ using preemptive mode and we are Unix fd's
1843: ** then return here to get the same effect as when we are
1844: ** connecting to a socket. That way, HTCache acts just like any
1845: ** other protocol module even though we are in fact doing
1846: ** blocking connect
1847: */
2.42 ! frystyk 1848: if (!HTNet_preemptive(net)) {
2.34 frystyk 1849: if (PROT_TRACE) HTTrace("Load Cache.. returning\n");
2.42 ! frystyk 1850: HTHost_register(HTNet_host(net), net, HTEvent_READ);
2.34 frystyk 1851: return HT_OK;
1852: }
2.22 frystyk 1853: #endif
1854: } else if (status == HT_WOULD_BLOCK || status == HT_PENDING)
1855: return HT_OK;
1856: else {
1857: HTRequest_addError(request, ERR_INFO, NO, HTERR_INTERNAL,
1858: NULL, 0, "HTLoadCache");
1859: cache->state = CL_ERROR; /* Error or interrupt */
1860: }
1861: break;
1862:
1863: case CL_NEED_CONTENT:
2.42 ! frystyk 1864: status = HTHost_read(HTNet_host(net), net);
2.22 frystyk 1865: if (status == HT_WOULD_BLOCK)
1866: return HT_OK;
1867: else if (status == HT_LOADED || status == HT_CLOSED) {
1868: cache->state = CL_GOT_DATA;
1869: } else {
1870: HTRequest_addError(request, ERR_INFO, NO, HTERR_FORBIDDEN,
1871: NULL, 0, "HTLoadCache");
1872: cache->state = CL_ERROR;
1873: }
1874: break;
1875:
1876: case CL_GOT_DATA:
2.35 frystyk 1877: CacheCleanup(request, HT_NOT_MODIFIED);
2.34 frystyk 1878: return HT_OK;
2.22 frystyk 1879: break;
2.1 frystyk 1880:
2.22 frystyk 1881: case CL_NO_DATA:
1882: CacheCleanup(request, HT_NO_DATA);
1883: return HT_OK;
1884: break;
1885:
1886: case CL_ERROR:
1887: CacheCleanup(request, HT_ERROR);
1888: return HT_OK;
1889: break;
1890: }
1891: } /* End of while(1) */
2.1 frystyk 1892: }
Webmaster