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