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

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

Webmaster