📄 htcache.c
字号:
/* the cache size is 0 when we want to force the revalidation of the cache, for example, after a PUT */#if 0 /* @@ JK: trying the following instruction */ if (cache->size == 0) return HT_CACHE_FLUSH;#endif /* ** If we only have a part of this request then make a range request ** using the If-Range condition GET request */ if (cache->range) { char buf[20]; sprintf(buf, "%ld-", cache->size); HTTRACE(CACHE_TRACE, "Cache....... Asking for range `%s\'\n" _ buf); HTRequest_addRange(request, "bytes", buf); HTRequest_addRqHd(request, HT_C_RANGE); return HT_CACHE_RANGE_VALIDATE; } /* ** In case this entry is of type "must-revalidate" then we just ** go ahead and validate it. */ if (cache->must_revalidate) return HT_CACHE_VALIDATE; /* ** Check whether we have any special constraints like min-fresh in ** the cache control */ if (cc) { char * token = NULL; if ((token = HTAssocList_findObject(cc, "max-age"))) max_age = atol(token); if ((token = HTAssocList_findObject(cc, "max-stale"))) max_stale = atol(token); if ((token = HTAssocList_findObject(cc, "min-fresh"))) min_fresh = atol(token); } /* ** Now do the checking against the age constraints that we've got */ { time_t resident_time = time(NULL) - cache->response_time; time_t current_age = cache->corrected_initial_age + resident_time; /* ** Check that the max-age, max-stale, and min-fresh directives ** given in the request cache control header is followed. */ if (max_age >= 0 && current_age > max_age) { HTTRACE(CACHE_TRACE, "Cache....... Max-age validation\n"); return HT_CACHE_VALIDATE; } if (min_fresh >= 0 && cache->freshness_lifetime < current_age + min_fresh) { HTTRACE(CACHE_TRACE, "Cache....... Min-fresh validation\n"); return HT_CACHE_VALIDATE; } return (cache->freshness_lifetime + (max_stale >= 0 ? max_stale : 0) > current_age) ? HT_CACHE_OK : HT_CACHE_VALIDATE; } } return HT_CACHE_FLUSH;}/*** While we are creating a new cache object or while we are validating an** existing one, we must have a lock on the entry so that not other** requests can get to it in the mean while.*/PUBLIC BOOL HTCache_getLock (HTCache * cache, HTRequest * request){ if (cache && request) { HTTRACE(CACHE_TRACE, "Cache....... Locking cache entry %p\n" _ cache); cache->lock = request; return YES; } return NO;}PUBLIC BOOL HTCache_releaseLock (HTCache * cache){ if (cache) { HTTRACE(CACHE_TRACE, "Cache....... Unlocking cache entry %p\n" _ cache); cache->lock = NULL; return YES; } return NO;}PUBLIC BOOL HTCache_hasLock (HTCache * cache){ return cache && cache->lock;}PUBLIC BOOL HTCache_breakLock (HTCache * cache, HTRequest * request){ if (cache && cache->lock) { if (cache->lock == request) { HTTRACE(CACHE_TRACE, "Cache....... Breaking lock on entry %p\n" _ cache); cache->lock = NULL; return YES; } } return NO;}/*** Is we have a valid entry in the cache then we also need a location** where we can get it. Hopefully, we may be able to access it** thourgh one of our protocol modules, for example the local file** module. The name returned is in URL syntax and must be freed by** the caller*/PUBLIC char * HTCache_name (HTCache * cache){ if (cache) { char * local = cache->cachename; char * url = HTLocalToWWW(local, "cache:"); return url; } return NULL;}/*** Remove from memory AND from disk. You must explicitly remove a lock** before this operation can succeed*/PUBLIC BOOL HTCache_remove (HTCache * cache){ return flush_object(cache) && HTCache_delete(cache);}PUBLIC BOOL HTCache_addHit (HTCache * cache){ if (cache) { cache->hits++; HTTRACE(CACHE_TRACE, "Cache....... Hits for %p is %d\n" _ cache _ cache->hits); return YES; } return NO;}/* ------------------------------------------------------------------------- *//* CACHE WRITER *//* ------------------------------------------------------------------------- */PRIVATE BOOL free_stream (HTStream * me, BOOL abort){ if (me) { HTCache * cache = me->cache; /* ** We close the file object. This does not mean that we have the ** complete object. In case of an "abort" then we only have a part, ** however, next time we do a load we can use byte ranges to complete ** the request. */ /* OSB: Now also setting fp to NULL */ if (me->fp) { fclose(me->fp); me->fp = NULL; } /* ** We are done storing the object body and can update the cache entry. ** Also update the meta information entry on disk as well. When we ** are done we don't need the lock anymore. */ if (cache) { HTCache_writeMeta(cache, me->request, me->response); HTCache_releaseLock(cache); /* ** Remember if this is the full entity body or only a subpart ** We assume that an abort will only give a part of the object. */ cache->range = abort; /* ** Set the size and maybe do gc. If it is an abort then set the ** byte range so that we can start from this point next time. We ** take the byte range as the number of bytes that we have already ** written to the cache entry. */ HTCache_setSize(cache, me->bytes_written, me->append); } /* ** In order not to loose information, we dump the current cache index ** every time we have created DUMP_FREQUENCY new entries */ if (new_entries > DUMP_FREQUENCY) { HTCacheIndex_write(HTCacheRoot); new_entries = 0; } HT_FREE(me); return YES; } return NO;}PRIVATE int HTCache_free (HTStream * me){ return free_stream(me, NO) ? HT_OK : HT_ERROR;}PRIVATE int HTCache_abort (HTStream * me, HTList * e){ HTTRACE(CACHE_TRACE, "Cache....... ABORTING\n"); free_stream(me, YES); return HT_ERROR;}PRIVATE int HTCache_flush (HTStream * me){ return (fflush(me->fp) == EOF) ? HT_ERROR : HT_OK;}PRIVATE int HTCache_putBlock (HTStream * me, const char * s, int l){ int status = (fwrite(s, 1, l, me->fp) != (size_t) l) ? HT_ERROR : HT_OK; if (l > 1 && status == HT_OK) { HTCache_flush(me); me->bytes_written += l; } return status;}PRIVATE int HTCache_putChar (HTStream * me, char c){ return HTCache_putBlock(me, &c, 1);}PRIVATE int HTCache_putString (HTStream * me, const char * s){ return HTCache_putBlock(me, s, (int) strlen(s));}PRIVATE const HTStreamClass HTCacheClass ={ "Cache", HTCache_flush, HTCache_free, HTCache_abort, HTCache_putChar, HTCache_putString, HTCache_putBlock};PRIVATE HTStream * HTCacheStream (HTRequest * request, BOOL append){ HTCache * cache = NULL; FILE * fp = NULL; HTResponse * response = HTRequest_response(request); HTParentAnchor * anchor = HTRequest_anchor(request); /* If cache is not enabled then exit now */ if (!HTCacheEnable || !HTCacheInitialized) { HTTRACE(CACHE_TRACE, "Cache....... Not enabled\n"); return NULL; } /* don't cache protected documents */ if (HTRequest_credentials (request) && !HTCacheProtected) { HTTRACE(CACHE_TRACE, "Cache....... won't cache protected objects\n"); return NULL; } /* ** Check to see if we already now can see that the entry is going ** to be too big. */ if (HTAnchor_length(anchor) > HTCacheMaxEntrySize) { HTTRACE(CACHE_TRACE, "Cache....... Entry is too big - won't cache\n"); return 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; } /* Test that the cached object is not locked */ if (HTCache_hasLock(cache)) { if (HTCache_breakLock(cache, request) == NO) { HTTRACE(CACHE_TRACE, "Cache....... Entry already in use\n"); return NULL; } } HTCache_getLock(cache, request); /* ** Test that we can actually write to the cache file. If the entry already ** existed then it will be overridden with the new data. */ if ((fp = fopen(cache->cachename, append ? "ab" : "wb")) == NULL) { HTTRACE(CACHE_TRACE, "Cache....... Can't open `%s\' for writing\n" _ cache->cachename); HTCache_delete(cache); return NULL; } else { HTTRACE(CACHE_TRACE, "Cache....... %s file `%s\'\n" _ append ? "Append to" : "Creating" _ cache->cachename); } /* Set up the stream */ { HTStream * me = NULL; if ((me = (HTStream *) HT_CALLOC(1, sizeof(HTStream))) == NULL) HT_OUTOFMEM("Cache"); me->isa = &HTCacheClass; me->request = request; me->response = response; me->cache = cache; me->fp = fp; me->append = append; return me; } return NULL;}PUBLIC HTStream * HTCacheWriter (HTRequest * request, void * param, HTFormat input_format, HTFormat output_format, HTStream * output_stream){ return HTCacheStream(request, NO);}PUBLIC HTStream * HTCacheAppend (HTRequest * request, void * param, HTFormat input_format, HTFormat output_format, HTStream * output_stream){ return HTCacheStream(request, YES);}/* ------------------------------------------------------------------------- *//* CACHE READER *//* ------------------------------------------------------------------------- *//*** This function copies the headers associated with a cached** object to closes the connection and frees memory.** Returns YES on OK, else NO*/PRIVATE void HTCache_copyHeaders (HTRequest * req){ HTParentAnchor * anchor; char * url; anchor = HTRequest_anchor (req); url = HTAnchor_physical(anchor); if (url && !strncmp (url, "cache:", sizeof ("cache:") - 1 )) { /* HTMIME_anchor2response (req); */ /* set up a "dummy" stack just for copying the MIME type. We need this to remove any dependencies between the MIME and CACHE minilibs */ HTStreamStack(WWW_MIME_COPYHEADERS, WWW_DEBUG, HTBlackHole(), req, NO); }}/*** This function closes the connection and frees memory.** Returns YES on OK, else NO*/PRIVATE int CacheCleanup (HTRequest * req, int status){ HTNet * net = HTRequest_net(req); cache_info * cache = (cache_info *) HTNet_context(net); HTStream * input = HTRequest_inputStream(req); /* Free stream with data TO Local cache system */ if (input) { if (status == HT_INTERRUPTED) (*input->isa->abort)(input, NULL); else (*input->isa->_free)(input); HTRequest_setInputStream(req, NULL); } /* ** Remove if we have registered a timer function as a callback */ if (cache->timer) { HTTimer_delete(cache->timer); cache->timer = NULL; } if (cache) { HT_FREE(cache->local); HT_FREE(cache); } /* if the object was cached, we copy the pertinent HTTP headers from the anchor object (where they are stored) to the response object */ if (status == HT_NOT_MODIFIED) HTCache_copyHeaders (req); HTNet_delete(net, status); return YES;}/*** This load function loads an object from the cache and puts it to the** output defined by the request object. For the moment, this load function** handles the persistent cache as if it was on local file but in fact ** it could be anywhere.**** Returns HT_ERROR Error has occured in call back** HT_OK Call back was OK*/PRIVATE int CacheEvent (SOCKET soc, void * pVoid, HTEventType type);PUBLIC int HTLoadCache (SOCKET soc, HTRequest * request){ cache_info * cache; /* Specific access information */ HTParentAnchor * anchor = HTRequest_anchor(request); HTNet * net = HTRequest_net(request); /* ** Initiate a new cache structure and bind to request structure ** This is actually state CACHE_BEGIN, but it can't be in the state ** machine as we need the structure first. */ HTTRACE(PROT_TRACE, "Load Cache.. Looking for `%s\'\n" _ HTAnchor_physical(anchor)); if ((cache = (cache_info *) HT_CALLOC(1, sizeof(cache_info))) == NULL) HT_OUTOFMEM("HTLoadCACHE"); cache->state = CL_BEGIN; cache->net = net; HTNet_setContext(net, cache); HTNet_setEventCallback(net, CacheEvent); HTNet_setEventParam(net, cache); /* callbacks get http* */ return CacheEvent(soc, cache, HTEvent_BEGIN); /* get it started - ops is ignored */}PRIVATE int ReturnEvent (HTTimer * timer, void * param, HTEventType type){ cache_info * cache = (cache_info *) param; if (timer != cache->timer) HTDEBUGBREAK("File timer %p not in sync\n" _
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -