Annotation of libwww/Library/src/HTCache.c, revision 2.26.2.1

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

Webmaster