Annotation of libwww/Library/src/HTCache.c, revision 2.63
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.63 ! kahan 6: ** @(#) $Id: HTCache.c,v 2.62 1999/02/07 18:20:31 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.50 frystyk 18: #include "wwwsys.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.43 frystyk 26: #define HT_CACHE_LOC "/tmp/"
27: #define HT_CACHE_ROOT "w3c-cache/"
2.22 frystyk 28: #define HT_CACHE_INDEX ".index"
2.48 frystyk 29: #define HT_CACHE_LOCK ".lock"
2.22 frystyk 30: #define HT_CACHE_META ".meta"
2.34 frystyk 31: #define HT_CACHE_ETAG "@w3c@"
2.22 frystyk 32:
2.31 frystyk 33: /* Default heuristics cache expirations - thanks to Jeff Mogul for good comments! */
34: #define NO_LM_EXPIRATION 24*3600 /* 24 hours */
35: #define MAX_LM_EXPIRATION 48*3600 /* Max expiration from LM */
36:
37: /*
38: ** If using LM to find the expiration then take 10% and no more than
39: ** MAX_LM_EXPIRATION.
40: */
41: #ifndef LM_EXPIRATION
42: #define LM_EXPIRATION(t) (HTMIN((MAX_LM_EXPIRATION), (t) / 10))
43: #endif
44:
2.56 frystyk 45: #define WARN_HEURISTICS 24*3600 /* When to issue a warning */
2.31 frystyk 46:
2.56 frystyk 47: #define DUMP_FREQUENCY 10 /* Dump index every x loads */
2.22 frystyk 48:
2.56 frystyk 49: #define MEGA 0x100000L
50: #define HT_CACHE_TOTAL_SIZE 20 /* Default cache size is 20M */
51: #define HT_CACHE_FOLDER_PCT 10 /* 10% of cache size for metainfo etc. */
2.57 frystyk 52: #define HT_CACHE_GC_PCT 10 /* 10% of cache size free after GC */
2.56 frystyk 53: #define HT_MIN_CACHE_TOTAL_SIZE 5 /* 5M Min cache size */
54: #define HT_MAX_CACHE_ENTRY_SIZE 3 /* 3M Max sixe of single cached entry */
2.22 frystyk 55:
56: /* Final states have negative value */
57: typedef enum _CacheState {
58: CL_ERROR = -3,
59: CL_NO_DATA = -2,
60: CL_GOT_DATA = -1,
61: CL_BEGIN = 0,
62: CL_NEED_BODY,
63: CL_NEED_OPEN_FILE,
64: CL_NEED_CONTENT
65: } CacheState;
66:
67: /* This is the context structure for the this module */
68: typedef struct _cache_info {
69: CacheState state; /* Current state of the connection */
70: char * local; /* Local representation of file name */
71: struct stat stat_info; /* Contains actual file chosen */
2.29 frystyk 72: HTNet * net;
2.55 frystyk 73: HTTimer * timer;
2.22 frystyk 74: } cache_info;
75:
76: struct _HTCache {
2.24 frystyk 77: /* Location */
2.22 frystyk 78: int hash;
79: char * url;
80: char * cachename;
2.24 frystyk 81:
82: /* GC parameters */
2.35 frystyk 83: char * etag;
2.27 frystyk 84: BOOL range; /* Is this the full part or a subpart? */
2.35 frystyk 85: BOOL must_revalidate;
2.26 frystyk 86: int hits; /* Hit counts */
2.35 frystyk 87: long size; /* Size of cached entity body */
2.34 frystyk 88: time_t lm; /* Last modified */
2.35 frystyk 89: time_t expires;
2.22 frystyk 90: time_t freshness_lifetime;
91: time_t response_time;
92: time_t corrected_initial_age;
93: HTRequest * lock;
94: };
2.1 frystyk 95:
96: struct _HTStream {
2.15 frystyk 97: const HTStreamClass * isa;
2.1 frystyk 98: FILE * fp;
2.26 frystyk 99: long bytes_written; /* Number of bytes written */
2.7 frystyk 100: HTCache * cache;
2.1 frystyk 101: HTRequest * request;
2.26 frystyk 102: HTResponse * response;
2.22 frystyk 103: HTChunk * buffer; /* For index reading */
104: HTEOLState EOLstate;
2.27 frystyk 105: BOOL append; /* Creating or appending? */
2.22 frystyk 106: };
107:
108: struct _HTInputStream {
109: const HTInputStreamClass * isa;
2.1 frystyk 110: };
111:
2.22 frystyk 112: /* Cache parameters */
113: PRIVATE BOOL HTCacheEnable = NO; /* Disabled by default */
2.48 frystyk 114: PRIVATE BOOL HTCacheInitialized = NO;
2.63 ! kahan 115: PRIVATE BOOL HTCacheProtected = YES;
2.61 frystyk 116: PRIVATE char * HTCacheRoot = NULL; /* Local Destination for cache */
2.22 frystyk 117: PRIVATE HTExpiresMode HTExpMode = HT_EXPIRES_IGNORE;
118: PRIVATE HTDisconnectedMode DisconnectedMode = HT_DISCONNECT_NONE;
2.1 frystyk 119:
2.31 frystyk 120: /* Heuristic expiration parameters */
121: PRIVATE int DefaultExpiration = NO_LM_EXPIRATION;
122:
2.22 frystyk 123: /* List of cache entries */
124: PRIVATE HTList ** CacheTable = NULL;
125:
126: /* Cache size variables */
2.56 frystyk 127: PRIVATE long HTCacheTotalSize = HT_CACHE_TOTAL_SIZE*MEGA;
128: PRIVATE long HTCacheFolderSize = (HT_CACHE_TOTAL_SIZE*MEGA)/HT_CACHE_FOLDER_PCT;
2.57 frystyk 129: PRIVATE long HTCacheGCBuffer = (HT_CACHE_TOTAL_SIZE*MEGA)/HT_CACHE_GC_PCT;
2.56 frystyk 130: PRIVATE long HTCacheContentSize = 0L;
131: PRIVATE long HTCacheMaxEntrySize = HT_MAX_CACHE_ENTRY_SIZE*MEGA;
2.22 frystyk 132:
133: PRIVATE int new_entries = 0; /* Number of new entries */
2.2 frystyk 134:
2.61 frystyk 135: PRIVATE HTNetBefore HTCacheFilter;
136: PRIVATE HTNetAfter HTCacheUpdateFilter;
137: PRIVATE HTNetAfter HTCacheCheckFilter;
2.53 frystyk 138:
2.1 frystyk 139: /* ------------------------------------------------------------------------- */
2.22 frystyk 140: /* CACHE GARBAGE COLLECTOR */
2.1 frystyk 141: /* ------------------------------------------------------------------------- */
142:
2.57 frystyk 143: PRIVATE BOOL stopGC (void)
144: {
145: return (HTCacheContentSize + HTCacheFolderSize < HTCacheTotalSize - HTCacheGCBuffer);
146: }
147:
148: PRIVATE BOOL startGC (void)
149: {
150: return (HTCacheContentSize + HTCacheFolderSize > HTCacheTotalSize);
151: }
152:
2.22 frystyk 153: PRIVATE BOOL HTCacheGarbage (void)
2.1 frystyk 154: {
2.56 frystyk 155: long old_size = HTCacheContentSize;
2.22 frystyk 156: if (CACHE_TRACE) HTTrace("Cache....... Garbage collecting\n");
157: if (CacheTable) {
158: time_t cur_time = time(NULL);
159: HTList * cur;
160: int cnt;
2.23 frystyk 161: int hits;
2.22 frystyk 162:
163: /*
2.51 frystyk 164: ** Tell the user that we're gc'ing.
2.22 frystyk 165: */
2.1 frystyk 166: {
2.51 frystyk 167: HTAlertCallback * cbf = HTAlert_find(HT_PROG_OTHER);
168: if (cbf) (*cbf)(NULL, HT_PROG_OTHER, HT_MSG_NULL,NULL, NULL, NULL);
2.22 frystyk 169: }
170:
171: /*
172: ** Walk through and delete all the expired entries. If this is not
173: ** sufficient then take the fresh ones which have the lowest cache
174: ** hit count. This algorithm could be made a lot fancier by including
175: ** the size and also the pain it took to get the document in the first
176: ** case. It could also include max_stale.
177: */
2.23 frystyk 178: if (CACHE_TRACE) HTTrace("Cache....... Collecting Stale entries\n");
2.62 frystyk 179: for (cnt=0; cnt<HT_XL_HASH_SIZE; cnt++) {
2.22 frystyk 180: if ((cur = CacheTable[cnt])) {
2.35 frystyk 181: HTList * old_cur = cur;
2.22 frystyk 182: HTCache * pres;
183: while ((pres = (HTCache *) HTList_nextObject(cur)) != NULL) {
184: time_t resident_time = cur_time - pres->response_time;
185: time_t current_age = pres->corrected_initial_age +
186: resident_time;
187: if (pres->freshness_lifetime < current_age) {
188: HTCache_remove(pres);
189: cur = old_cur;
190: } else {
191: old_cur = cur;
192: }
2.57 frystyk 193: if (stopGC()) break;
2.1 frystyk 194: }
195: }
196: }
2.23 frystyk 197:
198: /*
199: ** We must at least free the min buffer size so that we don't
200: ** dead lock ourselves. We start from the bottom up by taking
201: ** all the documents with 0 hits, 1 hits, 2 hits, etc.
202: */
2.57 frystyk 203: if (CACHE_TRACE) HTTrace("Cache....... Collecting least used entries\n");
2.23 frystyk 204: hits = 0;
2.57 frystyk 205: while (startGC()) {
2.23 frystyk 206: BOOL removed = NO;
207: if (CACHE_TRACE)
208: HTTrace("Cache....... Collecting entries with %d hits\n",hits);
2.62 frystyk 209: for (cnt=0; cnt<HT_XL_HASH_SIZE; cnt++) {
2.57 frystyk 210: if ((cur = CacheTable[cnt])) {
211: HTList * old_cur = cur;
212: HTCache * pres;
213: while ((pres = (HTCache *) HTList_nextObject(cur))) {
214: if (pres->size > HTCacheMaxEntrySize || pres->hits <= hits) {
215: HTCache_remove(pres);
216: cur = old_cur;
217: removed = YES;
218: } else {
219: old_cur = cur;
2.23 frystyk 220: }
2.57 frystyk 221: if (stopGC()) break;
2.23 frystyk 222: }
223: }
2.57 frystyk 224: }
2.23 frystyk 225: if (!removed) break;
226: hits++;
227: }
2.22 frystyk 228: if (CACHE_TRACE)
229: HTTrace("Cache....... Size reduced from %ld to %ld\n",
2.56 frystyk 230: old_size, HTCacheContentSize);
2.23 frystyk 231: /*
232: ** Dump the new content to the index file
233: */
234: HTCacheIndex_write(HTCacheRoot);
235: new_entries = 0;
2.22 frystyk 236: return YES;
2.1 frystyk 237: }
2.22 frystyk 238: return NO;
2.1 frystyk 239: }
240:
2.22 frystyk 241: /* ------------------------------------------------------------------------- */
242: /* CACHE INDEX */
243: /* ------------------------------------------------------------------------- */
2.1 frystyk 244:
2.22 frystyk 245: PRIVATE char * cache_index_name (const char * cache_root)
2.1 frystyk 246: {
2.22 frystyk 247: if (cache_root) {
248: char * location = NULL;
249: if ((location = (char *)
250: HT_MALLOC(strlen(cache_root) + strlen(HT_CACHE_INDEX) + 1)) == NULL)
251: HT_OUTOFMEM("cache_index_name");
252: strcpy(location, cache_root);
253: strcat(location, HT_CACHE_INDEX);
254: return location;
2.1 frystyk 255: }
2.22 frystyk 256: return NULL;
2.1 frystyk 257: }
258:
259: /*
2.22 frystyk 260: ** Remove the cache index file
2.1 frystyk 261: */
2.22 frystyk 262: PUBLIC BOOL HTCacheIndex_delete (const char * cache_root)
2.1 frystyk 263: {
2.22 frystyk 264: if (cache_root) {
265: char * index = cache_index_name(cache_root);
266: REMOVE(index);
267: HT_FREE(index);
2.1 frystyk 268: return YES;
2.22 frystyk 269: }
270: return NO;
271: }
2.1 frystyk 272:
2.22 frystyk 273: /*
274: ** Walk through the list of cached objects and save them to disk.
275: ** We override any existing version but that is normally OK as we have
276: ** already read its contents.
277: */
278: PUBLIC BOOL HTCacheIndex_write (const char * cache_root)
279: {
280: if (cache_root && CacheTable) {
281: char * index = cache_index_name(cache_root);
282: FILE * fp = NULL;
283: if (CACHE_TRACE) HTTrace("Cache Index. Writing index `%s\'\n", index);
284:
285: /*
286: ** Open the file for writing. Note - we don't take a backup!
287: ** This should probably be fixed!
288: */
289: if (!index) return NO;
290: if ((fp = fopen(index, "wb")) == NULL) {
291: if (CACHE_TRACE)
292: HTTrace("Cache Index. Can't open `%s\' for writing\n", index);
293: HT_FREE(index);
294: return NO;
295: }
2.1 frystyk 296:
2.22 frystyk 297: /*
298: ** Walk through the list and write it out. The format is really
2.25 frystyk 299: ** simple as we keep it all in ASCII.
2.22 frystyk 300: */
301: {
302: HTList * cur;
303: int cnt;
2.62 frystyk 304: for (cnt=0; cnt<HT_XL_HASH_SIZE; cnt++) {
2.22 frystyk 305: if ((cur = CacheTable[cnt])) {
306: HTCache * pres;
2.24 frystyk 307: while ((pres = (HTCache *) HTList_nextObject(cur))) {
2.34 frystyk 308: if (fprintf(fp, "%s %s %s %ld %ld %ld %c %d %d %ld %ld %ld %c\r\n",
2.24 frystyk 309: pres->url,
310: pres->cachename,
2.34 frystyk 311: pres->etag ? pres->etag : HT_CACHE_ETAG,
2.48 frystyk 312: (long) (pres->lm),
313: (long) (pres->expires),
2.24 frystyk 314: pres->size,
2.27 frystyk 315: pres->range+0x30,
2.24 frystyk 316: pres->hash,
317: pres->hits,
2.48 frystyk 318: (long) (pres->freshness_lifetime),
319: (long) (pres->response_time),
320: (long) (pres->corrected_initial_age),
2.22 frystyk 321: pres->must_revalidate+0x30) < 0) {
322: if (CACHE_TRACE)
323: HTTrace("Cache Index. Error writing cache index\n");
324: return NO;
325: }
326: }
327: }
328: }
329: }
2.1 frystyk 330:
2.22 frystyk 331: /* Done writing */
332: fclose(fp);
333: HT_FREE(index);
334: }
2.1 frystyk 335: return NO;
336: }
337:
338: /*
2.22 frystyk 339: ** Load one line of index file
340: ** Returns YES if line OK, else NO
2.2 frystyk 341: */
2.22 frystyk 342: PRIVATE BOOL HTCacheIndex_parseLine (char * line)
2.2 frystyk 343: {
2.22 frystyk 344: HTCache * cache = NULL;
345: if (line) {
346: char validate;
2.27 frystyk 347: char range;
2.22 frystyk 348: if ((cache = (HTCache *) HT_CALLOC(1, sizeof(HTCache))) == NULL)
349: HT_OUTOFMEM("HTCacheIndex_parseLine");
350:
351: /*
352: ** Read the line and create the cache object
353: */
354: {
355: char * url = HTNextField(&line);
356: char * cachename = HTNextField(&line);
2.34 frystyk 357: char * etag = HTNextField(&line);
2.22 frystyk 358: StrAllocCopy(cache->url, url);
359: StrAllocCopy(cache->cachename, cachename);
2.34 frystyk 360: if (strcmp(etag, HT_CACHE_ETAG)) StrAllocCopy(cache->etag, etag);
2.22 frystyk 361: }
2.48 frystyk 362: #ifdef HAVE_LONG_TIME_T
2.35 frystyk 363: /*
364: ** On some 64 bit machines (alpha) time_t is of type int and not long.
365: ** This means that we have to adjust sscanf accordingly so that we
366: ** know what we are looking for. Otherwise er may get unalignment
367: ** problems.
368: */
2.48 frystyk 369: if (sscanf(line, "%ld %ld %ld %c %d %d %ld %ld %ld %c",
2.35 frystyk 370: #else
2.48 frystyk 371: if (sscanf(line, "%d %d %ld %c %d %d %d %d %d %c",
2.35 frystyk 372: #endif
2.34 frystyk 373: &cache->lm,
2.24 frystyk 374: &cache->expires,
375: &cache->size,
2.28 frystyk 376: &range,
2.24 frystyk 377: &cache->hash,
378: &cache->hits,
2.22 frystyk 379: &cache->freshness_lifetime,
380: &cache->response_time,
381: &cache->corrected_initial_age,
382: &validate) < 0) {
383: if (CACHE_TRACE) HTTrace("Cache Index. Error reading cache index\n");
384: return NO;
385: }
2.27 frystyk 386: cache->range = range-0x30;
2.22 frystyk 387: cache->must_revalidate = validate-0x30;
388:
389: /*
2.26 frystyk 390: ** Create the new anchor and fill in the expire information we have read
391: ** in the index.
2.25 frystyk 392: */
393: if (cache) {
394: HTAnchor * anchor = HTAnchor_findAddress(cache->url);
395: HTParentAnchor * parent = HTAnchor_parent(anchor);
2.34 frystyk 396: HTAnchor_setExpires(parent, cache->expires);
397: HTAnchor_setLastModified(parent, cache->lm);
398: if (cache->etag) HTAnchor_setEtag(parent, cache->etag);
2.25 frystyk 399: }
400:
401: /*
2.22 frystyk 402: ** Create the cache table if not already existent and add the new
403: ** entry. Also check that the hash is still within bounds
404: */
405: if (!CacheTable) {
2.62 frystyk 406: if ((CacheTable = (HTList **) HT_CALLOC(HT_XL_HASH_SIZE,
2.22 frystyk 407: sizeof(HTList *))) == NULL)
408: HT_OUTOFMEM("HTCache_parseLine");
409: }
2.62 frystyk 410: if (cache->hash >= 0 && cache->hash < HT_XL_HASH_SIZE) {
2.22 frystyk 411: int hash = cache->hash;
412: if (!CacheTable[hash]) CacheTable[hash] = HTList_new();
413: HTList_addObject(CacheTable[hash], (void *) cache);
2.2 frystyk 414: }
2.22 frystyk 415:
416: /* Update the total cache size */
2.56 frystyk 417: HTCacheContentSize += cache->size;
2.22 frystyk 418:
419: return YES;
2.2 frystyk 420: }
2.22 frystyk 421: return NO;
2.2 frystyk 422: }
423:
424: /*
2.22 frystyk 425: ** Folding is either of CF LWS, LF LWS, CRLF LWS
2.2 frystyk 426: */
2.22 frystyk 427: PRIVATE int HTCacheIndex_put_block (HTStream * me, const char * b, int l)
2.2 frystyk 428: {
2.22 frystyk 429: while (l > 0) {
430: if (me->EOLstate == EOL_FCR) {
431: if (*b == LF) /* CRLF */
432: me->EOLstate = EOL_FLF;
2.46 frystyk 433: else if (isspace((int) *b)) /* Folding: CR SP */
2.22 frystyk 434: me->EOLstate = EOL_DOT;
435: else { /* New line */
436: HTCacheIndex_parseLine(HTChunk_data(me->buffer));
437: me->EOLstate = EOL_BEGIN;
438: HTChunk_clear(me->buffer);
439: continue;
440: }
441: } else if (me->EOLstate == EOL_FLF) {
2.46 frystyk 442: if (isspace((int) *b)) /* Folding: LF SP or CR LF SP */
2.22 frystyk 443: me->EOLstate = EOL_DOT;
444: else { /* New line */
445: HTCacheIndex_parseLine(HTChunk_data(me->buffer));
446: me->EOLstate = EOL_BEGIN;
447: HTChunk_clear(me->buffer);
448: continue;
449: }
450: } else if (me->EOLstate == EOL_DOT) {
2.46 frystyk 451: if (isspace((int) *b)) {
2.22 frystyk 452: me->EOLstate = EOL_BEGIN;
453: HTChunk_putc(me->buffer, ' ');
454: } else {
455: HTCacheIndex_parseLine(HTChunk_data(me->buffer));
456: me->EOLstate = EOL_BEGIN;
457: HTChunk_clear(me->buffer);
458: continue;
459: }
460: } else if (*b == CR) {
461: me->EOLstate = EOL_FCR;
462: } else if (*b == LF) {
463: me->EOLstate = EOL_FLF; /* Line found */
464: } else
465: HTChunk_putc(me->buffer, *b);
466: l--; b++;
2.2 frystyk 467: }
2.22 frystyk 468: return HT_OK;
2.2 frystyk 469: }
470:
2.22 frystyk 471: PRIVATE int HTCacheIndex_put_character (HTStream * me, char c)
472: {
473: return HTCacheIndex_put_block(me, &c, 1);
474: }
2.2 frystyk 475:
2.22 frystyk 476: PRIVATE int HTCacheIndex_put_string (HTStream * me, const char * s)
2.1 frystyk 477: {
2.22 frystyk 478: return HTCacheIndex_put_block(me, s, (int) strlen(s));
479: }
2.1 frystyk 480:
2.22 frystyk 481: PRIVATE int HTCacheIndex_flush (HTStream * me)
482: {
483: if (me) {
484: char * flush = HTChunk_data(me->buffer);
485: if (flush) HTCacheIndex_parseLine(flush);
486: HTChunk_clear(me->buffer);
487: }
488: return HT_OK;
489: }
2.1 frystyk 490:
2.22 frystyk 491: PRIVATE int HTCacheIndex_free (HTStream * me)
492: {
493: if (me) {
494: int status = HTCacheIndex_flush(me);
495: if (APP_TRACE) HTTrace("Cache Index. FREEING....\n");
496: HTChunk_delete(me->buffer);
497: HT_FREE(me);
498: return status;
499: }
500: return HT_ERROR;
501: }
2.1 frystyk 502:
2.22 frystyk 503: PRIVATE int HTCacheIndex_abort (HTStream * me, HTList * e)
504: {
505: if (me) {
506: int status = HT_ERROR;
507: if (APP_TRACE) HTTrace("Cache Index. ABORTING...\n");
508: HTChunk_delete(me->buffer);
509: HT_FREE(me);
510: return status;
2.1 frystyk 511: }
2.22 frystyk 512: return HT_ERROR;
513: }
2.1 frystyk 514:
2.22 frystyk 515: /* Structured Object Class
516: ** -----------------------
517: */
518: PRIVATE const HTStreamClass HTCacheIndexClass =
519: {
520: "CacheIndexParser",
521: HTCacheIndex_flush,
522: HTCacheIndex_free,
523: HTCacheIndex_abort,
524: HTCacheIndex_put_character,
525: HTCacheIndex_put_string,
526: HTCacheIndex_put_block
527: };
2.1 frystyk 528:
2.22 frystyk 529: PRIVATE HTStream * HTCacheIndexReader (HTRequest * request)
530: {
531: HTStream * me;
532: if ((me = (HTStream *) HT_CALLOC(1, sizeof(HTStream))) == NULL)
533: HT_OUTOFMEM("HTCacheIndexs");
534: me->isa = &HTCacheIndexClass;
535: me->request = request;
536: me->buffer = HTChunk_new(512);
537: me->EOLstate = EOL_BEGIN;
538: return me;
539: }
540:
541: /*
542: ** Read the saved set of cached entries from disk. we only allow the index
543: ** ro be read when there is no entries in memory. That way we can ensure
544: ** consistancy.
545: */
546: PUBLIC BOOL HTCacheIndex_read (const char * cache_root)
547: {
548: BOOL status = NO;
549: if (cache_root && CacheTable == NULL) {
2.33 eric 550: BOOL wasInteractive;
2.22 frystyk 551: char * file = cache_index_name(cache_root);
2.61 frystyk 552: char * index = HTLocalToWWW(file, "cache:");
2.34 frystyk 553: HTAnchor * anchor = HTAnchor_findAddress(index);
2.22 frystyk 554: HTRequest * request = HTRequest_new();
555: HTRequest_setPreemptive(request, YES);
556: HTRequest_setOutputFormat(request, WWW_SOURCE);
2.58 frystyk 557:
558: /* Make sure we don't use any filters */
559: HTRequest_addBefore(request, NULL, NULL, NULL, 0, YES);
560: HTRequest_addAfter(request, NULL, NULL, NULL, HT_ALL, 0, YES);
561:
562: /* Set the output */
2.22 frystyk 563: HTRequest_setOutputStream(request, HTCacheIndexReader(request));
2.23 frystyk 564: HTRequest_setAnchor(request, anchor);
2.34 frystyk 565: HTAnchor_setFormat((HTParentAnchor *) anchor, HTAtom_for("www/cache-index"));
2.33 eric 566: wasInteractive = HTAlert_interactive();
2.23 frystyk 567: HTAlert_setInteractive(NO);
568: status = HTLoad(request, NO);
2.33 eric 569: HTAlert_setInteractive(wasInteractive);
2.22 frystyk 570: HTRequest_delete(request);
571: HT_FREE(file);
572: HT_FREE(index);
2.1 frystyk 573: }
2.22 frystyk 574: return status;
2.1 frystyk 575: }
576:
2.22 frystyk 577: /* ------------------------------------------------------------------------- */
578: /* CACHE PARAMETERS */
579: /* ------------------------------------------------------------------------- */
2.1 frystyk 580:
2.22 frystyk 581: PRIVATE BOOL create_cache_root (const char * cache_root)
2.1 frystyk 582: {
583: struct stat stat_info;
2.22 frystyk 584: char * loc = NULL;
2.1 frystyk 585: char * cur = NULL;
586: BOOL create = NO;
2.22 frystyk 587: if (!cache_root) return NO;
588: StrAllocCopy(loc, cache_root); /* Get our own copy */
2.61 frystyk 589: #ifdef WWW_MSWINDOWS
590: cur = *(loc+1) == ':' ? loc+3 : loc+1;
591: #else
2.22 frystyk 592: cur = loc+1;
2.61 frystyk 593: #endif
594: while ((cur = strchr(cur, DIR_SEPARATOR_CHAR))) {
2.22 frystyk 595: *cur = '\0';
596: if (create || HT_STAT(loc, &stat_info) == -1) {
597: create = YES; /* To avoid doing stat()s in vain */
598: if (CACHE_TRACE) HTTrace("Cache....... Creating dir `%s\'\n", loc);
599: if (MKDIR(loc, 0777) < 0) {
600: if (CACHE_TRACE) HTTrace("Cache....... can't create\n");
601: HT_FREE(loc);
2.1 frystyk 602: return NO;
603: }
604: } else {
2.22 frystyk 605: if (CACHE_TRACE)
606: HTTrace("Cache....... dir `%s\' already exists\n", loc);
2.1 frystyk 607: }
2.61 frystyk 608: *cur++ = DIR_SEPARATOR_CHAR;
2.1 frystyk 609: }
2.22 frystyk 610: HT_FREE(loc);
2.1 frystyk 611: return YES;
612: }
613:
2.22 frystyk 614: /*
615: ** If `cache_root' is NULL then the current value (might be a define)
616: ** Should we check if the cache_root is actually OK? I think not!
2.1 frystyk 617: */
2.22 frystyk 618: PRIVATE BOOL HTCacheMode_setRoot (const char * cache_root)
2.1 frystyk 619: {
2.43 frystyk 620: if (cache_root) {
2.61 frystyk 621: if ((HTCacheRoot = HTWWWToLocal(cache_root, "file:", NULL)) == NULL)
622: return NO;
623: if (*(HTCacheRoot+strlen(HTCacheRoot)-1) != DIR_SEPARATOR_CHAR)
624: StrAllocCat(HTCacheRoot, DIR_SEPARATOR_STR);
2.43 frystyk 625: } else {
626: /*
627: ** If no cache root has been indicated then look for a suitable
628: ** location.
629: */
2.61 frystyk 630: char * addr = NULL;
631: char * cr = (char *) getenv("WWW_CACHE");
2.43 frystyk 632: if (!cr) cr = (char *) getenv("TMP");
633: if (!cr) cr = (char *) getenv("TEMP");
634: if (!cr) cr = HT_CACHE_LOC;
2.61 frystyk 635: addr = HTLocalToWWW(cr, NULL);
636: if (*(addr+strlen(addr)-1) != DIR_SEPARATOR_CHAR)
637: StrAllocCat(addr, DIR_SEPARATOR_STR);
638: StrAllocCat(addr, HT_CACHE_ROOT);
639: if (*(addr+strlen(addr)-1) != DIR_SEPARATOR_CHAR)
640: StrAllocCat(addr, DIR_SEPARATOR_STR);
641: if ((HTCacheRoot = HTWWWToLocal(addr, "file:", NULL)) == NULL) {
642: HT_FREE(addr);
643: return NO;
644: }
645: HT_FREE(addr);
2.43 frystyk 646: }
2.22 frystyk 647: if (create_cache_root(HTCacheRoot) == NO) return NO;
2.61 frystyk 648: if (CACHE_TRACE) HTTrace("Cache Root.. Local root set to `%s\'\n", HTCacheRoot);
2.22 frystyk 649: return YES;
2.1 frystyk 650: }
651:
652: /*
2.22 frystyk 653: ** Return the value of the cache root. The cache root can only be
654: ** set through the HTCacheInit() function
655: */
2.61 frystyk 656: PUBLIC char * HTCacheMode_getRoot (void)
2.22 frystyk 657: {
2.61 frystyk 658: return HTLocalToWWW(HTCacheRoot, NULL);
2.1 frystyk 659: }
660:
2.22 frystyk 661: /*
2.48 frystyk 662: ** As this is a single user cache, we have to lock it when in use.
663: */
2.52 frystyk 664: PRIVATE FILE *locked_open_file = {NULL};
665:
2.48 frystyk 666: PRIVATE BOOL HTCache_getSingleUserLock (const char * root)
667: {
2.52 frystyk 668: if (root && !locked_open_file) {
2.48 frystyk 669: FILE * fp;
670: char * location = NULL;
671: if ((location = (char *)
672: HT_MALLOC(strlen(root) + strlen(HT_CACHE_LOCK) + 1)) == NULL)
673: HT_OUTOFMEM("HTCache_getLock");
674: strcpy(location, root);
675: strcat(location, HT_CACHE_LOCK);
676: if ((fp = fopen(location, "r")) != NULL) {
677: HTAlertCallback *cbf = HTAlert_find(HT_A_CONFIRM);
2.49 frystyk 678: HTTrace("Cache....... In `%s\' is already in use\n", root);
2.48 frystyk 679: fclose(fp);
2.54 frystyk 680: if (cbf) {
681: BOOL result = (*cbf)(NULL, HT_A_CONFIRM,
682: HT_MSG_CACHE_LOCK,NULL,location,NULL);
683: if (result == YES) {
684: REMOVE(location);
685: } else {
686: HT_FREE(location);
687: return NO;
688: }
689: } else {
690: HT_FREE(location);
691: return NO;
692: }
2.48 frystyk 693: }
694: if ((fp = fopen(location, "w")) == NULL) {
695: HTTrace("Cache....... Can't open `%s\' for writing\n", location);
696: HT_FREE(location);
697: return NO;
698: }
2.52 frystyk 699: locked_open_file = fp;
2.48 frystyk 700: HT_FREE(location);
701: return YES;
702: }
703: return NO;
704: }
705:
706: /*
707: ** Release the single user lock
708: */
709: PRIVATE BOOL HTCache_deleteSingleUserLock (const char * root)
710: {
711: if (root) {
712: char * location = NULL;
713: if ((location = (char *)
714: HT_MALLOC(strlen(root) + strlen(HT_CACHE_LOCK) + 1)) == NULL)
715: HT_OUTOFMEM("HTCache_deleteLock");
716: strcpy(location, root);
717: strcat(location, HT_CACHE_LOCK);
2.52 frystyk 718: /* under UNIX you can remove an open file, not so under NT */
719: if (locked_open_file) {
720: fclose(locked_open_file);
721: locked_open_file = NULL;
722: }
2.48 frystyk 723: REMOVE(location);
724: HT_FREE(location);
725: return YES;
726: }
727: return NO;
728: }
729:
730: /*
2.1 frystyk 731: ** If `cache_root' is NULL then reuse old value or use HT_CACHE_ROOT.
732: ** An empty string will make '/' as cache root
2.12 frystyk 733: ** We can only enable the cache if the HTSecure flag is not set. This
734: ** is for example the case if using an application as a telnet shell.
2.1 frystyk 735: */
2.22 frystyk 736: PUBLIC BOOL HTCacheInit (const char * cache_root, int size)
2.1 frystyk 737: {
2.22 frystyk 738: if (!HTLib_secure() && !HTCacheRoot) {
739:
740: /*
741: ** Find an appropriate root for the cache
742: */
743: if (HTCacheMode_setRoot(cache_root) != YES) return NO;
744:
745: /*
746: ** Set the max size of the cache
747: */
748: HTCacheMode_setMaxSize(size);
749:
750: /*
2.48 frystyk 751: ** Set a lock on the cache so that multiple users
752: ** don't step on each other.
753: */
754: if (HTCache_getSingleUserLock(HTCacheRoot) == NO)
755: return NO;
756:
757: /*
2.22 frystyk 758: ** Look for the cache index and read the contents
759: */
760: HTCacheIndex_read(HTCacheRoot);
761:
762: /*
2.60 frystyk 763: ** Register the cache before and after filters
764: */
765: HTNet_addBefore(HTCacheFilter, "http://*", NULL, HT_FILTER_MIDDLE);
766: HTNet_addAfter(HTCacheUpdateFilter, "http://*", NULL,
767: HT_NOT_MODIFIED, HT_FILTER_MIDDLE);
768:
769: /*
2.54 frystyk 770: ** Register the cache AFTER filter for checking whether
771: ** we should invalidate the cached entry
2.53 frystyk 772: */
2.54 frystyk 773: HTNet_addAfter(HTCacheCheckFilter, "http://*", NULL, HT_ALL,
2.53 frystyk 774: HT_FILTER_MIDDLE);
775:
776: /*
2.22 frystyk 777: ** Do caching from now on
778: */
2.12 frystyk 779: HTCacheEnable = YES;
2.48 frystyk 780: HTCacheInitialized = YES;
2.12 frystyk 781: return YES;
782: }
783: return NO;
2.1 frystyk 784: }
785:
2.22 frystyk 786: /*
787: ** Turns off the cache and updates entries on disk.
2.1 frystyk 788: */
2.22 frystyk 789: PUBLIC BOOL HTCacheTerminate (void)
2.1 frystyk 790: {
2.48 frystyk 791: if (HTCacheInitialized) {
792:
793: /*
794: ** Write the index to file
795: */
796: HTCacheIndex_write(HTCacheRoot);
797:
798: /*
2.60 frystyk 799: ** Unregister the cache before and after filters
800: */
801: HTNet_deleteBefore(HTCacheFilter);
802: HTNet_deleteAfter(HTCacheUpdateFilter);
803: HTNet_deleteAfter(HTCacheCheckFilter);
804:
805: /*
2.48 frystyk 806: ** Set a lock on the cache so that multiple users
807: ** don't step on each other.
808: */
809: HTCache_deleteSingleUserLock(HTCacheRoot);
810:
811: /*
812: ** Cleanup memory by deleting all HTCache objects
813: */
814: HTCache_deleteAll();
815:
816: /*
817: ** Don't do anymore caching from now on
818: */
819: HT_FREE(HTCacheRoot);
820: HTCacheEnable = NO;
821: return YES;
822: }
823: return NO;
2.1 frystyk 824: }
825:
2.22 frystyk 826: /*
827: ** The cache can be temporarily suspended by using the enable/disable
828: ** flag. This does not prevent the cache from being enabled/disable at
829: ** a later point in time.
2.1 frystyk 830: */
2.22 frystyk 831: PUBLIC void HTCacheMode_setEnabled (BOOL mode)
832: {
833: HTCacheEnable = mode;
834: }
835:
836: PUBLIC BOOL HTCacheMode_enabled (void)
2.1 frystyk 837: {
2.12 frystyk 838: return HTCacheEnable;
2.1 frystyk 839: }
840:
2.63 ! kahan 841: PUBLIC void HTCacheMode_setProtected (BOOL mode)
! 842: {
! 843: HTCacheProtected = mode;
! 844: }
! 845:
! 846: PUBLIC BOOL HTCacheMode_protected (void)
! 847: {
! 848: return HTCacheProtected;
! 849: }
! 850:
2.22 frystyk 851: /*
852: ** We can set the cache to operate in disconnected mode in which we only
853: ** return (valid) responses from the cache. Disconnected mode does not
854: ** automatically deliver stale documents as this must be declared
855: ** explicitly.
2.1 frystyk 856: */
2.22 frystyk 857: PUBLIC void HTCacheMode_setDisconnected (HTDisconnectedMode mode)
2.1 frystyk 858: {
2.22 frystyk 859: DisconnectedMode = mode;
2.1 frystyk 860: }
861:
2.22 frystyk 862: PUBLIC HTDisconnectedMode HTCacheMode_disconnected (void)
2.1 frystyk 863: {
2.22 frystyk 864: return DisconnectedMode;
2.1 frystyk 865: }
866:
2.22 frystyk 867: PUBLIC BOOL HTCacheMode_isDisconnected (HTReload mode)
2.1 frystyk 868: {
2.22 frystyk 869: return (DisconnectedMode != HT_DISCONNECT_NONE);
2.1 frystyk 870: }
871:
2.7 frystyk 872: /*
873: ** Set the mode for how we handle Expires header from the local history
874: ** list. The following modes are available:
875: **
876: ** HT_EXPIRES_IGNORE : No update in the history list
877: ** HT_EXPIRES_NOTIFY : The user is notified but no reload
878: ** HT_EXPIRES_AUTO : Automatic reload
879: */
2.22 frystyk 880: PUBLIC void HTCacheMode_setExpires (HTExpiresMode mode)
2.7 frystyk 881: {
882: HTExpMode = mode;
883: }
884:
2.22 frystyk 885: PUBLIC HTExpiresMode HTCacheMode_expires (void)
2.7 frystyk 886: {
887: return HTExpMode;
888: }
889:
2.22 frystyk 890: /*
891: ** Cache size management. We set the default cache size to 20M.
892: ** We set the minimum size to 5M in order not to get into weird
893: ** problems while writing the cache. The size is indicated in Mega
894: ** bytes
895: */
896: PUBLIC BOOL HTCacheMode_setMaxSize (int size)
897: {
2.56 frystyk 898: long new_size = size < HT_MIN_CACHE_TOTAL_SIZE ?
899: HT_MIN_CACHE_TOTAL_SIZE*MEGA : size*MEGA;
900: long old_size = HTCacheTotalSize;
901: HTCacheTotalSize = new_size;
902: HTCacheFolderSize = HTCacheTotalSize/HT_CACHE_FOLDER_PCT;
2.57 frystyk 903: HTCacheGCBuffer = HTCacheTotalSize/HT_CACHE_GC_PCT;
2.56 frystyk 904: if (new_size < old_size) HTCacheGarbage();
905: if (CACHE_TRACE)
2.57 frystyk 906: HTTrace("Cache....... Total cache size: %ld with %ld bytes for metainformation and folders and at least %ld bytes free after every gc\n",
907: HTCacheTotalSize, HTCacheFolderSize, HTCacheGCBuffer);
2.22 frystyk 908: return YES;
909: }
910:
911: PUBLIC int HTCacheMode_maxSize (void)
912: {
2.56 frystyk 913: return HTCacheTotalSize / MEGA;
914: }
915:
916: /*
917: ** How big can a single cached entry be in Mbytes. The default is 3M
918: **
919: */
920: PUBLIC BOOL HTCacheMode_setMaxCacheEntrySize (int size)
921: {
922: long new_size = size*MEGA;
923: if (new_size > 0 && new_size < HTCacheTotalSize-HTCacheFolderSize) {
924: long old_size = HTCacheMaxEntrySize;
925: HTCacheMaxEntrySize = new_size;
926: if (new_size < old_size) HTCacheGarbage();
927: if (CACHE_TRACE)
928: HTTrace("Cache...... Max entry cache size is %ld\n", HTCacheMaxEntrySize);
929: return YES;
930: }
931: if (CACHE_TRACE) HTTrace("Cache...... Max entry cache size is unchanged\n");
932: return NO;
933: }
934:
935: PUBLIC int HTCacheMode_maxCacheEntrySize (void)
936: {
937: return HTCacheMaxEntrySize / MEGA;
2.22 frystyk 938: }
939:
2.7 frystyk 940: /* ------------------------------------------------------------------------- */
2.22 frystyk 941: /* CACHE OBJECT */
2.2 frystyk 942: /* ------------------------------------------------------------------------- */
943:
2.22 frystyk 944: PRIVATE BOOL free_object (HTCache * me)
945: {
946: HT_FREE(me->url);
947: HT_FREE(me->cachename);
2.34 frystyk 948: HT_FREE(me->etag);
2.22 frystyk 949: HT_FREE(me);
950: return YES;
951: }
952:
953: PRIVATE BOOL delete_object (HTList * list, HTCache * me)
954: {
955: if (CACHE_TRACE) HTTrace("Cache....... delete %p from list %p\n",me, list);
956: HTList_removeObject(list, (void *) me);
2.56 frystyk 957: HTCacheContentSize -= me->size;
2.22 frystyk 958: free_object(me);
959: return YES;
960: }
961:
962: /*
963: ** Create directory path for cache file
964: **
965: ** On exit:
966: ** return YES
967: ** if directories created -- after that caller
968: ** can rely on fopen(cfn,"w") succeeding.
969: **
970: */
971: PRIVATE BOOL HTCache_createLocation (HTCache * me)
972: {
973: if (me && HTCacheRoot) {
974: BOOL status = YES;
975: char * path = NULL;
976: struct stat stat_info;
977: if ((path = (char *) HT_MALLOC(strlen(HTCacheRoot) + 10)) == NULL)
978: HT_OUTOFMEM("HTCache_createLocation");
2.43 frystyk 979:
980: /*
981: ** Find the path and check whether the directory already exists or not
982: */
2.22 frystyk 983: sprintf(path, "%s%d", HTCacheRoot, me->hash);
984: if (HT_STAT(path, &stat_info) == -1) {
985: if (CACHE_TRACE) HTTrace("Cache....... Create dir `%s\'\n", path);
986: if (MKDIR(path, 0777) < 0) {
987: if (CACHE_TRACE) HTTrace("Cache....... Can't create...\n");
988: status = NO;
989: }
990: } else {
991: if (CACHE_TRACE)
992: HTTrace("Cache....... Directory `%s\' already exists\n", path);
993: }
2.43 frystyk 994:
995: /*
996: ** Find a non-existent filename within the path that we just created
997: */
998: me->cachename = HTGetTmpFileName(path);
2.22 frystyk 999: HT_FREE(path);
1000: return status;
1001: }
1002: return NO;
1003: }
1004:
1005: /*
1006: ** Find a cache filename for this cache object.
1007: */
2.43 frystyk 1008: #if 0
2.22 frystyk 1009: PRIVATE BOOL HTCache_findName (HTCache * me)
1010: {
1011: if (me) {
1012: /*
1013: ** Create path for this cache entry. We base the cache location on the
1014: ** hash calculated as a function of the URL. That way, we ensure a
1015: ** resonably uniform distribution.
1016: */
1017: me->cachename = HTGetTmpFileName(NULL);
1018: return HTCache_createLocation(me);
1019: }
1020: return NO;
1021: }
2.43 frystyk 1022: #endif
2.22 frystyk 1023:
1024: /*
2.24 frystyk 1025: ** Calculate the corrected_initial_age of the object. We use the time
1026: ** when this function is called as the response_time as this is when
1027: ** we have received the complete response. This may cause a delay if
2.26 frystyk 1028: ** the reponse header is very big but should not cause any incorrect
2.24 frystyk 1029: ** behavior.
1030: */
2.26 frystyk 1031: PRIVATE BOOL calculate_time (HTCache * me, HTRequest * request,
1032: HTResponse * response)
2.24 frystyk 1033: {
1034: if (me && request) {
1035: HTParentAnchor * anchor = HTRequest_anchor(request);
2.26 frystyk 1036: time_t date = HTAnchor_date(anchor);
2.24 frystyk 1037: me->response_time = time(NULL);
1038: me->expires = HTAnchor_expires(anchor);
1039: {
2.26 frystyk 1040: time_t apparent_age = HTMAX(0, me->response_time - date);
2.24 frystyk 1041: time_t corrected_received_age = HTMAX(apparent_age, HTAnchor_age(anchor));
1042: time_t response_delay = me->response_time - HTRequest_date(request);
1043: me->corrected_initial_age = corrected_received_age + response_delay;
1044: }
1045:
1046: /*
1047: ** Estimate an expires time using the max-age and expires time. If we
1048: ** don't have an explicit expires time then set it to 10% of the LM
2.31 frystyk 1049: ** date (although max 24 h). If no LM date is available then use 24 hours.
2.24 frystyk 1050: */
1051: {
2.26 frystyk 1052: time_t freshness_lifetime = HTResponse_maxAge(response);
2.24 frystyk 1053: if (freshness_lifetime < 0) {
1054: if (me->expires < 0) {
1055: time_t lm = HTAnchor_lastModified(anchor);
2.31 frystyk 1056: if (lm < 0) {
1057: freshness_lifetime = DefaultExpiration;
1058: } else {
1059: freshness_lifetime = LM_EXPIRATION(date - lm);
1060: if (freshness_lifetime > WARN_HEURISTICS)
1061: HTRequest_addError(request, ERR_WARN, NO,
1062: HTERR_HEURISTIC_EXPIRATION, NULL, 0,
1063: "calculate_time");
1064: }
2.24 frystyk 1065: } else
2.26 frystyk 1066: freshness_lifetime = me->expires - date;
2.24 frystyk 1067: }
1068: me->freshness_lifetime = HTMAX(0, freshness_lifetime);
1069: }
1070: if (CACHE_TRACE) {
1071: HTTrace("Cache....... Received Age %d, corrected %d, freshness lifetime %d\n",
1072: HTAnchor_age(anchor),
1073: me->corrected_initial_age,
1074: me->freshness_lifetime);
1075: }
1076: return YES;
1077: }
1078: return NO;
1079: }
1080:
1081: /*
2.22 frystyk 1082: ** Create a new cache entry and add it to the list
1083: */
2.26 frystyk 1084: PRIVATE HTCache * HTCache_new (HTRequest * request, HTResponse * response,
1085: HTParentAnchor * anchor)
2.22 frystyk 1086: {
1087: HTList * list = NULL; /* Current list in cache */
1088: HTCache * pres = NULL;
1089: int hash = 0;
1090: char * url = NULL;
2.26 frystyk 1091: if (!request || !response || !anchor) {
2.22 frystyk 1092: if (CORE_TRACE) HTTrace("Cache....... Bad argument\n");
1093: return NULL;
1094: }
1095:
1096: /* Find a hash for this anchor */
1097: if ((url = HTAnchor_address((HTAnchor *) anchor))) {
1098: char * ptr;
1099: for (ptr=url; *ptr; ptr++)
2.62 frystyk 1100: hash = (int) ((hash * 3 + (*(unsigned char *) ptr)) % HT_XL_HASH_SIZE);
2.22 frystyk 1101: if (!CacheTable) {
2.62 frystyk 1102: if ((CacheTable = (HTList **) HT_CALLOC(HT_XL_HASH_SIZE,
2.22 frystyk 1103: sizeof(HTList *))) == NULL)
1104: HT_OUTOFMEM("HTCache_new");
1105: }
1106: if (!CacheTable[hash]) CacheTable[hash] = HTList_new();
1107: list = CacheTable[hash];
1108: } else
1109: return NULL;
1110:
1111: /* Search the cache */
1112: {
1113: HTList * cur = list;
1114: while ((pres = (HTCache *) HTList_nextObject(cur))) {
1115: if (!strcmp(pres->url, url)) break;
1116: }
1117: }
1118:
1119: /* If not found then create new cache object, else use existing one */
1120: if (!pres) {
1121: if ((pres = (HTCache *) HT_CALLOC(1, sizeof(HTCache))) == NULL)
1122: HT_OUTOFMEM("HTCache_new");
1123: pres->hash = hash;
1124: pres->url = url;
2.27 frystyk 1125: pres->range = NO;
2.43 frystyk 1126: HTCache_createLocation(pres);
2.22 frystyk 1127: HTList_addObject(list, (void *) pres);
1128: new_entries++;
2.24 frystyk 1129: } else
1130: HT_FREE(url);
2.22 frystyk 1131:
1132: if (HTCache_hasLock(pres)) {
2.39 frystyk 1133: if (HTCache_breakLock(pres, request) == NO) {
1134: if (CACHE_TRACE) HTTrace("Cache....... Entry %p already in use\n");
1135: return pres;
1136: }
2.22 frystyk 1137: }
2.39 frystyk 1138: HTCache_getLock(pres, request);
2.22 frystyk 1139:
2.39 frystyk 1140:
2.24 frystyk 1141: /* Calculate the various times */
2.26 frystyk 1142: calculate_time(pres, request, response);
2.24 frystyk 1143:
2.34 frystyk 1144: /* Get the last-modified and etag values if any */
1145: {
1146: char * etag = HTAnchor_etag(anchor);
1147: if (etag) StrAllocCopy(pres->etag, etag);
1148: pres->lm = HTAnchor_lastModified(anchor);
1149: }
1150:
2.26 frystyk 1151: /* Must we revalidate this every time? */
1152: pres->must_revalidate = HTResponse_mustRevalidate(response);
2.22 frystyk 1153: return pres;
2.53 frystyk 1154: }
1155:
1156: /*
1157: ** Add an entry for a resource that has just been created so that we can
1158: ** remember the etag and other things. This allows us to guarantee that
1159: ** we don't loose data due to the lost update problem
1160: */
2.54 frystyk 1161: PUBLIC HTCache * HTCache_touch (HTRequest * request, HTResponse * response,
1162: HTParentAnchor * anchor)
2.53 frystyk 1163: {
1164: HTCache * cache = NULL;
1165:
1166: /* Get a new cache entry */
1167: if ((cache = HTCache_new(request, response, anchor)) == NULL) {
1168: if (CACHE_TRACE) HTTrace("Cache....... Can't get a cache object\n");
1169: return NULL;
1170: }
1171:
1172: /* We don't have any of the data in cache - only meta information */
1173: if (cache) {
1174: cache->size = 0;
1175: cache->range = YES;
1176: }
1177:
1178: return cache;
1179: }
1180:
1181: /*
2.61 frystyk 1182: ** Cache Validation BEFORE Filter
1183: ** ------------------------------
1184: ** Check the cache mode to see if we can use an already loaded version
1185: ** of this document. If so and our copy is valid then we don't have
1186: ** to go out and get it unless we are forced to
1187: ** We only check the cache in caseof a GET request. Otherwise, we go
1188: ** directly to the source.
1189: */
1190: PRIVATE int HTCacheFilter (HTRequest * request, void * param, int mode)
1191: {
1192: HTParentAnchor * anchor = HTRequest_anchor(request);
1193: HTCache * cache = NULL;
1194: HTReload reload = HTRequest_reloadMode(request);
1195: HTMethod method = HTRequest_method(request);
1196: HTDisconnectedMode disconnect = HTCacheMode_disconnected();
1197: BOOL validate = NO;
1198:
1199: /*
1200: ** If the cache is disabled all together then it won't help looking, huh?
1201: */
1202: if (!HTCacheMode_enabled()) return HT_OK;
1203: if (CACHE_TRACE) HTTrace("Cachefilter. Checking persistent cache\n");
1204:
1205: /*
1206: ** Now check the cache...
1207: */
1208: if (method != METHOD_GET) {
1209: if (CACHE_TRACE) HTTrace("Cachefilter. We only check GET methods\n");
1210: } else if (reload == HT_CACHE_FLUSH) {
1211: /*
1212: ** If the mode if "Force Reload" then don't even bother to check the
1213: ** cache - we flush everything we know abut this document anyway.
1214: ** Add the appropriate request headers. We use both the "pragma"
1215: ** and the "cache-control" headers in order to be
1216: ** backwards compatible with HTTP/1.0
1217: */
1218: validate = YES;
1219: HTRequest_addGnHd(request, HT_G_PRAGMA_NO_CACHE);
1220: HTRequest_addCacheControl(request, "no-cache", "");
1221:
1222: /*
1223: ** We also flush the information in the anchor as we don't want to
1224: ** inherit any "old" values
1225: */
1226: HTAnchor_clearHeader(anchor);
1227:
1228: } else {
1229: /*
1230: ** Check the persistent cache manager. If we have a cache hit then
1231: ** continue to see if the reload mode requires us to do a validation
1232: ** check. This filter assumes that we can get the cached version
1233: ** through one of our protocol modules (for example the file module)
1234: */
1235: cache = HTCache_find(anchor);
1236: if (cache) {
1237: HTReload cache_mode = HTCache_isFresh(cache, request);
1238: if (cache_mode == HT_CACHE_ERROR) cache = NULL;
1239: reload = HTMAX(reload, cache_mode);
1240: HTRequest_setReloadMode(request, reload);
1241:
1242: /*
1243: ** Now check the mode and add the right headers for the validation
1244: ** If we are to validate a cache entry then we get a lock
1245: ** on it so that not other requests can steal it.
1246: */
1247: if (reload == HT_CACHE_RANGE_VALIDATE) {
1248: /*
1249: ** If we were asked to range validate the cached object then
1250: ** use the etag or the last modified for cache validation
1251: */
1252: validate = YES;
1253: HTCache_getLock(cache, request);
1254: HTRequest_addRqHd(request, HT_C_IF_RANGE);
1255: } else if (reload == HT_CACHE_END_VALIDATE) {
1256: /*
1257: ** If we were asked to end-to-end validate the cached object
1258: ** then use a max-age=0 cache control directive
1259: */
1260: validate = YES;
1261: HTCache_getLock(cache, request);
1262: HTRequest_addCacheControl(request, "max-age", "0");
1263: } else if (reload == HT_CACHE_VALIDATE) {
1264: /*
1265: ** If we were asked to validate the cached object then
1266: ** use the etag or the last modified for cache validation
1267: ** We use both If-None-Match or If-Modified-Since.
1268: */
1269: validate = YES;
1270: HTCache_getLock(cache, request);
1271: HTRequest_addRqHd(request, HT_C_IF_NONE_MATCH | HT_C_IMS);
1272: } else if (cache) {
1273: /*
1274: ** The entity does not require any validation at all. We
1275: ** can just go ahead and get it from the cache. In case we
1276: ** have a fresh subpart of the entity, then we issue a
1277: ** conditional GET request with the range set by the cache
1278: ** manager. Issuing the conditional range request is
1279: ** equivalent to a validation as we have to go out on the
1280: ** net. This may have an effect if running in disconnected
1281: ** mode. We disable all BEFORE filters as they don't make
1282: ** sense while loading the cache entry.
1283: */
1284: {
1285: char * name = HTCache_name(cache);
1286: HTAnchor_setPhysical(anchor, name);
1287: HTCache_addHit(cache);
1288: HT_FREE(name);
1289: }
1290: }
1291: }
1292: }
1293:
1294: /*
1295: ** If we are in disconnected mode and we are to validate an entry
1296: ** then check whether what mode of disconnected mode we're in. If
1297: ** we are to use our own cache then return a "504 Gateway Timeout"
1298: */
1299: if ((!cache || validate) && disconnect != HT_DISCONNECT_NONE) {
1300: if (disconnect == HT_DISCONNECT_EXTERNAL)
1301: HTRequest_addCacheControl(request, "only-if-cached", "");
1302: else {
1303: HTRequest_addError(request, ERR_FATAL, NO,
1304: HTERR_GATE_TIMEOUT, "Disconnected Cache Mode",
1305: 0, "HTCacheFilter");
1306: return HT_ERROR;
1307: }
1308: }
1309: return HT_OK;
1310: }
1311:
1312: /*
1313: ** Cache Update AFTER filter
1314: ** -------------------------
1315: ** On our way out we catch the metainformation and stores it in
1316: ** our persistent store. If we have a cache validation (a 304
1317: ** response then we use the new metainformation and merges it with
1318: ** the existing information already captured in the cache.
1319: */
1320: PRIVATE int HTCacheUpdateFilter (HTRequest * request, HTResponse * response,
1321: void * param, int status)
1322: {
1323: HTParentAnchor * anchor = HTRequest_anchor(request);
1324: HTCache * cache = HTCache_find(anchor);
1325: if (cache) {
1326:
1327: /*
1328: ** It may in fact be that the information in the 304 response
1329: ** told us that we can't cache the entity anymore. If this is the
1330: ** case then flush it now. Otherwise prepare for a cache read
1331: */
1332: if (CACHE_TRACE) HTTrace("Cache....... Merging metainformation\n");
1333: if (HTResponse_isCachable(response) == HT_NO_CACHE) {
1334: HTCache_remove(cache);
1335: } else {
1336: char * name = HTCache_name(cache);
1337: HTAnchor_setPhysical(anchor, name);
1338: HTCache_addHit(cache);
1339: HT_FREE(name);
1340: HTCache_updateMeta(cache, request, response);
1341: }
1342:
1343: /*
1344: ** Start request directly from the cache. As with the redirection filter
1345: ** we reuse the same request object which means that we must
1346: ** keep this around until the cache load request has terminated
1347: ** In the case of a
1348: */
1349: HTLoad(request, YES);
1350: return HT_ERROR;
1351: } else {
1352:
1353: /* If entry doesn't already exist then create a new entry */
1354: HTCache_touch(request, response, anchor);
1355:
1356: }
1357: return HT_OK;
1358: }
1359:
1360: /*
2.54 frystyk 1361: ** Cache Check AFTER filter
2.53 frystyk 1362: ** ------------------------
1363: ** Add an entry for a resource that has just been created so that we can
1364: ** remember the etag and other things. This allows us to guarantee that
2.54 frystyk 1365: ** we don't loose data due to the lost update problem. We also check
1366: ** whether we should delete the cached entry if the request/response
1367: ** invalidated it (if success and method was not "safe")
2.53 frystyk 1368: */
2.54 frystyk 1369: PRIVATE int HTCacheCheckFilter (HTRequest * request, HTResponse * response,
2.53 frystyk 1370: void * param, int status)
1371: {
2.54 frystyk 1372: if (status/100==2 && !HTMethod_isSafe(HTRequest_method(request))) {
1373: if (status==201) {
1374: HTParentAnchor * anchor = HTAnchor_parent(HTResponse_redirection(response));
1375: if (!anchor) anchor = HTRequest_anchor(request);
1376: HTCache_touch(request, response, anchor);
1377: } else {
1378: HTParentAnchor * anchor = HTRequest_anchor(request);
1379: HTCache * cache = HTCache_find(anchor);
1380: if (cache) {
1381: if (status == 204)
1382: HTCache_updateMeta(cache, request, response);
1383: else
1384: HTCache_remove(cache);
1385: }
1386: HTCache_touch(request, response, anchor);
1387: }
1388: }
2.53 frystyk 1389: return HT_OK;
2.22 frystyk 1390: }
1391:
1392: /*
1393: ** Set the size of a cached object. We don't consider the metainformation as
1394: ** part of the size which is the the reason for why the min cache size should
1395: ** not be less than 5M. When we set the cache size we also check whether we
1396: ** should run the gc or not.
1397: */
2.27 frystyk 1398: PRIVATE BOOL HTCache_setSize (HTCache * cache, long written, BOOL append)
2.22 frystyk 1399: {
1400: if (cache) {
2.26 frystyk 1401: /*
1402: ** First look to see if we already have registered this cache entry
1403: ** with a certain size. This size may be a subpart of the total entity
1404: ** (in case the download was interrupted)
1405: */
2.56 frystyk 1406: if (cache->size > 0 && !append) HTCacheContentSize -= cache->size;
2.27 frystyk 1407: cache->size = written;
2.56 frystyk 1408: HTCacheContentSize += written;
2.26 frystyk 1409:
1410: /*
1411: ** Now add the new size to the total cache size. If the new size is
1412: ** bigger than the legal cache size then start the gc.
1413: */
2.56 frystyk 1414: if (CACHE_TRACE) HTTrace("Cache....... Total size %ld\n", HTCacheContentSize);
2.57 frystyk 1415: if (startGC()) HTCacheGarbage();
2.22 frystyk 1416: return YES;
1417: }
1418: return NO;
1419: }
1420:
2.2 frystyk 1421: /*
1422: ** Verifies if a cache object exists for this URL and if so returns a URL
1423: ** for the cached object. It does not verify whether the object is valid or
1424: ** not, for example it might have expired.
1425: **
2.17 frystyk 1426: ** Returns: file name If OK (must be freed by caller)
2.2 frystyk 1427: ** NULL If no cache object found
1428: */
2.22 frystyk 1429: PUBLIC HTCache * HTCache_find (HTParentAnchor * anchor)
1430: {
1431: HTList * list = NULL;
1432: HTCache * pres = NULL;
1433:
1434: /* Find a hash entry for this URL */
1435: if (HTCacheMode_enabled() && anchor && CacheTable) {
1436: char * url = HTAnchor_address((HTAnchor *) anchor);
1437: int hash = 0;
1438: char * ptr = url;
1439: for (; *ptr; ptr++)
2.62 frystyk 1440: hash = (int) ((hash * 3 + (*(unsigned char *) ptr)) % HT_XL_HASH_SIZE);
2.22 frystyk 1441: if (!CacheTable[hash]) {
1442: HT_FREE(url);
1443: return NULL;
1444: }
1445: list = CacheTable[hash];
1446:
1447: /* Search the cache */
1448: {
1449: HTList * cur = list;
1450: while ((pres = (HTCache *) HTList_nextObject(cur))) {
1451: if (!strcmp(pres->url, url)) {
1452: if (CACHE_TRACE) HTTrace("Cache....... Found %p hits %d\n",
1453: pres, pres->hits);
1454: break;
1455: }
1456: }
1457: }
1458: HT_FREE(url);
1459: }
1460: return pres;
1461: }
1462:
1463: /* HTCache_delete
1464: ** --------------
1465: ** Deletes a cache entry
1466: */
1467: PRIVATE BOOL HTCache_delete (HTCache * cache)
2.2 frystyk 1468: {
2.22 frystyk 1469: if (cache && CacheTable) {
1470: HTList * cur = CacheTable[cache->hash];
1471: return cur && delete_object(cur, cache);
1472: }
1473: return NO;
1474: }
1475:
1476: /* HTCache_deleteAll
1477: ** -----------------
1478: ** Destroys all cache entried in memory but does not write anything to
1479: ** disk
1480: */
1481: PUBLIC BOOL HTCache_deleteAll (void)
1482: {
1483: if (CacheTable) {
1484: HTList * cur;
1485: int cnt;
1486:
1487: /* Delete the rest */
2.62 frystyk 1488: for (cnt=0; cnt<HT_XL_HASH_SIZE; cnt++) {
2.22 frystyk 1489: if ((cur = CacheTable[cnt])) {
1490: HTCache * pres;
1491: while ((pres = (HTCache *) HTList_nextObject(cur)) != NULL)
1492: free_object(pres);
1493: }
1494: HTList_delete(CacheTable[cnt]);
2.2 frystyk 1495: }
2.22 frystyk 1496: HT_FREE(CacheTable);
2.56 frystyk 1497: HTCacheContentSize = 0L;
2.22 frystyk 1498: return YES;
2.2 frystyk 1499: }
2.22 frystyk 1500: return NO;
2.2 frystyk 1501: }
1502:
1503: /*
2.25 frystyk 1504: ** Is we have a valid entry in the cache then we also need a location
1505: ** where we can get it. Hopefully, we may be able to access it
1506: ** thourgh one of our protocol modules, for example the local file
1507: ** module. The name returned is in URL syntax and must be freed by
1508: ** the caller
1509: */
2.43 frystyk 1510: PRIVATE char * HTCache_metaLocation (HTCache * cache)
2.25 frystyk 1511: {
1512: char * local = NULL;
2.43 frystyk 1513: if (cache && cache->cachename && *cache->cachename) {
1514: if ((local = (char *) HT_MALLOC(strlen(cache->cachename) +
1515: strlen(HT_CACHE_META) + 5)) == NULL)
1516: HT_OUTOFMEM("HTCache_metaLocation");
1517: sprintf(local, "%s%s", cache->cachename, HT_CACHE_META);
2.25 frystyk 1518: }
1519: return local;
1520: }
1521:
1522: /*
2.34 frystyk 1523: ** Walk through the set of headers and write those out that we are allowed
1524: ** to store in the cache. We look into the connection header to see what the
1525: ** hop-by-hop headers are and also into the cache-control directive to see what
1526: ** headers should not be cached.
1527: */
1528: PRIVATE BOOL meta_write (FILE * fp, HTRequest * request, HTResponse * response)
1529: {
1530: if (fp && request && response) {
2.47 frystyk 1531: HTAssocList * headers = HTAnchor_header(HTRequest_anchor(request));
2.34 frystyk 1532: HTAssocList * connection = HTResponse_connection(response);
1533: char * nocache = HTResponse_noCache(response);
1534:
1535: /*
1536: ** If we don't have any headers then just return now.
1537: */
1538: if (!headers) return NO;
1539:
1540: /*
1541: ** Check whether either the connection header or the cache control
1542: ** header includes header names that we should not cache
1543: */
1544: if (connection || nocache) {
1545:
1546: /*
1547: ** Walk though the cache control no-cache directive
1548: */
1549: if (nocache) {
1550: char * line = NULL;
1551: char * ptr = NULL;
1552: char * field = NULL;
1553: StrAllocCopy(line, nocache); /* Get our own copy */
1554: ptr = line;
1555: while ((field = HTNextField(&ptr)) != NULL)
1556: HTAssocList_removeObject(headers, field);
1557: HT_FREE(line);
1558: }
1559:
1560: /*
1561: ** Walk though the connection header
1562: */
1563: if (connection) {
1564: HTAssoc * pres;
1565: while ((pres=(HTAssoc *) HTAssocList_nextObject(connection))) {
1566: char * field = HTAssoc_name(pres);
1567: HTAssocList_removeObject(headers, field);
1568: }
1569: }
1570: }
1571:
1572: /*
1573: ** Write out the remaining list of headers that we not already store
1574: ** in the index file.
1575: */
1576: {
1577: HTAssocList * cur = headers;
1578: HTAssoc * pres;
1579: while ((pres = (HTAssoc *) HTAssocList_nextObject(cur))) {
2.47 frystyk 1580: char * name = HTAssoc_name(pres);
1581:
1582: /* Don't write the headers that are already hop-by-hop */
1583: if (strcasecomp(name, "connection") &&
1584: strcasecomp(name, "keep-alive") &&
1585: strcasecomp(name, "proxy-authenticate") &&
1586: strcasecomp(name, "proxy-authorization") &&
1587: strcasecomp(name, "transfer-encoding") &&
1588: strcasecomp(name, "upgrade")) {
1589: if (fprintf(fp, "%s: %s\n", name, HTAssoc_value(pres)) < 0) {
1590: if (CACHE_TRACE) HTTrace("Cache....... Error writing metainfo\n");
1591: return NO;
1592: }
2.34 frystyk 1593: }
1594: }
1595: }
1596:
1597: /*
1598: ** Terminate the header with a newline
1599: */
1600: if (fprintf(fp, "\n") < 0) {
1601: if (CACHE_TRACE) HTTrace("Cache....... Error writing metainfo\n");
1602: return NO;
1603: }
1604: return YES;
1605: }
1606: return NO;
1607: }
1608:
1609: /*
1610: ** Save the metainformation for the data object. If no headers
1611: ** are available then the meta file is empty
1612: */
1613: PUBLIC BOOL HTCache_writeMeta (HTCache * cache, HTRequest * request,
1614: HTResponse * response)
1615: {
1616: if (cache && request && response) {
1617: BOOL status;
1618: FILE * fp;
2.43 frystyk 1619: char * name = HTCache_metaLocation(cache);
1620: if (!name) {
1621: if (CACHE_TRACE) HTTrace("Cache....... Invalid cache entry\n");
1622: HTCache_remove(cache);
1623: return NO;
1624: }
2.34 frystyk 1625: if ((fp = fopen(name, "wb")) == NULL) {
1626: if (CACHE_TRACE)
1627: HTTrace("Cache....... Can't open `%s\' for writing\n", name);
1628: HTCache_remove(cache);
1629: HT_FREE(name);
1630: return NO;
1631: }
1632: status = meta_write(fp, request, response);
1633: fclose(fp);
1634: HT_FREE(name);
1635: return status;
1636: }
1637: return NO;
1638: }
1639:
1640: PRIVATE BOOL meta_read (FILE * fp, HTRequest * request, HTStream * target)
1641: {
1642: if (fp && request && target) {
1643: int status;
1644: char buffer[512];
1645: while (1) {
1646: if ((status = fread(buffer, 1, 512, fp)) <= 0) {
1647: if (PROT_TRACE) HTTrace("Cache....... Meta information loaded\n");
1648: return YES;
1649: }
1650:
1651: /* Send the data down the pipe */
1652: status = (*target->isa->put_block)(target, buffer, status);
2.59 frystyk 1653:
1654: /* Delete the response headers */
1655: HTRequest_setResponse(request, NULL);
2.34 frystyk 1656: if (status == HT_LOADED) {
1657: (*target->isa->flush)(target);
1658: return YES;
1659: }
1660: if (status < 0) {
1661: if (PROT_TRACE) HTTrace("Cache....... Target ERROR %d\n", status);
1662: break;
1663: }
1664: }
1665: }
1666: return NO;
1667: }
1668:
1669: /*
1670: ** Read the metainformation for the data object. If no headers are
1671: ** available then the meta file is empty
1672: */
1673: PRIVATE BOOL HTCache_readMeta (HTCache * cache, HTRequest * request)
1674: {
1675: HTParentAnchor * anchor = HTRequest_anchor(request);
1676: if (cache && request && anchor) {
1677: BOOL status;
1678: FILE * fp;
2.43 frystyk 1679: char * name = HTCache_metaLocation(cache);
1680: if (!name) {
1681: if (CACHE_TRACE) HTTrace("Cache....... Invalid meta name\n", name);
1682: HTCache_remove(cache);
1683: return NO;
1684: }
2.34 frystyk 1685: if (CACHE_TRACE) HTTrace("Cache....... Looking for `%s\'\n", name);
1686: if ((fp = fopen(name, "rb")) == NULL) {
1687: if (CACHE_TRACE)
1688: HTTrace("Cache....... Can't open `%s\' for reading\n", name);
1689: HTCache_remove(cache);
1690: HT_FREE(name);
1691: } else {
1692: HTStream * target = HTStreamStack(WWW_MIME_HEAD, WWW_DEBUG,
1693: HTBlackHole(), request, NO);
2.42 frystyk 1694: /*
1695: ** Make sure that we save the reponse information in the anchor
1696: */
2.52 frystyk 1697: HTResponse_setCachable(HTRequest_response(request), HT_CACHE_ALL);
2.34 frystyk 1698: status = meta_read(fp, request, target);
1699: (*target->isa->_free)(target);
1700: fclose(fp);
1701: HT_FREE(name);
1702: return status;
1703: }
1704: }
1705: return NO;
1706: }
1707:
1708: /*
1709: ** Merge metainformation with existing version. This means that we have had a
1710: ** successful validation and hence a true cache hit. We only regard the
1711: ** following headers: Date, content-location, expires, cache-control, and vary.
1712: */
1713: PUBLIC BOOL HTCache_updateMeta (HTCache * cache, HTRequest * request,
1714: HTResponse * response)
1715: {
1716: if (cache && request && response) {
2.36 frystyk 1717: HTParentAnchor * anchor = HTRequest_anchor(request);
2.34 frystyk 1718: cache->hits++;
2.36 frystyk 1719:
1720: /* Calculate the various times */
1721: calculate_time(cache, request, response);
1722:
1723: /* Get the last-modified and etag values if any */
1724: {
1725: char * etag = HTAnchor_etag(anchor);
1726: if (etag) StrAllocCopy(cache->etag, etag);
1727: cache->lm = HTAnchor_lastModified(anchor);
1728: }
1729:
1730: /* Must we revalidate this every time? */
1731: cache->must_revalidate = HTResponse_mustRevalidate(response);
1732:
1733: return YES;
2.34 frystyk 1734: }
1735: return NO;
1736: }
1737:
1738: /*
2.25 frystyk 1739: ** Remove from disk. You must explicitly remove a lock
1740: ** before this operation can succeed
1741: */
1742: PRIVATE BOOL flush_object (HTCache * cache)
1743: {
1744: if (cache && !HTCache_hasLock(cache)) {
2.43 frystyk 1745: char * head = HTCache_metaLocation(cache);
2.25 frystyk 1746: REMOVE(head);
1747: HT_FREE(head);
2.43 frystyk 1748: REMOVE(cache->cachename);
2.25 frystyk 1749: return YES;
1750: }
1751: return NO;
1752: }
1753:
1754: /* HTCache_flushAll
1755: ** ----------------
1756: ** Destroys all cache entried in memory and on disk. Resets the cache
1757: ** to empty but the cache does not have to be reinitialized before we
1758: ** can use it again.
1759: */
1760: PUBLIC BOOL HTCache_flushAll (void)
1761: {
1762: if (CacheTable) {
1763: HTList * cur;
1764: int cnt;
1765:
1766: /* Delete the rest */
2.62 frystyk 1767: for (cnt=0; cnt<HT_XL_HASH_SIZE; cnt++) {
2.25 frystyk 1768: if ((cur = CacheTable[cnt])) {
1769: HTCache * pres;
1770: while ((pres = (HTCache *) HTList_nextObject(cur)) != NULL) {
1771: flush_object(pres);
1772: free_object(pres);
1773: }
1774: }
1775: HTList_delete(CacheTable[cnt]);
1776: CacheTable[cnt] = NULL;
1777: }
1778:
1779: /* Write the new empty index to disk */
1780: HTCacheIndex_write(HTCacheRoot);
2.56 frystyk 1781: HTCacheContentSize = 0L;
2.25 frystyk 1782: return YES;
1783: }
1784: return NO;
1785: }
1786:
1787: /*
2.2 frystyk 1788: ** This function checks whether a document has expired or not.
1789: ** The check is based on the metainformation passed in the anchor object
2.22 frystyk 1790: ** The function returns the level of validation needed for getting a fresh
1791: ** version. We also check the cache control directives in the request to
1792: ** see if they change the freshness discission.
1793: */
1794: PUBLIC HTReload HTCache_isFresh (HTCache * cache, HTRequest * request)
1795: {
1796: HTAssocList * cc = HTRequest_cacheControl(request);
1797: if (cache) {
1798: time_t max_age = -1;
1799: time_t max_stale = -1;
1800: time_t min_fresh = -1;
1801:
1802: /*
2.34 frystyk 1803: ** Make sure that we have the metainformation loaded from the
1804: ** persistent cache
1805: */
1806: HTParentAnchor * anchor = HTRequest_anchor(request);
2.43 frystyk 1807: if (!HTAnchor_headerParsed(anchor)) {
2.60 frystyk 1808: if (HTCache_readMeta(cache, request) != YES)
1809: return HT_CACHE_ERROR;
1810: HTAnchor_setHeaderParsed(anchor);
2.43 frystyk 1811: }
2.34 frystyk 1812:
1813: /*
2.26 frystyk 1814: ** If we only have a part of this request then make a range request
1815: ** using the If-Range condition GET request
1816: */
2.27 frystyk 1817: if (cache->range) {
2.26 frystyk 1818: char buf[20];
2.27 frystyk 1819: sprintf(buf, "%ld-", cache->size);
2.26 frystyk 1820: if (CACHE_TRACE) HTTrace("Cache....... Asking for range `%s\'\n", buf);
1821: HTRequest_addRange(request, "bytes", buf);
1822: HTRequest_addRqHd(request, HT_C_RANGE);
1823: return HT_CACHE_RANGE_VALIDATE;
1824: }
1825:
1826: /*
2.22 frystyk 1827: ** In case this entry is of type "must-revalidate" then we just
1828: ** go ahead and validate it.
1829: */
1830: if (cache->must_revalidate)
1831: return HT_CACHE_VALIDATE;
1832: /*
1833: ** Check whether we have any special constraints like min-fresh in
1834: ** the cache control
1835: */
1836: if (cc) {
1837: char * token = NULL;
1838: if ((token = HTAssocList_findObject(cc, "max-age")))
1839: max_age = atol(token);
1840: if ((token = HTAssocList_findObject(cc, "max-stale")))
1841: max_stale = atol(token);
1842: if ((token = HTAssocList_findObject(cc, "min-fresh")))
1843: min_fresh = atol(token);
1844: }
1845:
1846: /*
1847: ** Now do the checking against the age constraints that we've got
1848: */
1849: {
1850: time_t resident_time = time(NULL) - cache->response_time;
1851: time_t current_age = cache->corrected_initial_age + resident_time;
1852:
1853: /*
1854: ** Check that the max-age, max-stale, and min-fresh directives
1855: ** given in the request cache control header is followed.
1856: */
1857: if (max_age >= 0 && current_age > max_age) {
1858: if (CACHE_TRACE) HTTrace("Cache....... Max-age validation\n");
1859: return HT_CACHE_VALIDATE;
1860: }
1861: if (min_fresh >= 0 &&
1862: cache->freshness_lifetime < current_age + min_fresh) {
1863: if (CACHE_TRACE) HTTrace("Cache....... Min-fresh validation\n");
1864: return HT_CACHE_VALIDATE;
1865: }
1866:
1867: return (cache->freshness_lifetime +
1868: (max_stale >= 0 ? max_stale : 0) > current_age) ?
1869: HT_CACHE_OK : HT_CACHE_VALIDATE;
1870: }
1871: }
1872: return HT_CACHE_FLUSH;
1873: }
1874:
1875: /*
1876: ** While we are creating a new cache object or while we are validating an
1877: ** existing one, we must have a lock on the entry so that not other
1878: ** requests can get to it in the mean while.
1879: */
1880: PUBLIC BOOL HTCache_getLock (HTCache * cache, HTRequest * request)
1881: {
1882: if (cache && request) {
1883: if (CACHE_TRACE) HTTrace("Cache....... Locking cache entry %p\n", cache);
1884: cache->lock = request;
1885: return YES;
1886: }
1887: return NO;
1888: }
1889:
1890: PUBLIC BOOL HTCache_releaseLock (HTCache * cache)
1891: {
1892: if (cache) {
1893: if (CACHE_TRACE) HTTrace("Cache....... Unlocking cache entry %p\n", cache);
1894: cache->lock = NULL;
1895: return YES;
1896: }
1897: return NO;
1898: }
1899:
1900: PUBLIC BOOL HTCache_hasLock (HTCache * cache)
1901: {
1902: return cache && cache->lock;
1903: }
1904:
1905: PUBLIC BOOL HTCache_breakLock (HTCache * cache, HTRequest * request)
1906: {
1907: if (cache && cache->lock) {
1908: if (cache->lock == request) {
1909: if (CACHE_TRACE)
1910: HTTrace("Cache....... Breaking lock on entry %p\n", cache);
1911: cache->lock = NULL;
1912: return YES;
1913: }
1914: }
1915: return NO;
1916: }
1917:
1918: /*
1919: ** Is we have a valid entry in the cache then we also need a location
1920: ** where we can get it. Hopefully, we may be able to access it
1921: ** thourgh one of our protocol modules, for example the local file
1922: ** module. The name returned is in URL syntax and must be freed by
1923: ** the caller
1924: */
1925: PUBLIC char * HTCache_name (HTCache * cache)
1926: {
2.43 frystyk 1927: if (cache) {
1928: char * local = cache->cachename;
2.61 frystyk 1929: char * url = HTLocalToWWW(local, "cache:");
2.22 frystyk 1930: return url;
1931: }
1932: return NULL;
1933: }
1934:
1935: /*
1936: ** Remove from memory AND from disk. You must explicitly remove a lock
1937: ** before this operation can succeed
2.2 frystyk 1938: */
2.22 frystyk 1939: PUBLIC BOOL HTCache_remove (HTCache * cache)
2.2 frystyk 1940: {
2.25 frystyk 1941: return flush_object(cache) && HTCache_delete(cache);
2.22 frystyk 1942: }
1943:
1944: PUBLIC BOOL HTCache_addHit (HTCache * cache)
1945: {
1946: if (cache) {
1947: cache->hits++;
1948: if (CACHE_TRACE) HTTrace("Cache....... Hits for %p is %d\n",
1949: cache, cache->hits);
1950: return YES;
1951: }
1952: return NO;
1953: }
1954:
2.34 frystyk 1955: /* ------------------------------------------------------------------------- */
1956: /* CACHE WRITER */
1957: /* ------------------------------------------------------------------------- */
2.1 frystyk 1958:
2.26 frystyk 1959: PRIVATE BOOL free_stream (HTStream * me, BOOL abort)
2.1 frystyk 1960: {
2.22 frystyk 1961: if (me) {
2.26 frystyk 1962: HTCache * cache = me->cache;
1963:
1964: /*
1965: ** We close the file object. This does not mean that we have the
1966: ** complete object. In case of an "abort" then we only have a part,
1967: ** however, next time we do a load we can use byte ranges to complete
1968: ** the request.
1969: */
2.22 frystyk 1970: if (me->fp) fclose(me->fp);
2.26 frystyk 1971:
2.22 frystyk 1972: /*
1973: ** We are done storing the object body and can update the cache entry.
1974: ** Also update the meta information entry on disk as well. When we
1975: ** are done we don't need the lock anymore.
1976: */
2.26 frystyk 1977: if (cache) {
1978: HTCache_writeMeta(cache, me->request, me->response);
1979: HTCache_releaseLock(cache);
2.22 frystyk 1980:
1981: /*
2.27 frystyk 1982: ** Remember if this is the full entity body or only a subpart
1983: ** We assume that an abort will only give a part of the object.
1984: */
1985: cache->range = abort;
1986:
1987: /*
2.26 frystyk 1988: ** Set the size and maybe do gc. If it is an abort then set the
1989: ** byte range so that we can start from this point next time. We
1990: ** take the byte range as the number of bytes that we have already
1991: ** written to the cache entry.
2.22 frystyk 1992: */
2.27 frystyk 1993: HTCache_setSize(cache, me->bytes_written, me->append);
2.22 frystyk 1994: }
1995:
1996: /*
1997: ** In order not to loose information, we dump the current cache index
1998: ** every time we have created DUMP_FREQUENCY new entries
1999: */
2000: if (new_entries > DUMP_FREQUENCY) {
2001: HTCacheIndex_write(HTCacheRoot);
2002: new_entries = 0;
2003: }
2004: HT_FREE(me);
2.26 frystyk 2005: return YES;
2.22 frystyk 2006: }
2.26 frystyk 2007: return NO;
2008: }
2009:
2010:
2011: PRIVATE int HTCache_free (HTStream * me)
2012: {
2.34 frystyk 2013: return free_stream(me, NO) ? HT_OK : HT_ERROR;
2.1 frystyk 2014: }
2015:
2.12 frystyk 2016: PRIVATE int HTCache_abort (HTStream * me, HTList * e)
2.1 frystyk 2017: {
2.22 frystyk 2018: if (CACHE_TRACE) HTTrace("Cache....... ABORTING\n");
2.34 frystyk 2019: free_stream(me, YES);
2020: return HT_ERROR;
2.1 frystyk 2021: }
2022:
2.34 frystyk 2023: PRIVATE int HTCache_flush (HTStream * me)
2024: {
2025: return (fflush(me->fp) == EOF) ? HT_ERROR : HT_OK;
2026: }
2027:
2028: PRIVATE int HTCache_putBlock (HTStream * me, const char * s, int l)
2029: {
2030: int status = (fwrite(s, 1, l, me->fp) != l) ? HT_ERROR : HT_OK;
2031: if (l > 1 && status == HT_OK) {
2032: HTCache_flush(me);
2033: me->bytes_written += l;
2034: }
2.37 frystyk 2035: return status;
2.34 frystyk 2036: }
2037:
2038: PRIVATE int HTCache_putChar (HTStream * me, char c)
2039: {
2040: return HTCache_putBlock(me, &c, 1);
2041: }
2042:
2043: PRIVATE int HTCache_putString (HTStream * me, const char * s)
2044: {
2045: return HTCache_putBlock(me, s, (int) strlen(s));
2046: }
2047:
2.15 frystyk 2048: PRIVATE const HTStreamClass HTCacheClass =
2.1 frystyk 2049: {
2050: "Cache",
2051: HTCache_flush,
2.17 frystyk 2052: HTCache_free,
2.1 frystyk 2053: HTCache_abort,
2054: HTCache_putChar,
2055: HTCache_putString,
2056: HTCache_putBlock
2057: };
2058:
2.26 frystyk 2059: PRIVATE HTStream * HTCacheStream (HTRequest * request, BOOL append)
2.1 frystyk 2060: {
2.22 frystyk 2061: HTCache * cache = NULL;
2062: FILE * fp = NULL;
2.26 frystyk 2063: HTResponse * response = HTRequest_response(request);
2.19 frystyk 2064: HTParentAnchor * anchor = HTRequest_anchor(request);
2.56 frystyk 2065:
2066: /* If cache is not enabled then exit now */
2.48 frystyk 2067: if (!HTCacheEnable || !HTCacheInitialized) {
2.14 eric 2068: if (CACHE_TRACE) HTTrace("Cache....... Not enabled\n");
2.56 frystyk 2069: return NULL;
2.63 ! kahan 2070: }
! 2071:
! 2072: /* don't cache protected documents */
! 2073: if (HTRequest_credentials (request) && !HTCacheProtected) {
! 2074: if (CACHE_TRACE) HTTrace("Cache....... won't cache protected objects\n");
! 2075: return NULL;
2.56 frystyk 2076: }
2077:
2078: /*
2079: ** Check to see if we already now can see that the entry is going
2080: ** to be too big.
2081: */
2082: if (HTAnchor_length(anchor) > HTCacheMaxEntrySize) {
2083: if (CACHE_TRACE) HTTrace("Cache....... Entry is too big - won't cache\n");
2.22 frystyk 2084: return NULL;
2.1 frystyk 2085: }
2086:
2.22 frystyk 2087: /* Get a new cache entry */
2.26 frystyk 2088: if ((cache = HTCache_new(request, response, anchor)) == NULL) {
2.22 frystyk 2089: if (CACHE_TRACE) HTTrace("Cache....... Can't get a cache object\n");
2090: return NULL;
2091: }
2092:
2093: /* Test that the cached object is not locked */
2094: if (HTCache_hasLock(cache)) {
2095: if (HTCache_breakLock(cache, request) == NO) {
2096: if (CACHE_TRACE) HTTrace("Cache....... Entry already in use\n");
2097: return NULL;
2098: }
2099: }
2100: HTCache_getLock(cache, request);
2101:
2102: /*
2103: ** Test that we can actually write to the cache file. If the entry already
2104: ** existed then it will be overridden with the new data.
2105: */
2.43 frystyk 2106: if ((fp = fopen(cache->cachename, append ? "ab" : "wb")) == NULL) {
2107: if (CACHE_TRACE)
2108: HTTrace("Cache....... Can't open `%s\' for writing\n", cache->cachename);
2109: HTCache_delete(cache);
2110: return NULL;
2111: } else {
2112: if (CACHE_TRACE)
2113: HTTrace("Cache....... %s file `%s\'\n",
2114: append ? "Append to" : "Creating", cache->cachename);
2.22 frystyk 2115: }
2.1 frystyk 2116:
2117: /* Set up the stream */
2.22 frystyk 2118: {
2119: HTStream * me = NULL;
2120: if ((me = (HTStream *) HT_CALLOC(1, sizeof(HTStream))) == NULL)
2121: HT_OUTOFMEM("Cache");
2122: me->isa = &HTCacheClass;
2123: me->request = request;
2.26 frystyk 2124: me->response = response;
2.22 frystyk 2125: me->cache = cache;
2.38 frystyk 2126: me->fp = fp;
2.27 frystyk 2127: me->append = append;
2.22 frystyk 2128: return me;
2129: }
2130: return NULL;
2131: }
2132:
2.26 frystyk 2133: PUBLIC HTStream * HTCacheWriter (HTRequest * request,
2134: void * param,
2135: HTFormat input_format,
2136: HTFormat output_format,
2137: HTStream * output_stream)
2138: {
2139: return HTCacheStream(request, NO);
2140: }
2141:
2142: PUBLIC HTStream * HTCacheAppend (HTRequest * request,
2143: void * param,
2144: HTFormat input_format,
2145: HTFormat output_format,
2146: HTStream * output_stream)
2147: {
2148: return HTCacheStream(request, YES);
2149: }
2150:
2.22 frystyk 2151: /* ------------------------------------------------------------------------- */
2152: /* CACHE READER */
2153: /* ------------------------------------------------------------------------- */
2154:
2155: /*
2156: ** This function closes the connection and frees memory.
2157: ** Returns YES on OK, else NO
2158: */
2.32 eric 2159: PRIVATE int CacheCleanup (HTRequest * req, int status)
2.22 frystyk 2160: {
2.32 eric 2161: HTNet * net = HTRequest_net(req);
2.22 frystyk 2162: cache_info * cache = (cache_info *) HTNet_context(net);
2.32 eric 2163: HTStream * input = HTRequest_inputStream(req);
2164:
2165: /* Free stream with data TO Local cache system */
2166: if (input) {
2167: if (status == HT_INTERRUPTED)
2168: (*input->isa->abort)(input, NULL);
2169: else
2170: (*input->isa->_free)(input);
2171: HTRequest_setInputStream(req, NULL);
2172: }
2173:
2.55 frystyk 2174: /*
2175: ** Remove if we have registered a timer function as a callback
2176: */
2177: if (cache->timer) {
2178: HTTimer_delete(cache->timer);
2179: cache->timer = NULL;
2180: }
2181:
2182: if (cache) {
2183: HT_FREE(cache->local);
2184: HT_FREE(cache);
2.22 frystyk 2185: }
2.55 frystyk 2186: HTNet_delete(net, status);
2.22 frystyk 2187: return YES;
2188: }
2189:
2190: /*
2191: ** This load function loads an object from the cache and puts it to the
2192: ** output defined by the request object. For the moment, this load function
2193: ** handles the persistent cache as if it was on local file but in fact
2194: ** it could be anywhere.
2195: **
2196: ** Returns HT_ERROR Error has occured in call back
2197: ** HT_OK Call back was OK
2198: */
2.29 frystyk 2199: PRIVATE int CacheEvent (SOCKET soc, void * pVoid, HTEventType type);
2200:
2201: PUBLIC int HTLoadCache (SOCKET soc, HTRequest * request)
2.22 frystyk 2202: {
2.29 frystyk 2203: cache_info * cache; /* Specific access information */
2204: HTParentAnchor * anchor = HTRequest_anchor(request);
2.22 frystyk 2205: HTNet * net = HTRequest_net(request);
2206:
2207: /*
2208: ** Initiate a new cache structure and bind to request structure
2209: ** This is actually state CACHE_BEGIN, but it can't be in the state
2210: ** machine as we need the structure first.
2211: */
2.29 frystyk 2212: if (PROT_TRACE) HTTrace("Load Cache.. Looking for `%s\'\n",
2213: HTAnchor_physical(anchor));
2214: if ((cache = (cache_info *) HT_CALLOC(1, sizeof(cache_info))) == NULL)
2215: HT_OUTOFMEM("HTLoadCACHE");
2216: cache->state = CL_BEGIN;
2217: cache->net = net;
2218: HTNet_setContext(net, cache);
2219: HTNet_setEventCallback(net, CacheEvent);
2220: HTNet_setEventParam(net, cache); /* callbacks get http* */
2221:
2222: return CacheEvent(soc, cache, HTEvent_BEGIN); /* get it started - ops is ignored */
2223: }
2224:
2.55 frystyk 2225: PRIVATE int ReturnEvent (HTTimer * timer, void * param, HTEventType type)
2226: {
2227: cache_info * cache = (cache_info *) param;
2228: if (timer != cache->timer)
2229: HTDebugBreak(__FILE__, __LINE__, "File timer %p not in sync\n", timer);
2230: if (PROT_TRACE) HTTrace("HTLoadCache. Continuing %p with timer %p\n", cache, timer);
2231:
2232: /*
2233: ** Delete the timer
2234: */
2235: cache->timer = NULL;
2236:
2237: /*
2238: ** Now call the event again
2239: */
2240: return CacheEvent(INVSOC, cache, HTEvent_READ);
2241: }
2242:
2.29 frystyk 2243: PRIVATE int CacheEvent (SOCKET soc, void * pVoid, HTEventType type)
2244: {
2245: cache_info * cache = (cache_info *)pVoid;
2246: int status = HT_ERROR;
2247: HTNet * net = cache->net;
2248: HTRequest * request = HTNet_request(net);
2249: HTParentAnchor * anchor = HTRequest_anchor(request);
2250:
2.32 eric 2251: if (type == HTEvent_BEGIN) {
2252: cache->state = CL_BEGIN;
2253: } else if (type == HTEvent_CLOSE) {
2.22 frystyk 2254: HTRequest_addError(request, ERR_FATAL, NO, HTERR_INTERRUPTED,
2255: NULL, 0, "HTLoadCache");
2256: CacheCleanup(request, HT_INTERRUPTED);
2257: return HT_OK;
2.32 eric 2258: } else if (type == HTEvent_END) {
2259: CacheCleanup(request, HT_OK);
2260: return HT_OK;
2261: } else if (type == HTEvent_RESET) {
2262: CacheCleanup(request, HT_RECOVER_PIPE);
2263: cache->state = CL_BEGIN;
2264: return HT_OK;
2265: }
2.22 frystyk 2266:
2267: /* Now jump into the machine. We know the state from the previous run */
2268: while (1) {
2269: switch (cache->state) {
2.34 frystyk 2270:
2.22 frystyk 2271: case CL_BEGIN:
2272: if (HTLib_secure()) {
2273: if (PROT_TRACE)
2274: HTTrace("Load Cache.. No access to local file system\n");
2275: cache->state = CL_ERROR;
2276: break;
2277: }
2.40 eric 2278: cache->local = HTWWWToLocal(HTAnchor_physical(anchor), "",
2279: HTRequest_userProfile(request));
2280: if (!cache->local) {
2281: cache->state = CL_ERROR;
2282: break;
2283: }
2.42 frystyk 2284:
2285: /*
2286: ** Create a new host object and link it to the net object
2287: */
2288: {
2289: HTHost * host = NULL;
2.60 frystyk 2290: if ((host = HTHost_new("cache", 0)) == NULL) return HT_ERROR;
2.42 frystyk 2291: HTNet_setHost(net, host);
2292: if (HTHost_addNet(host, net) == HT_PENDING)
2293: if (PROT_TRACE) HTTrace("HTLoadCache. Pending...\n");
2294: }
2.34 frystyk 2295: cache->state = CL_NEED_BODY;
2.22 frystyk 2296: break;
2297:
2298: case CL_NEED_BODY:
2.40 eric 2299: if (HT_STAT(cache->local, &cache->stat_info) == -1) {
2300: if (PROT_TRACE)
2301: HTTrace("Load Cache.. Not found `%s\'\n", cache->local);
2302: HTRequest_addError(request, ERR_FATAL, NO, HTERR_NOT_FOUND,
2303: NULL, 0, "HTLoadCache");
2304: cache->state = CL_ERROR;
2305: break;
2306: }
2.22 frystyk 2307:
2.40 eric 2308: /*
2309: ** The cache entry may be empty in which case we just return
2310: */
2311: if (!cache->stat_info.st_size) {
2312: HTRequest_addError(request, ERR_FATAL, NO,HTERR_NO_CONTENT,
2313: NULL, 0, "HTLoadCache");
2314: cache->state = CL_NO_DATA;
2315: } else
2316: cache->state = CL_NEED_OPEN_FILE;
2.22 frystyk 2317: break;
2318:
2319: case CL_NEED_OPEN_FILE:
2320: status = HTFileOpen(net, cache->local, HT_FT_RDONLY);
2321: if (status == HT_OK) {
2322: /*
2323: ** Create the stream pipe FROM the channel to the application.
2324: ** The target for the input stream pipe is set up using the
2325: ** stream stack.
2326: */
2.42 frystyk 2327: {
2328: HTStream * rstream = HTStreamStack(HTAnchor_format(anchor),
2329: HTRequest_outputFormat(request),
2330: HTRequest_outputStream(request),
2331: request, YES);
2332: HTNet_setReadStream(net, rstream);
2333: HTRequest_setOutputConnected(request, YES);
2334: }
2.32 eric 2335:
2.36 frystyk 2336: /* Set the return code as being OK */
2.34 frystyk 2337: HTRequest_addError(request, ERR_INFO, NO, HTERR_OK,
2338: NULL, 0, "HTLoadCache");
2339: cache->state = CL_NEED_CONTENT;
2.22 frystyk 2340:
2.34 frystyk 2341: /* If we are _not_ using preemptive mode and we are Unix fd's
2342: ** then return here to get the same effect as when we are
2343: ** connecting to a socket. That way, HTCache acts just like any
2344: ** other protocol module even though we are in fact doing
2345: ** blocking connect
2346: */
2.55 frystyk 2347: if (HTEvent_isCallbacksRegistered()) {
2348: if (!HTRequest_preemptive(request)) {
2349: if (!HTNet_preemptive(net)) {
2350: if (PROT_TRACE) HTTrace("HTLoadCache. Returning\n");
2351: HTHost_register(HTNet_host(net), net, HTEvent_READ);
2352: } else if (!cache->timer) {
2353: if (PROT_TRACE) HTTrace("HTLoadCache. Returning\n");
2354: cache->timer = HTTimer_new(NULL, ReturnEvent, cache, 1, YES, NO);
2355: }
2356: return HT_OK;
2357: }
2358: }
2359: } else if (status == HT_WOULD_BLOCK || status == HT_PENDING)
2.22 frystyk 2360: return HT_OK;
2361: else {
2362: HTRequest_addError(request, ERR_INFO, NO, HTERR_INTERNAL,
2363: NULL, 0, "HTLoadCache");
2364: cache->state = CL_ERROR; /* Error or interrupt */
2365: }
2366: break;
2367:
2368: case CL_NEED_CONTENT:
2.42 frystyk 2369: status = HTHost_read(HTNet_host(net), net);
2.22 frystyk 2370: if (status == HT_WOULD_BLOCK)
2371: return HT_OK;
2372: else if (status == HT_LOADED || status == HT_CLOSED) {
2373: cache->state = CL_GOT_DATA;
2374: } else {
2375: HTRequest_addError(request, ERR_INFO, NO, HTERR_FORBIDDEN,
2376: NULL, 0, "HTLoadCache");
2377: cache->state = CL_ERROR;
2378: }
2379: break;
2380:
2381: case CL_GOT_DATA:
2.35 frystyk 2382: CacheCleanup(request, HT_NOT_MODIFIED);
2.34 frystyk 2383: return HT_OK;
2.22 frystyk 2384: break;
2.1 frystyk 2385:
2.22 frystyk 2386: case CL_NO_DATA:
2387: CacheCleanup(request, HT_NO_DATA);
2388: return HT_OK;
2389: break;
2390:
2391: case CL_ERROR:
2392: CacheCleanup(request, HT_ERROR);
2393: return HT_OK;
2394: break;
2395: }
2396: } /* End of while(1) */
2.1 frystyk 2397: }
Webmaster