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