/* $Id: cache.c,v 1.12 2006/11/18 16:04:05 maxim Exp $ * */ #include #include #include #include #include #include #include #include "configure.h" #if SUPPORT_CACHE #include "common/settings.h" #include "common/misc.h" #include "common/str.h" #include "config.h" #include "core.h" #include "config.h" #include "cache.h" #define CACHE_HASH 8192 #define CACHE_STEP 1024 static CacheEntry* freeEntries; void deactivateEntry(Cache* cache, CacheEntry* e); Cache* newCache() { Cache* cache = CALLOC(sizeof(Cache)); if (cache) cache->entries = CALLOC(CACHE_HASH * sizeof(CacheEntry*)); return cache; } Cache* freeCache(Cache* cache) { if (!cache) return NULL; while(cache->old) deactivateEntry(cache, cache->old); FREE(cache->entries); return FREE(cache->name); } int freeCacheArea(Cache* cache, size_t need) { size_t freed = 0; debug("freeCacheArea(%d)", need); while(cache->old && freed < need) { freed += cache->old->length; deactivateEntry(cache, cache->old); } return freed >= need; } void copyCache(Cache* cache, Cache* old) { debug("copyCache(%s)", cache->name); if (old->size > cache->maxSize) freeCacheArea(old, old->size - cache->maxSize); cache->size = old->size; cache->count = old->count; cache->orphans = old->orphans; cache->creates = old->creates; cache->hits = old->hits; cache->recent = old->recent; cache->old = old->old; old->old = old->recent = NULL; { CacheEntry** e; e = cache->entries; cache->entries = old->entries; old->entries = e; } } void resetTodayCache() { Pair* p; debug("resetTodayCache()"); if (!config->caches->count) return; startListPairs(config->caches); while((p = nextPair(config->caches))) { Cache* cache = p->value; CacheEntry* e = cache->recent; while(e) { e->hits = 0; e = e->old; } cache->hits = cache->creates = 0; } } void cleanupCache() { Pair* p; debug("cleanupCache()"); if (!config->caches->count) return; startListPairs(config->caches); while((p = nextPair(config->caches))) { Cache* cache = p->value; CacheEntry* e = cache->recent; while(e) { CacheEntry* next = e->old; if (now - e->created > cache->maxAge || (e->expires && now > e->expires)) deactivateEntry(cache, e); e = next; } } } CacheEntry* newCacheEntry(Request* r) { Cache* cache = r->alias->cache; CacheEntry* e; char name[MAX_URI]; int n; char* s; if (r->alias->exactLocation) n = catstr(name, MAX_URI, cache->skipHost ? "" : r->host, "=", r->alias->location, NULL); else n = catstr(name, MAX_URI, cache->skipHost ? "" : r->host, r->alias->alias, r->path, NULL); if (!cache->skipQuery) { if (r->params) n += catstr(name + n, MAX_URI - n, ";", r->params, NULL); if (r->query) n += catstr(name + n, MAX_URI - n, "?", r->query, NULL); } debug("newCacheEntry(%s)", name); if (cache->size + r->length > cache->maxSize && !freeCacheArea(cache, cache->size + r->length - cache->maxSize)) return NULL; if (!(e = freeEntries)) { debug("newCacheEntry: allocating new entries block"); freeEntries = MALLOC(CACHE_STEP * sizeof(CacheEntry)); if (!freeEntries) return NULL; for (e = freeEntries; e < freeEntries + CACHE_STEP - 1; e++) e->next = e + 1; e->next = NULL; e = freeEntries; } freeEntries = freeEntries->next; s = MALLOC(++n + r->length + (r->contentType ? (strlen(r->contentType) + 1) : 0) + (r->contentEnc ? (strlen(r->contentEnc) + 1) : 0)); if (!s) { e->next = freeEntries; freeEntries = e; return NULL; } e->hash = hash(name); e->name = memcpy(s, name, n); s += n; if (r->contentType) { n = strlen(r->contentType) + 1; e->contentType = memcpy(s, r->contentType, n); s += n; } else { e->contentType = NULL; } if (r->contentEnc) { n = strlen(r->contentEnc) + 1; e->contentEnc = memcpy(s, r->contentEnc, n); s += n; } else { e->contentEnc = NULL; } e->body = s; e->length = r->length; e->modified = r->modified; e->expires = r->expires; e->created = 0; e->links = 1; e->hits = 0; e->prev = e->next = e->recent = e->old = NULL; cache->creates++; cache->orphans++; return e; } void insertEntry(Cache* cache, CacheEntry *e) { debug("insertEntry(%s)", e->name); if ((e->old = cache->recent)) cache->recent->recent = e; else cache->old = e; cache->recent = e; e->recent = NULL; } void removeEntry(Cache* cache, CacheEntry* e) { debug("removeEntry(%s)", e->name); if (e->old) e->old->recent = e->recent; else cache->old = e->recent; if (e->recent) e->recent->old = e->old; else cache->recent = e->old; } void activateEntry(Cache* cache, CacheEntry* e) { unsigned h = e->hash & (CACHE_HASH - 1); debug("activateEntry(%s)", e->name); if ((e->next = cache->entries[h])) e->next->prev = e; cache->entries[h] = e; e->prev = NULL; e->created = now; cache->size += e->length; cache->count++; cache->orphans--; insertEntry(cache, e); } void deactivateEntry(Cache* cache, CacheEntry* e) { debug("deactivateEntry(%s)%s%s", e->name, e->links ? " linked" : "", e->created ? " listed" : ""); if (e->created) { if (e->next) e->next->prev = e->prev; if (e->prev) e->prev->next = e->next; else cache->entries[e->hash & (CACHE_HASH - 1)] = e->next; cache->size -= e->length; cache->count--; cache->orphans++; e->created = 0; removeEntry(cache, e); } if (!e->links) { debug("really freed"); FREE(e->name); cache->orphans--; e->next = freeEntries; freeEntries = e; } } CacheEntry* unlinkEntry(Cache* cache, CacheEntry* e) { debug("unlinkEntry(%s) [%d]%s", e->name, e->links, e->created ? " listed" : ""); if (--(e->links) == 0 && e->created == 0) deactivateEntry(cache, e); return NULL; } int handleCached(Request* r) { Cache* cache = r->alias->cache; CacheEntry* e; unsigned h; char name[MAX_URI]; int n = 0; if (r->alias->exactLocation) n = catstr(name, MAX_URI, cache->skipHost ? "" : r->host, "=", r->alias->location, NULL); else n = catstr(name, MAX_URI, cache->skipHost ? "" : r->host, r->alias->alias, r->path, NULL); if (!cache->skipQuery) { if (r->params) n += catstr(name + n, MAX_URI - n, ";", r->params, NULL); if (r->query) n += catstr(name + n, MAX_URI - n, "?", r->query, NULL); } debug("handleCached(%s)", name); h = hash(name); e = cache->entries[h & (CACHE_HASH - 1)]; while (e) { if (e->hash == h && !strcmp(name, e->name)) { if ((r->nocache && now - e->created > cache->minAge) || (e->expires && e->expires < now)) { debug("remove requested entry %s: %s", name, r->nocache ? "nocache" : "expired"); deactivateEntry(cache, e); return 404; } else if (e != cache->recent) { removeEntry(cache, e); insertEntry(cache, e); } cache->hits++; e->links++; e->hits++; r->cached = e; if (r->ims && r->ims >= e->modified) return 304; if (r->iums && r->iums < e->modified) return 412; r->body = e->body; r->length = e->length; r->modified = e->modified; r->expires = e->expires; r->contentType = e->contentType; r->contentEnc = e->contentEnc; r->acceptRanges = YES; if (r->ifRange && r->ifRange < e->modified) r->rangeFrom = r->rangeTo = -1; return 0; } e = e->next; } return 404; } int getCacheStats(char* buffer, int size, char show, char* uri) { int m, s; Cache* cache; Pair* p; int n = 0; unsigned sumCount = 0, sumCounts = 0, sumCreates = 0, sumHits = 0; long long sumSize = 0; int l = 0; if (!config->caches->count) return 0; m = strlen("99 caches"); startListPairs(config->caches); while((p = nextPair(config->caches))) { cache = (Cache*)p->value; s = strlen(cache->name); if (s > m) m = s; n++; } l += SNPRINTF(buffer + l, size - l, "\n%-*s %5s %6s %8s %10s\n", m + 2, "Cache", "size", "number", "creates", "hits"); startListPairs(config->caches); while((p = nextPair(config->caches))) { cache = (Cache*)p->value; sumCount++; l += SNPRINTF(buffer + l, size - l, " %s%-*s %5s %6s %8s %10s%s%s\n", n > 1 && sumCount < n ? "" : "", m, cache->name, humanSize(cache->size), itos(cache->count), itos(cache->creates), itos(cache->hits), cache->orphans < 20 ? "" : fmt(" (%s orphans)", itos(cache->orphans)), n > 1 && sumCount < n ? "" : "" ); sumCounts += cache->count; sumSize += cache->size; sumCreates += cache->creates; sumHits += cache->hits; } if (sumCount > 1) l += SNPRINTF(buffer + l, size - l, " %d %-*s %5s %6s %8s %10s\n", sumCount, m - (sumCount < 10 ? 2 : sumCount < 100 ? 3 : 4), "caches", humanSize(sumSize), itos(sumCounts), itos(sumCreates), itos(sumHits) ); if (show) { l += SNPRINTF(buffer + l, size - l, "%7s %5s %4s %5s %s\n", "Hits", "Links", "Age", "Size", "Name"); startListPairs(config->caches); while((p = nextPair(config->caches))) { CacheEntry* e; cache = (Cache*)p->value; e = cache->recent; while(e && l < size - 100) { l += SNPRINTF(buffer + l, size - l, "%7s %5d %4s %5s %s\n", itos(e->hits), e->links, humanPeriod(now - e->created), humanSize(e->length), e->name); e = e->old; } } } return l; } #endif