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

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

Webmaster