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

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

Webmaster