libdap++  Updated for version 3.14.0
HTTPCacheTable.cc
Go to the documentation of this file.
1 
2 // -*- mode: c++; c-basic-offset:4 -*-
3 
4 // This file is part of libdap, A C++ implementation of the OPeNDAP Data
5 // Access Protocol.
6 
7 // Copyright (c) 2002,2003 OPeNDAP, Inc.
8 // Author: James Gallagher <jgallagher@opendap.org>
9 //
10 // This library is free software; you can redistribute it and/or
11 // modify it under the terms of the GNU Lesser General Public
12 // License as published by the Free Software Foundation; either
13 // version 2.1 of the License, or (at your option) any later version.
14 //
15 // This library is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 // Lesser General Public License for more details.
19 //
20 // You should have received a copy of the GNU Lesser General Public
21 // License along with this library; if not, write to the Free Software
22 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 //
24 // You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
25 
26 #include "config.h"
27 
28 // #define DODS_DEBUG
29 
30 // TODO: Remove unneeded includes.
31 
32 #include <pthread.h>
33 #include <limits.h>
34 #include <unistd.h> // for stat
35 #include <sys/types.h> // for stat and mkdir
36 #include <sys/stat.h>
37 
38 #include <cstring>
39 #include <iostream>
40 #include <sstream>
41 #include <algorithm>
42 #include <iterator>
43 #include <set>
44 
45 #include "Error.h"
46 #include "InternalErr.h"
47 #include "ResponseTooBigErr.h"
48 #ifndef WIN32
49 #include "SignalHandler.h"
50 #endif
52 #include "HTTPCacheTable.h"
53 #include "HTTPCacheMacros.h"
54 
55 #include "util_mit.h"
56 #include "debug.h"
57 
58 #ifdef WIN32
59 #include <direct.h>
60 #include <time.h>
61 #include <fcntl.h>
62 #define MKDIR(a,b) _mkdir((a))
63 #define REMOVE(a) do { \
64  int s = remove((a)); \
65  if (s != 0) \
66  throw InternalErr(__FILE__, __LINE__, "Cache error; could not remove file: " + long_to_string(s)); \
67  } while(0);
68 #define MKSTEMP(a) _open(_mktemp((a)),_O_CREAT,_S_IREAD|_S_IWRITE)
69 #define DIR_SEPARATOR_CHAR '\\'
70 #define DIR_SEPARATOR_STR "\\"
71 #else
72 #define MKDIR(a,b) mkdir((a), (b))
73 #define MKSTEMP(a) mkstemp((a))
74 #define DIR_SEPARATOR_CHAR '/'
75 #define DIR_SEPARATOR_STR "/"
76 #endif
77 
78 #define CACHE_META ".meta"
79 #define CACHE_INDEX ".index"
80 #define CACHE_EMPTY_ETAG "@cache@"
81 
82 #define NO_LM_EXPIRATION 24*3600 // 24 hours
83 #define MAX_LM_EXPIRATION 48*3600 // Max expiration from LM
84 
85 // If using LM to find the expiration then take 10% and no more than
86 // MAX_LM_EXPIRATION.
87 #ifndef LM_EXPIRATION
88 #define LM_EXPIRATION(t) (min((MAX_LM_EXPIRATION), static_cast<int>((t) / 10)))
89 #endif
90 
91 const int CACHE_TABLE_SIZE = 1499;
92 
93 using namespace std;
94 
95 namespace libdap {
96 
100 int
101 get_hash(const string &url)
102 {
103  int hash = 0;
104 
105  for (const char *ptr = url.c_str(); *ptr; ptr++)
106  hash = (int)((hash * 3 + (*(unsigned char *)ptr)) % CACHE_TABLE_SIZE);
107 
108  return hash;
109 }
110 
111 HTTPCacheTable::HTTPCacheTable(const string &cache_root, int block_size) :
112  d_cache_root(cache_root), d_block_size(block_size), d_current_size(0), d_new_entries(0)
113 {
114  d_cache_index = cache_root + CACHE_INDEX;
115 
116  d_cache_table = new CacheEntries*[CACHE_TABLE_SIZE];
117 
118  // Initialize the cache table.
119  for (int i = 0; i < CACHE_TABLE_SIZE; ++i)
120  d_cache_table[i] = 0;
121 
123 }
124 
128 static inline void
129 delete_cache_entry(HTTPCacheTable::CacheEntry *e)
130 {
131  DBG2(cerr << "Deleting CacheEntry: " << e << endl);
132  delete e;
133 }
134 
136 {
137  for (int i = 0; i < CACHE_TABLE_SIZE; ++i) {
138  HTTPCacheTable::CacheEntries *cp = get_cache_table()[i];
139  if (cp) {
140  // delete each entry
141  for_each(cp->begin(), cp->end(), delete_cache_entry);
142 
143  // now delete the vector that held the entries
144  delete get_cache_table()[i];
145  get_cache_table()[i] = 0;
146  }
147  }
148 
149  delete[] d_cache_table;
150 }
151 
159 class DeleteExpired : public unary_function<HTTPCacheTable::CacheEntry *&, void> {
160  time_t d_time;
161  HTTPCacheTable &d_table;
162 
163 public:
164  DeleteExpired(HTTPCacheTable &table, time_t t) :
165  d_time(t), d_table(table) {
166  if (!t)
167  d_time = time(0); // 0 == now
168  }
169 
170  void operator()(HTTPCacheTable::CacheEntry *&e) {
171  if (e && !e->readers && (e->freshness_lifetime
172  < (e->corrected_initial_age + (d_time - e->response_time)))) {
173  DBG(cerr << "Deleting expired cache entry: " << e->url << endl);
174  d_table.remove_cache_entry(e);
175  delete e; e = 0;
176  }
177  }
178 };
179 
180 // @param time base deletes againt this time, defaults to 0 (now)
182  // Walk through and delete all the expired entries.
183  for (int cnt = 0; cnt < CACHE_TABLE_SIZE; cnt++) {
184  HTTPCacheTable::CacheEntries *slot = get_cache_table()[cnt];
185  if (slot) {
186  for_each(slot->begin(), slot->end(), DeleteExpired(*this, time));
187  slot->erase(remove(slot->begin(), slot->end(),
188  static_cast<HTTPCacheTable::CacheEntry *>(0)), slot->end());
189  }
190  }
191 }
192 
199 class DeleteByHits : public unary_function<HTTPCacheTable::CacheEntry *&, void> {
200  HTTPCacheTable &d_table;
201  int d_hits;
202 
203 public:
204  DeleteByHits(HTTPCacheTable &table, int hits) :
205  d_table(table), d_hits(hits) {
206  }
207 
208  void operator()(HTTPCacheTable::CacheEntry *&e) {
209  if (e && !e->readers && e->hits <= d_hits) {
210  DBG(cerr << "Deleting cache entry: " << e->url << endl);
211  d_table.remove_cache_entry(e);
212  delete e; e = 0;
213  }
214  }
215 };
216 
217 void
219  for (int cnt = 0; cnt < CACHE_TABLE_SIZE; cnt++) {
220  if (get_cache_table()[cnt]) {
221  HTTPCacheTable::CacheEntries *slot = get_cache_table()[cnt];
222  for_each(slot->begin(), slot->end(), DeleteByHits(*this, hits));
223  slot->erase(remove(slot->begin(), slot->end(),
224  static_cast<HTTPCacheTable::CacheEntry*>(0)),
225  slot->end());
226 
227  }
228  }
229 }
230 
235 class DeleteBySize : public unary_function<HTTPCacheTable::CacheEntry *&, void> {
236  HTTPCacheTable &d_table;
237  unsigned int d_size;
238 
239 public:
240  DeleteBySize(HTTPCacheTable &table, unsigned int size) :
241  d_table(table), d_size(size) {
242  }
243 
244  void operator()(HTTPCacheTable::CacheEntry *&e) {
245  if (e && !e->readers && e->size > d_size) {
246  DBG(cerr << "Deleting cache entry: " << e->url << endl);
247  d_table.remove_cache_entry(e);
248  delete e; e = 0;
249  }
250  }
251 };
252 
253 void HTTPCacheTable::delete_by_size(unsigned int size) {
254  for (int cnt = 0; cnt < CACHE_TABLE_SIZE; cnt++) {
255  if (get_cache_table()[cnt]) {
256  HTTPCacheTable::CacheEntries *slot = get_cache_table()[cnt];
257  for_each(slot->begin(), slot->end(), DeleteBySize(*this, size));
258  slot->erase(remove(slot->begin(), slot->end(),
259  static_cast<HTTPCacheTable::CacheEntry*>(0)),
260  slot->end());
261 
262  }
263  }
264 }
265 
272 
279 bool
281 {
282  d_new_entries = 0;
283 
284  return (REMOVE_BOOL(d_cache_index.c_str()) == 0);
285 }
286 
295 bool
297 {
298  FILE *fp = fopen(d_cache_index.c_str(), "r");
299  // If the cache index can't be opened that's OK; start with an empty
300  // cache. 09/05/02 jhrg
301  if (!fp) {
302  return false;
303  }
304 
305  char line[1024];
306  while (!feof(fp) && fgets(line, 1024, fp)) {
308  DBG2(cerr << line << endl);
309  }
310 
311  int res = fclose(fp) ;
312  if (res) {
313  DBG(cerr << "HTTPCache::cache_index_read - Failed to close " << (void *)fp << endl);
314  }
315 
316  d_new_entries = 0;
317 
318  return true;
319 }
320 
330 {
331  // Read the line and create the cache object
333  istringstream iss(line);
334  iss >> entry->url;
335  iss >> entry->cachename;
336 
337  iss >> entry->etag;
338  if (entry->etag == CACHE_EMPTY_ETAG)
339  entry->etag = "";
340 
341  iss >> entry->lm;
342  iss >> entry->expires;
343  iss >> entry->size;
344  iss >> entry->range; // range is not used. 10/02/02 jhrg
345 
346  iss >> entry->hash;
347  iss >> entry->hits;
348  iss >> entry->freshness_lifetime;
349  iss >> entry->response_time;
350  iss >> entry->corrected_initial_age;
351 
352  iss >> entry->must_revalidate;
353 
354  return entry;
355 }
356 
359 class WriteOneCacheEntry :
360  public unary_function<HTTPCacheTable::CacheEntry *, void>
361 {
362 
363  FILE *d_fp;
364 
365 public:
366  WriteOneCacheEntry(FILE *fp) : d_fp(fp)
367  {}
368 
369  void operator()(HTTPCacheTable::CacheEntry *e)
370  {
371  if (e && fprintf(d_fp,
372  "%s %s %s %ld %ld %ld %c %d %d %ld %ld %ld %c\r\n",
373  e->url.c_str(),
374  e->cachename.c_str(),
375  e->etag == "" ? CACHE_EMPTY_ETAG : e->etag.c_str(),
376  (long)(e->lm),
377  (long)(e->expires),
378  e->size,
379  e->range ? '1' : '0', // not used. 10/02/02 jhrg
380  e->hash,
381  e->hits,
382  (long)(e->freshness_lifetime),
383  (long)(e->response_time),
384  (long)(e->corrected_initial_age),
385  e->must_revalidate ? '1' : '0') < 0)
386  throw Error("Cache Index. Error writing cache index\n");
387  }
388 };
389 
399 void
401 {
402  DBG(cerr << "Cache Index. Writing index " << d_cache_index << endl);
403 
404  // Open the file for writing.
405  FILE * fp = NULL;
406  if ((fp = fopen(d_cache_index.c_str(), "wb")) == NULL) {
407  throw Error(string("Cache Index. Can't open `") + d_cache_index
408  + string("' for writing"));
409  }
410 
411  // Walk through the list and write it out. The format is really
412  // simple as we keep it all in ASCII.
413 
414  for (int cnt = 0; cnt < CACHE_TABLE_SIZE; cnt++) {
415  HTTPCacheTable::CacheEntries *cp = get_cache_table()[cnt];
416  if (cp)
417  for_each(cp->begin(), cp->end(), WriteOneCacheEntry(fp));
418  }
419 
420  /* Done writing */
421  int res = fclose(fp);
422  if (res) {
423  DBG(cerr << "HTTPCache::cache_index_write - Failed to close "
424  << (void *)fp << endl);
425  }
426 
427  d_new_entries = 0;
428 }
429 
431 
444 string
446 {
447  struct stat stat_info;
448  ostringstream path;
449 
450  path << d_cache_root << hash;
451  string p = path.str();
452 
453  if (stat(p.c_str(), &stat_info) == -1) {
454  DBG2(cerr << "Cache....... Create dir " << p << endl);
455  if (MKDIR(p.c_str(), 0777) < 0) {
456  DBG2(cerr << "Cache....... Can't create..." << endl);
457  throw Error("Could not create cache slot to hold response! Check the write permissions on your disk cache directory. Cache root: " + d_cache_root + ".");
458  }
459  }
460  else {
461  DBG2(cerr << "Cache....... Directory " << p << " already exists"
462  << endl);
463  }
464 
465  return p;
466 }
467 
482 void
484 {
485  string hash_dir = create_hash_directory(entry->hash);
486 #ifdef WIN32
487  hash_dir += "\\dodsXXXXXX";
488 #else
489  hash_dir += "/dodsXXXXXX"; // mkstemp uses six characters.
490 #endif
491 
492  // mkstemp uses the storage passed to it; must be writable and local.
493  // char *templat = new char[hash_dir.size() + 1];
494  vector<char> templat(hash_dir.size() + 1);
495  strncpy(&templat[0], hash_dir.c_str(), hash_dir.size() + 1);
496 
497  // Open truncated for update. NB: mkstemp() returns a file descriptor.
498  // man mkstemp says "... The file is opened with the O_EXCL flag,
499  // guaranteeing that when mkstemp returns successfully we are the only
500  // user." 09/19/02 jhrg
501 #ifndef WIN32
502  // Make sure that temp files are accessible only by the owner.
503  umask(077);
504 #endif
505  int fd = MKSTEMP(&templat[0]); // fd mode is 666 or 600 (Unix)
506  if (fd < 0) {
507  // delete[] templat; templat = 0;
508  close(fd);
509  throw Error("The HTTP Cache could not create a file to hold the response; it will not be cached.");
510  }
511 
512  entry->cachename = &templat[0];
513  // delete[] templat; templat = 0;
514  close(fd);
515 }
516 
517 
519 static inline int
520 entry_disk_space(int size, unsigned int block_size)
521 {
522  unsigned int num_of_blocks = (size + block_size) / block_size;
523 
524  DBG(cerr << "size: " << size << ", block_size: " << block_size
525  << ", num_of_blocks: " << num_of_blocks << endl);
526 
527  return num_of_blocks * block_size;
528 }
529 
533 
539 void
541 {
542  int hash = entry->hash;
543  if (hash > CACHE_TABLE_SIZE-1 || hash < 0)
544  throw InternalErr(__FILE__, __LINE__, "Hash value too large!");
545 
546  if (!d_cache_table[hash])
547  d_cache_table[hash] = new CacheEntries;
548 
549  d_cache_table[hash]->push_back(entry);
550 
551  DBG(cerr << "add_entry_to_cache_table, current_size: " << d_current_size
552  << ", entry->size: " << entry->size << ", block size: " << d_block_size
553  << endl);
554 
555  d_current_size += entry_disk_space(entry->size, d_block_size);
556 
557  DBG(cerr << "add_entry_to_cache_table, current_size: " << d_current_size << endl);
558 
560 }
561 
566 HTTPCacheTable::get_locked_entry_from_cache_table(const string &url) /*const*/
567 {
568  return get_locked_entry_from_cache_table(get_hash(url), url);
569 }
570 
579 HTTPCacheTable::get_locked_entry_from_cache_table(int hash, const string &url) /*const*/
580 {
581  DBG(cerr << "url: " << url << "; hash: " << hash << endl);
582  DBG(cerr << "d_cache_table: " << hex << d_cache_table << dec << endl);
583  if (d_cache_table[hash]) {
584  CacheEntries *cp = d_cache_table[hash];
585  for (CacheEntriesIter i = cp->begin(); i != cp->end(); ++i) {
586  // Must test *i because perform_garbage_collection may have
587  // removed this entry; the CacheEntry will then be null.
588  if ((*i) && (*i)->url == url) {
589  (*i)->lock_read_response(); // Lock the response
590  return *i;
591  }
592  }
593  }
594 
595  return 0;
596 }
597 
604 HTTPCacheTable::CacheEntry *
606 {
607  int hash = get_hash(url);
608  if (d_cache_table[hash]) {
609  CacheEntries *cp = d_cache_table[hash];
610  for (CacheEntriesIter i = cp->begin(); i != cp->end(); ++i) {
611  // Must test *i because perform_garbage_collection may have
612  // removed this entry; the CacheEntry will then be null.
613  if ((*i) && (*i)->url == url) {
614  (*i)->lock_write_response(); // Lock the response
615  return *i;
616  }
617  }
618  }
619 
620  return 0;
621 }
622 
630 void
632 {
633  // This should never happen; all calls to this method are protected by
634  // the caller, hence the InternalErr.
635  if (entry->readers)
636  throw InternalErr(__FILE__, __LINE__, "Tried to delete a cache entry that is in use.");
637 
638  REMOVE(entry->cachename.c_str());
639  REMOVE(string(entry->cachename + CACHE_META).c_str());
640 
641  DBG(cerr << "remove_cache_entry, current_size: " << get_current_size() << endl);
642 
643  unsigned int eds = entry_disk_space(entry->size, get_block_size());
644  set_current_size((eds > get_current_size()) ? 0 : get_current_size() - eds);
645 
646  DBG(cerr << "remove_cache_entry, current_size: " << get_current_size() << endl);
647 }
648 
651 class DeleteCacheEntry: public unary_function<HTTPCacheTable::CacheEntry *&, void>
652 {
653  string d_url;
654  HTTPCacheTable *d_cache_table;
655 
656 public:
657  DeleteCacheEntry(HTTPCacheTable *c, const string &url)
658  : d_url(url), d_cache_table(c)
659  {}
660 
661  void operator()(HTTPCacheTable::CacheEntry *&e)
662  {
663  if (e && e->url == d_url) {
664  e->lock_write_response();
665  d_cache_table->remove_cache_entry(e);
666  e->unlock_write_response();
667  delete e; e = 0;
668  }
669  }
670 };
671 
678 void
680 {
681  int hash = get_hash(url);
682  if (d_cache_table[hash]) {
683  CacheEntries *cp = d_cache_table[hash];
684  for_each(cp->begin(), cp->end(), DeleteCacheEntry(this, url));
685  cp->erase(remove(cp->begin(), cp->end(), static_cast<HTTPCacheTable::CacheEntry*>(0)),
686  cp->end());
687  }
688 }
689 
692 class DeleteUnlockedCacheEntry: public unary_function<HTTPCacheTable::CacheEntry *&, void> {
693  HTTPCacheTable &d_table;
694 
695 public:
696  DeleteUnlockedCacheEntry(HTTPCacheTable &t) :
697  d_table(t)
698  {
699  }
700  void operator()(HTTPCacheTable::CacheEntry *&e)
701  {
702  if (e) {
703  d_table.remove_cache_entry(e);
704  delete e;
705  e = 0;
706  }
707  }
708 };
709 
711 {
712  // Walk through the cache table and, for every entry in the cache, delete
713  // it on disk and in the cache table.
714  for (int cnt = 0; cnt < CACHE_TABLE_SIZE; cnt++) {
715  HTTPCacheTable::CacheEntries *slot = get_cache_table()[cnt];
716  if (slot) {
717  for_each(slot->begin(), slot->end(), DeleteUnlockedCacheEntry(*this));
718  slot->erase(remove(slot->begin(), slot->end(), static_cast<HTTPCacheTable::CacheEntry *> (0)), slot->end());
719  }
720  }
721 
723 }
724 
738 void
739 HTTPCacheTable::calculate_time(HTTPCacheTable::CacheEntry *entry, int default_expiration, time_t request_time)
740 {
741  entry->response_time = time(NULL);
742  time_t apparent_age = max(0, static_cast<int>(entry->response_time - entry->date));
743  time_t corrected_received_age = max(apparent_age, entry->age);
744  time_t response_delay = entry->response_time - request_time;
745  entry->corrected_initial_age = corrected_received_age + response_delay;
746 
747  // Estimate an expires time using the max-age and expires time. If we
748  // don't have an explicit expires time then set it to 10% of the LM date
749  // (although max 24 h). If no LM date is available then use 24 hours.
750  time_t freshness_lifetime = entry->max_age;
751  if (freshness_lifetime < 0) {
752  if (entry->expires < 0) {
753  if (entry->lm < 0) {
754  freshness_lifetime = default_expiration;
755  }
756  else {
757  freshness_lifetime = LM_EXPIRATION(entry->date - entry->lm);
758  }
759  }
760  else
761  freshness_lifetime = entry->expires - entry->date;
762  }
763 
764  entry->freshness_lifetime = max(0, static_cast<int>(freshness_lifetime));
765 
766  DBG2(cerr << "Cache....... Received Age " << entry->age
767  << ", corrected " << entry->corrected_initial_age
768  << ", freshness lifetime " << entry->freshness_lifetime << endl);
769 }
770 
782 void HTTPCacheTable::parse_headers(HTTPCacheTable::CacheEntry *entry, unsigned long max_entry_size,
783  const vector<string> &headers)
784 {
785  vector<string>::const_iterator i;
786  for (i = headers.begin(); i != headers.end(); ++i) {
787  // skip a blank header.
788  if ((*i).empty())
789  continue;
790 
791  string::size_type colon = (*i).find(':');
792 
793  // skip a header with no colon in it.
794  if (colon == string::npos)
795  continue;
796 
797  string header = (*i).substr(0, (*i).find(':'));
798  string value = (*i).substr((*i).find(": ") + 2);
799  DBG2(cerr << "Header: " << header << endl);DBG2(cerr << "Value: " << value << endl);
800 
801  if (header == "ETag") {
802  entry->etag = value;
803  }
804  else if (header == "Last-Modified") {
805  entry->lm = parse_time(value.c_str());
806  }
807  else if (header == "Expires") {
808  entry->expires = parse_time(value.c_str());
809  }
810  else if (header == "Date") {
811  entry->date = parse_time(value.c_str());
812  }
813  else if (header == "Age") {
814  entry->age = parse_time(value.c_str());
815  }
816  else if (header == "Content-Length") {
817  unsigned long clength = strtoul(value.c_str(), 0, 0);
818  if (clength > max_entry_size)
819  entry->set_no_cache(true);
820  }
821  else if (header == "Cache-Control") {
822  // Ignored Cache-Control values: public, private, no-transform,
823  // proxy-revalidate, s-max-age. These are used by shared caches.
824  // See section 14.9 of RFC 2612. 10/02/02 jhrg
825  if (value == "no-cache" || value == "no-store")
826  // Note that we *can* store a 'no-store' response in volatile
827  // memory according to RFC 2616 (section 14.9.2) but those
828  // will be rare coming from DAP servers. 10/02/02 jhrg
829  entry->set_no_cache(true);
830  else if (value == "must-revalidate")
831  entry->must_revalidate = true;
832  else if (value.find("max-age") != string::npos) {
833  string max_age = value.substr(value.find("=" + 1));
834  entry->max_age = parse_time(max_age.c_str());
835  }
836  }
837  }
838 }
839 
841 
842 // @TODO Change name to record locked response
844  entry->hits++; // Mark hit
845  d_locked_entries[body] = entry; // record lock, see release_cached_r...
846 }
847 
849 
850  HTTPCacheTable::CacheEntry *entry = d_locked_entries[body];
851  if (!entry)
852  throw InternalErr("There is no cache entry for the response given.");
853 
854  d_locked_entries.erase(body);
855  entry->unlock_read_response();
856 
857  if (entry->readers < 0)
858  throw InternalErr("An unlocked entry was released");
859 }
860 
862  return !d_locked_entries.empty();
863 }
864 
865 } // namespace libdap
void remove_cache_entry(HTTPCacheTable::CacheEntry *entry)
const int CACHE_TABLE_SIZE
time_t parse_time(const char *str, bool expand)
Definition: util_mit.cc:129
#define REMOVE_BOOL(a)
void create_location(CacheEntry *entry)
void parse_headers(HTTPCacheTable::CacheEntry *entry, unsigned long max_entry_size, const vector< string > &headers)
void add_entry_to_cache_table(CacheEntry *entry)
vector< CacheEntry * > CacheEntries
void calculate_time(HTTPCacheTable::CacheEntry *entry, int default_expiration, time_t request_time)
void delete_by_size(unsigned int size)
CacheEntries::iterator CacheEntriesIter
#define DBG2(x)
Definition: debug.h:73
A class for software fault reporting.
Definition: InternalErr.h:64
#define DBG(x)
Definition: debug.h:58
void set_current_size(unsigned long sz)
int get_hash(const string &url)
#define CACHE_INDEX
#define MKDIR(a, b)
void bind_entry_to_data(CacheEntry *entry, FILE *body)
#define MKSTEMP(a)
void delete_expired_entries(time_t time=0)
#define LM_EXPIRATION(t)
unsigned long get_current_size() const
CacheEntry * cache_index_parse_line(const char *line)
unsigned int get_block_size() const
void remove_entry_from_cache_table(const string &url)
#define REMOVE(a)
CacheEntry * get_write_locked_entry_from_cache_table(const string &url)
#define CACHE_EMPTY_ETAG
void delete_by_hits(int hits)
A class for error processing.
Definition: Error.h:90
string create_hash_directory(int hash)
#define CACHE_META
void uncouple_entry_from_data(FILE *body)