📄 htcache.c
字号:
long new_size = size*MEGA; if (new_size > 0 && new_size < HTCacheTotalSize-HTCacheFolderSize) { long old_size = HTCacheMaxEntrySize; HTCacheMaxEntrySize = new_size; if (new_size < old_size) HTCacheGarbage(); HTTRACE(CACHE_TRACE, "Cache...... Max entry cache size is %ld\n" _ HTCacheMaxEntrySize); return YES; } HTTRACE(CACHE_TRACE, "Cache...... Max entry cache size is unchanged\n"); return NO;}PUBLIC int HTCacheMode_maxCacheEntrySize (void){ return HTCacheMaxEntrySize / MEGA;}/*** Set the default expiration time. In seconds.*/PUBLIC void HTCacheMode_setDefaultExpiration (const int exp_time){ DefaultExpiration = exp_time;}PUBLIC int HTCacheMode_DefaultExpiration (void){ return DefaultExpiration;}/* ------------------------------------------------------------------------- *//* CACHE OBJECT *//* ------------------------------------------------------------------------- */PRIVATE BOOL free_object (HTCache * me){ HT_FREE(me->url); HT_FREE(me->cachename); HT_FREE(me->etag); HT_FREE(me); return YES;}PRIVATE BOOL delete_object (HTList * list, HTCache * me){ HTTRACE(CACHE_TRACE, "Cache....... delete %p from list %p\n" _ me _ list); HTList_removeObject(list, (void *) me); HTCacheContentSize -= me->size; free_object(me); return YES;}/*** Create directory path for cache file**** On exit:** return YES** if directories created -- after that caller** can rely on fopen(cfn,"w") succeeding.***/PRIVATE BOOL HTCache_createLocation (HTCache * me){ if (me && HTCacheRoot) { BOOL status = YES; char * path = NULL; struct stat stat_info; if ((path = (char *) HT_MALLOC(strlen(HTCacheRoot) + 10)) == NULL) HT_OUTOFMEM("HTCache_createLocation"); /* ** Find the path and check whether the directory already exists or not */ sprintf(path, "%s%d", HTCacheRoot, me->hash); if (HT_STAT(path, &stat_info) == -1) { HTTRACE(CACHE_TRACE, "Cache....... Create dir `%s\'\n" _ path); if (MKDIR(path, 0777) < 0) { HTTRACE(CACHE_TRACE, "Cache....... Can't create...\n"); status = NO; } } else { HTTRACE(CACHE_TRACE, "Cache....... Directory `%s\' already exists\n" _ path); } /* ** Find a non-existent filename within the path that we just created */ me->cachename = HTGetTmpFileName(path); HT_FREE(path); return status; } return NO;}/*** Find a cache filename for this cache object.*/#if 0PRIVATE BOOL HTCache_findName (HTCache * me){ if (me) { /* ** Create path for this cache entry. We base the cache location on the ** hash calculated as a function of the URL. That way, we ensure a ** resonably uniform distribution. */ me->cachename = HTGetTmpFileName(NULL); return HTCache_createLocation(me); } return NO;}#endif/*** Calculate the corrected_initial_age of the object. We use the time** when this function is called as the response_time as this is when** we have received the complete response. This may cause a delay if** the reponse header is very big but should not cause any incorrect** behavior.*/PRIVATE BOOL calculate_time (HTCache * me, HTRequest * request, HTResponse * response){ if (me && request) { HTParentAnchor * anchor = HTRequest_anchor(request); time_t date = HTAnchor_date(anchor); me->response_time = time(NULL); me->expires = HTAnchor_expires(anchor); { time_t apparent_age = HTMAX(0, me->response_time - date); time_t corrected_received_age = HTMAX(apparent_age, HTAnchor_age(anchor)); time_t response_delay = me->response_time - HTRequest_date(request); me->corrected_initial_age = corrected_received_age + response_delay; } /* ** Estimate an expires time using the max-age and expires time. If we ** don't have an explicit expires time then set it to 10% of the LM ** date (although max 24 h). If no LM date is available then use 24 hours. */ { time_t freshness_lifetime = HTResponse_maxAge(response); if (freshness_lifetime < 0) { if (me->expires < 0) { time_t lm = HTAnchor_lastModified(anchor); if (lm < 0) { freshness_lifetime = DefaultExpiration; } else { freshness_lifetime = LM_EXPIRATION(date - lm); if (freshness_lifetime > WARN_HEURISTICS) HTRequest_addError(request, ERR_WARN, NO, HTERR_HEURISTIC_EXPIRATION, NULL, 0, "calculate_time"); } } else freshness_lifetime = me->expires - date; } me->freshness_lifetime = HTMAX(0, freshness_lifetime); } HTTRACE(CACHE_TRACE, "Cache....... Received Age %d, corrected %d, freshness lifetime %d\n" _ HTAnchor_age(anchor) _ me->corrected_initial_age _ me->freshness_lifetime); return YES; } return NO;}/*** Create a new cache entry and add it to the list*/PRIVATE HTCache * HTCache_new (HTRequest * request, HTResponse * response, HTParentAnchor * anchor){ HTList * list = NULL; /* Current list in cache */ HTCache * pres = NULL; int hash = 0; char * url = NULL; if (!request || !response || !anchor) { HTTRACE(CORE_TRACE, "Cache....... Bad argument\n"); return NULL; } /* Find a hash for this anchor */ if ((url = HTAnchor_address((HTAnchor *) anchor))) { char * ptr; for (ptr=url; *ptr; ptr++) hash = (int) ((hash * 3 + (*(unsigned char *) ptr)) % HT_XL_HASH_SIZE); if (!CacheTable) { if ((CacheTable = (HTList **) HT_CALLOC(HT_XL_HASH_SIZE, sizeof(HTList *))) == NULL) HT_OUTOFMEM("HTCache_new"); } if (!CacheTable[hash]) CacheTable[hash] = HTList_new(); list = CacheTable[hash]; } else return NULL; /* Search the cache */ { HTList * cur = list; while ((pres = (HTCache *) HTList_nextObject(cur))) { if (!strcmp(pres->url, url)) break; } } /* If not found then create new cache object, else use existing one */ if (!pres) { if ((pres = (HTCache *) HT_CALLOC(1, sizeof(HTCache))) == NULL) HT_OUTOFMEM("HTCache_new"); pres->hash = hash; pres->url = url; pres->range = NO; HTCache_createLocation(pres); HTList_addObject(list, (void *) pres); new_entries++; } else HT_FREE(url); if (HTCache_hasLock(pres)) { if (HTCache_breakLock(pres, request) == NO) { HTTRACE(CACHE_TRACE, "Cache....... Entry %p already in use\n"); return pres; } } HTCache_getLock(pres, request); /* Calculate the various times */ calculate_time(pres, request, response); /* Get the last-modified and etag values if any */ { char * etag = HTAnchor_etag(anchor); if (etag) StrAllocCopy(pres->etag, etag); pres->lm = HTAnchor_lastModified(anchor); } /* Must we revalidate this every time? */ pres->must_revalidate = HTResponse_mustRevalidate(response); return pres;}/*** Add an entry for a resource that has just been created so that we can ** remember the etag and other things. This allows us to guarantee that** we don't loose data due to the lost update problem*/PUBLIC HTCache * HTCache_touch (HTRequest * request, HTResponse * response, HTParentAnchor * anchor){ HTCache * cache = NULL; /* Get a new cache entry */ if ((cache = HTCache_new(request, response, anchor)) == NULL) { HTTRACE(CACHE_TRACE, "Cache....... Can't get a cache object\n"); return NULL; } /* We don't have any of the data in cache - only meta information */ if (cache) { cache->size = 0; cache->range = YES; } return cache;}/*** Cache Validation BEFORE Filter** ------------------------------** Check the cache mode to see if we can use an already loaded version** of this document. If so and our copy is valid then we don't have** to go out and get it unless we are forced to** We only check the cache in caseof a GET request. Otherwise, we go** directly to the source.*/PRIVATE int HTCacheFilter (HTRequest * request, void * param, int mode){ HTParentAnchor * anchor = HTRequest_anchor(request); char * default_name = HTRequest_defaultPutName (request); HTCache * cache = NULL; HTReload reload = HTRequest_reloadMode(request); HTMethod method = HTRequest_method(request); HTDisconnectedMode disconnect = HTCacheMode_disconnected(); BOOL validate = NO; /* ** If the cache is disabled all together then it won't help looking, huh? */ if (!HTCacheMode_enabled()) return HT_OK; HTTRACE(CACHE_TRACE, "Cachefilter. Checking persistent cache\n"); /* ** Now check the cache... */ if (method != METHOD_GET) { HTTRACE(CACHE_TRACE, "Cachefilter. We only check GET methods\n"); } else if (reload == HT_CACHE_FLUSH) { /* ** If the mode if "Force Reload" then don't even bother to check the ** cache - we flush everything we know abut this document anyway. ** Add the appropriate request headers. We use both the "pragma" ** and the "cache-control" headers in order to be ** backwards compatible with HTTP/1.0 */ validate = YES; HTRequest_addGnHd(request, HT_G_PRAGMA_NO_CACHE); HTRequest_addCacheControl(request, "no-cache", ""); /* ** We also flush the information in the anchor as we don't want to ** inherit any "old" values */ HTAnchor_clearHeader(anchor); } else { /* ** Check the persistent cache manager. If we have a cache hit then ** continue to see if the reload mode requires us to do a validation ** check. This filter assumes that we can get the cached version ** through one of our protocol modules (for example the file module) */ cache = HTCache_find(anchor, default_name); if (cache) { HTReload cache_mode = HTCache_isFresh(cache, request); if (cache_mode == HT_CACHE_ERROR) cache = NULL; reload = HTMAX(reload, cache_mode); HTRequest_setReloadMode(request, reload); /* ** Now check the mode and add the right headers for the validation ** If we are to validate a cache entry then we get a lock ** on it so that not other requests can steal it. */ if (reload == HT_CACHE_RANGE_VALIDATE) { /* ** If we were asked to range validate the cached object then ** use the etag or the last modified for cache validation */ validate = YES; HTCache_getLock(cache, request); HTRequest_addRqHd(request, HT_C_IF_RANGE); } else if (reload == HT_CACHE_END_VALIDATE) { /* ** If we were asked to end-to-end validate the cached object ** then use a max-age=0 cache control directive */ validate = YES; HTCache_getLock(cache, request); HTRequest_addCacheControl(request, "max-age", "0"); } else if (reload == HT_CACHE_VALIDATE) { /* ** If we were asked to validate the cached object then ** use the etag or the last modified for cache validation ** We use both If-None-Match or If-Modified-Since. */ validate = YES; HTCache_getLock(cache, request); HTRequest_addRqHd(request, HT_C_IF_NONE_MATCH | HT_C_IMS); } else if (cache) { /* ** The entity does not require any validation at all. We ** can just go ahead and get it from the cache. In case we ** have a fresh subpart of the entity, then we issue a ** conditional GET request with the range set by the cache ** manager. Issuing the conditional range request is ** equivalent to a validation as we have to go out on the ** net. This may have an effect if running in disconnected ** mode. We disable all BEFORE filters as they don't make ** sense while loading the cache entry. */ { char * name = HTCache_name(cache); HTAnchor_setPhysical(anchor, name); HTCache_addHit(cache); HT_FREE(name); } } } } /* ** If we are in disconnected mode and we are to validate an entry ** then check whether what mode of disconnected mode we're in. If ** we are to use our own cache then return a "504 Gateway Timeout" */ if ((!cache || validate) && disconnect != HT_DISCONNECT_NONE) { if (disconnect == HT_DISCONNECT_EXTERNAL) HTRequest_addCacheControl(request, "only-if-cached", ""); else { HTRequest_addError(request, ERR_FATAL, NO, HTERR_GATE_TIMEOUT, "Disconnected Cache Mode", 0, "HTCacheFilter"); return HT_ERROR; } } return HT_OK;}/*** Cache Update AFTER filter** -------------------------** On our way out we catch the metainformation and stores it in** our persistent store. If we have a cache validation (a 304** response then we use the new metainformation and merges it with** the existing information already captured in the cache.*/PRIVATE int HTCacheUpdateFilter (HTRequest * request, HTResponse * response, void * param, int status){ HTParentAnchor * anchor = HTRequest_anchor(request); char * default_name = HTRequest_defaultPutName(request); HTCache * cache = HTCache_find(anchor, default_name); if (cache) { /* ** It may in fact be that the information in the 304 response ** told us that we can't cache the entity anymore. If this is the ** case then flush it now. Otherwise prepare for a cache read */ HTTRACE(CACHE_TRACE, "Cache....... Merging metainformation\n"); if (HTResponse_isCachable(response) == HT_NO_CACHE) { HTCache_remove(cache); } else { char * name = HTCache_name(cache); HTAnchor_setPhysical(anchor, name); HTCache_addHit(cache); HT_FREE(name); HTCache_updateMeta(cache, request, response); } /* ** Start request directly from the cache. As with the redirection filter ** we reuse the same request object which means that we must ** keep this around until the cache load request has terminated ** In the case of a */ HTLoad(request, YES); return HT_ERROR; } else { /* If entry doesn't already exist then create a new entry */ HTCache_touch(request, response, anchor); } return HT_OK;}/*** Cache Check AFTER filter** ------------------------** Add an entry for a resource that has just been created so that we can ** remember the etag and other things. This allows us to guarantee that** we don't loose data due to the lost update problem. We also check** whether we should delete the cached entry if the request/response** invalidated it (if success and method was not "safe")*/PRIVATE int HTCacheCheckFilter (HTRequest * request, HTResponse * response, void * param, int status){ if (status/100==2 && !HTMethod_isSafe(HTRequest_method(request))) { if (status==201) { HTParentAnchor * anchor = HTAnchor_parent(HTResponse_redirection(response));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -