📄 htcache.c
字号:
} 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. */ if (me->fp) fclose(me->fp); /* ** 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) != 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 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); } 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" _ timer); HTTRACE(PROT_TRACE, "HTLoadCache. Continuing %p with timer %p\n" _ cache _ timer); /* ** Delete the timer */ cache->timer = NULL; /* ** Now call the event again */ return CacheEvent(INVSOC, cache, HTEvent_READ);}PRIVATE int CacheEvent (SOCKET soc, void * pVoid, HTEventType type){ cache_info * cache = (cache_info *)pVoid; int status = HT_ERROR; HTNet * net = cache->net; HTRequest * request = HTNet_request(net); HTParentAnchor * anchor = HTRequest_anchor(request); if (type == HTEvent_BEGIN) { cache->state = CL_BEGIN; } else if (type == HTEvent_CLOSE) { HTRequest_addError(request, ERR_FATAL, NO, HTERR_INTERRUPTED, NULL, 0, "HTLoadCache"); CacheCleanup(request, HT_INTERRUPTED); return HT_OK; } else if (type == HTEvent_END) { CacheCleanup(request, HT_OK); return HT_OK; } else if (type == HTEvent_RESET) { CacheCleanup(request, HT_RECOVER_PIPE); cache->state = CL_BEGIN; return HT_OK; } /* Now jump into the machine. We know the state from the previous run */ while (1) { switch (cache->state) { case CL_BEGIN: if (HTLib_secure()) { HTTRACE(PROT_TRACE, "Load Cache.. No access to local file system\n"); cache->state = CL_ERROR; break; } cache->local = HTWWWToLocal(HTAnchor_physical(anchor), "", HTRequest_userProfile(request)); if (!cache->local) { cache->state = CL_ERROR; break; } /* ** Create a new host object and link it to the net object */ { HTHost * host = NULL; if ((host = HTHost_new("cache", 0)) == NULL) return HT_ERROR; HTNet_setHost(net, host); if (HTHost_addNet(host, net) == HT_PENDING) HTTRACE(PROT_TRACE, "HTLoadCache. Pending...\n"); } cache->state = CL_NEED_BODY; break; case CL_NEED_BODY: if (HT_STAT(cache->local, &cache->stat_info) == -1) { HTTRACE(PROT_TRACE, "Load Cache.. Not found `%s\'\n" _ cache->local); HTRequest_addError(request, ERR_FATAL, NO, HTERR_NOT_FOUND, NULL, 0, "HTLoadCache"); cache->state = CL_ERROR; break; } /* ** The cache entry may be empty in which case we just return */ if (!cache->stat_info.st_size) { HTRequest_addError(request, ERR_FATAL, NO,HTERR_NO_CONTENT, NULL, 0, "HTLoadCache"); cache->state = CL_NO_DATA; } else cache->state = CL_NEED_OPEN_FILE; break; case CL_NEED_OPEN_FILE: status = HTFileOpen(net, cache->local, HT_FT_RDONLY); if (status == HT_OK) { /* ** Create the stream pipe FROM the channel to the application. ** The target for the input stream pipe is set up using the ** stream stack. */ { HTStream * rstream = HTStreamStack(HTAnchor_format(anchor), HTRequest_outputFormat(request), HTRequest_outputStream(request), request, YES); HTNet_setReadStream(net, rstream); HTRequest_setOutputConnected(request, YES); } /* Set the return code as being OK
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -