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