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