libdap++  Updated for version 3.11.7
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 REMOVE(a) remove((a))
74 #define MKSTEMP(a) mkstemp((a))
75 #define DIR_SEPARATOR_CHAR '/'
76 #define DIR_SEPARATOR_STR "/"
77 #endif
78 
79 #define CACHE_META ".meta"
80 #define CACHE_INDEX ".index"
81 #define CACHE_EMPTY_ETAG "@cache@"
82 
83 #define NO_LM_EXPIRATION 24*3600 // 24 hours
84 #define MAX_LM_EXPIRATION 48*3600 // Max expiration from LM
85 
86 // If using LM to find the expiration then take 10% and no more than
87 // MAX_LM_EXPIRATION.
88 #ifndef LM_EXPIRATION
89 #define LM_EXPIRATION(t) (min((MAX_LM_EXPIRATION), static_cast<int>((t) / 10)))
90 #endif
91 
92 const int CACHE_TABLE_SIZE = 1499;
93 
94 using namespace std;
95 
96 namespace libdap {
97 
101 int
102 get_hash(const string &url)
103 {
104  int hash = 0;
105 
106  for (const char *ptr = url.c_str(); *ptr; ptr++)
107  hash = (int)((hash * 3 + (*(unsigned char *)ptr)) % CACHE_TABLE_SIZE);
108 
109  return hash;
110 }
111 
112 HTTPCacheTable::HTTPCacheTable(const string &cache_root, int block_size) :
113  d_cache_root(cache_root), d_block_size(block_size), d_current_size(0), d_new_entries(0)
114 {
115  d_cache_index = cache_root + CACHE_INDEX;
116 
117  d_cache_table = new CacheEntries*[CACHE_TABLE_SIZE];
118 
119  // Initialize the cache table.
120  for (int i = 0; i < CACHE_TABLE_SIZE; ++i)
121  d_cache_table[i] = 0;
122 
124 }
125 
129 static inline void
130 delete_cache_entry(HTTPCacheTable::CacheEntry *e)
131 {
132  DBG2(cerr << "Deleting CacheEntry: " << e << endl);
133  delete e;
134 }
135 
137 {
138  for (int i = 0; i < CACHE_TABLE_SIZE; ++i) {
139  HTTPCacheTable::CacheEntries *cp = get_cache_table()[i];
140  if (cp) {
141  // delete each entry
142  for_each(cp->begin(), cp->end(), delete_cache_entry);
143 
144  // now delete the vector that held the entries
145  delete get_cache_table()[i];
146  get_cache_table()[i] = 0;
147  }
148  }
149 
150  delete[] d_cache_table;
151 }
152 
160 class DeleteExpired : public unary_function<HTTPCacheTable::CacheEntry *&, void> {
161  time_t d_time;
162  HTTPCacheTable &d_table;
163 
164 public:
165  DeleteExpired(HTTPCacheTable &table, time_t t) :
166  d_time(t), d_table(table) {
167  if (!t)
168  d_time = time(0); // 0 == now
169  }
170 
171  void operator()(HTTPCacheTable::CacheEntry *&e) {
172  if (e && !e->readers && (e->freshness_lifetime
173  < (e->corrected_initial_age + (d_time - e->response_time)))) {
174  DBG(cerr << "Deleting expired cache entry: " << e->url << endl);
175  d_table.remove_cache_entry(e);
176  delete e; e = 0;
177  }
178  }
179 };
180 
181 // @param time base deletes againt this time, defaults to 0 (now)
183  // Walk through and delete all the expired entries.
184  for (int cnt = 0; cnt < CACHE_TABLE_SIZE; cnt++) {
185  HTTPCacheTable::CacheEntries *slot = get_cache_table()[cnt];
186  if (slot) {
187  for_each(slot->begin(), slot->end(), DeleteExpired(*this, time));
188  slot->erase(remove(slot->begin(), slot->end(),
189  static_cast<HTTPCacheTable::CacheEntry *>(0)), slot->end());
190  }
191  }
192 }
193 
200 class DeleteByHits : public unary_function<HTTPCacheTable::CacheEntry *&, void> {
201  HTTPCacheTable &d_table;
202  int d_hits;
203 
204 public:
205  DeleteByHits(HTTPCacheTable &table, int hits) :
206  d_table(table), d_hits(hits) {
207  }
208 
209  void operator()(HTTPCacheTable::CacheEntry *&e) {
210  if (e && !e->readers && e->hits <= d_hits) {
211  DBG(cerr << "Deleting cache entry: " << e->url << endl);
212  d_table.remove_cache_entry(e);
213  delete e; e = 0;
214  }
215  }
216 };
217 
218 void
220  for (int cnt = 0; cnt < CACHE_TABLE_SIZE; cnt++) {
221  if (get_cache_table()[cnt]) {
222  HTTPCacheTable::CacheEntries *slot = get_cache_table()[cnt];
223  for_each(slot->begin(), slot->end(), DeleteByHits(*this, hits));
224  slot->erase(remove(slot->begin(), slot->end(),
225  static_cast<HTTPCacheTable::CacheEntry*>(0)),
226  slot->end());
227 
228  }
229  }
230 }
231 
236 class DeleteBySize : public unary_function<HTTPCacheTable::CacheEntry *&, void> {
237  HTTPCacheTable &d_table;
238  unsigned int d_size;
239 
240 public:
241  DeleteBySize(HTTPCacheTable &table, unsigned int size) :
242  d_table(table), d_size(size) {
243  }
244 
245  void operator()(HTTPCacheTable::CacheEntry *&e) {
246  if (e && !e->readers && e->size > d_size) {
247  DBG(cerr << "Deleting cache entry: " << e->url << endl);
248  d_table.remove_cache_entry(e);
249  delete e; e = 0;
250  }
251  }
252 };
253 
254 void HTTPCacheTable::delete_by_size(unsigned int size) {
255  for (int cnt = 0; cnt < CACHE_TABLE_SIZE; cnt++) {
256  if (get_cache_table()[cnt]) {
257  HTTPCacheTable::CacheEntries *slot = get_cache_table()[cnt];
258  for_each(slot->begin(), slot->end(), DeleteBySize(*this, size));
259  slot->erase(remove(slot->begin(), slot->end(),
260  static_cast<HTTPCacheTable::CacheEntry*>(0)),
261  slot->end());
262 
263  }
264  }
265 }
266 
273 
280 bool
282 {
283  d_new_entries = 0;
284 
285  return (REMOVE_BOOL(d_cache_index.c_str()) == 0);
286 }
287 
296 bool
298 {
299  FILE *fp = fopen(d_cache_index.c_str(), "r");
300  // If the cache index can't be opened that's OK; start with an empty
301  // cache. 09/05/02 jhrg
302  if (!fp) {
303  return false;
304  }
305 
306  char line[1024];
307  while (!feof(fp) && fgets(line, 1024, fp)) {
309  DBG2(cerr << line << endl);
310  }
311 
312  int res = fclose(fp) ;
313  if (res) {
314  DBG(cerr << "HTTPCache::cache_index_read - Failed to close " << (void *)fp << endl);
315  }
316 
317  d_new_entries = 0;
318 
319  return true;
320 }
321 
331 {
332  // Read the line and create the cache object
334  istringstream iss(line);
335  iss >> entry->url;
336  iss >> entry->cachename;
337 
338  iss >> entry->etag;
339  if (entry->etag == CACHE_EMPTY_ETAG)
340  entry->etag = "";
341 
342  iss >> entry->lm;
343  iss >> entry->expires;
344  iss >> entry->size;
345  iss >> entry->range; // range is not used. 10/02/02 jhrg
346 
347  iss >> entry->hash;
348  iss >> entry->hits;
349  iss >> entry->freshness_lifetime;
350  iss >> entry->response_time;
351  iss >> entry->corrected_initial_age;
352 
353  iss >> entry->must_revalidate;
354 
355  return entry;
356 }
357 
360 class WriteOneCacheEntry :
361  public unary_function<HTTPCacheTable::CacheEntry *, void>
362 {
363 
364  FILE *d_fp;
365 
366 public:
367  WriteOneCacheEntry(FILE *fp) : d_fp(fp)
368  {}
369 
370  void operator()(HTTPCacheTable::CacheEntry *e)
371  {
372  if (e && fprintf(d_fp,
373  "%s %s %s %ld %ld %ld %c %d %d %ld %ld %ld %c\r\n",
374  e->url.c_str(),
375  e->cachename.c_str(),
376  e->etag == "" ? CACHE_EMPTY_ETAG : e->etag.c_str(),
377  (long)(e->lm),
378  (long)(e->expires),
379  e->size,
380  e->range ? '1' : '0', // not used. 10/02/02 jhrg
381  e->hash,
382  e->hits,
383  (long)(e->freshness_lifetime),
384  (long)(e->response_time),
385  (long)(e->corrected_initial_age),
386  e->must_revalidate ? '1' : '0') < 0)
387  throw Error("Cache Index. Error writing cache index\n");
388  }
389 };
390 
400 void
402 {
403  DBG(cerr << "Cache Index. Writing index " << d_cache_index << endl);
404 
405  // Open the file for writing.
406  FILE * fp = NULL;
407  if ((fp = fopen(d_cache_index.c_str(), "wb")) == NULL) {
408  throw Error(string("Cache Index. Can't open `") + d_cache_index
409  + string("' for writing"));
410  }
411 
412  // Walk through the list and write it out. The format is really
413  // simple as we keep it all in ASCII.
414 
415  for (int cnt = 0; cnt < CACHE_TABLE_SIZE; cnt++) {
416  HTTPCacheTable::CacheEntries *cp = get_cache_table()[cnt];
417  if (cp)
418  for_each(cp->begin(), cp->end(), WriteOneCacheEntry(fp));
419  }
420 
421  /* Done writing */
422  int res = fclose(fp);
423  if (res) {
424  DBG(cerr << "HTTPCache::cache_index_write - Failed to close "
425  << (void *)fp << endl);
426  }
427 
428  d_new_entries = 0;
429 }
430 
432 
445 string
447 {
448  struct stat stat_info;
449  ostringstream path;
450 
451  path << d_cache_root << hash;
452  string p = path.str();
453 
454  if (stat(p.c_str(), &stat_info) == -1) {
455  DBG2(cerr << "Cache....... Create dir " << p << endl);
456  if (MKDIR(p.c_str(), 0777) < 0) {
457  DBG2(cerr << "Cache....... Can't create..." << endl);
458  throw Error("Could not create cache slot to hold response! Check the write permissions on your disk cache directory. Cache root: " + d_cache_root + ".");
459  }
460  }
461  else {
462  DBG2(cerr << "Cache....... Directory " << p << " already exists"
463  << endl);
464  }
465 
466  return p;
467 }
468 
483 void
485 {
486  string hash_dir = create_hash_directory(entry->hash);
487 #ifdef WIN32
488  hash_dir += "\\dodsXXXXXX";
489 #else
490  hash_dir += "/dodsXXXXXX"; // mkstemp uses six characters.
491 #endif
492 
493  // mkstemp uses the storage passed to it; must be writable and local.
494  // char *templat = new char[hash_dir.size() + 1];
495  vector<char> templat(hash_dir.size() + 1);
496  strncpy(&templat[0], hash_dir.c_str(), hash_dir.size() + 1);
497 
498  // Open truncated for update. NB: mkstemp() returns a file descriptor.
499  // man mkstemp says "... The file is opened with the O_EXCL flag,
500  // guaranteeing that when mkstemp returns successfully we are the only
501  // user." 09/19/02 jhrg
502 #ifndef WIN32
503  // Make sure that temp files are accessible only by the owner.
504  umask(077);
505 #endif
506  int fd = MKSTEMP(&templat[0]); // fd mode is 666 or 600 (Unix)
507  if (fd < 0) {
508  // delete[] templat; templat = 0;
509  close(fd);
510  throw Error("The HTTP Cache could not create a file to hold the response; it will not be cached.");
511  }
512 
513  entry->cachename = &templat[0];
514  // delete[] templat; templat = 0;
515  close(fd);
516 }
517 
518 
520 static inline int
521 entry_disk_space(int size, unsigned int block_size)
522 {
523  unsigned int num_of_blocks = (size + block_size) / block_size;
524 
525  DBG(cerr << "size: " << size << ", block_size: " << block_size
526  << ", num_of_blocks: " << num_of_blocks << endl);
527 
528  return num_of_blocks * block_size;
529 }
530 
534 
540 void
542 {
543  unsigned int hash = entry->hash;
544  if (hash >= CACHE_TABLE_SIZE)
545  throw InternalErr(__FILE__, __LINE__, "Hash value too large!");
546 
547  if (!d_cache_table[hash])
548  d_cache_table[hash] = new CacheEntries;
549 
550  d_cache_table[hash]->push_back(entry);
551 
552  DBG(cerr << "add_entry_to_cache_table, current_size: " << d_current_size
553  << ", entry->size: " << entry->size << ", block size: " << d_block_size
554  << endl);
555 
556  d_current_size += entry_disk_space(entry->size, d_block_size);
557 
558  DBG(cerr << "add_entry_to_cache_table, current_size: " << d_current_size << endl);
559 
561 }
562 
567 HTTPCacheTable::get_locked_entry_from_cache_table(const string &url) /*const*/
568 {
569  return get_locked_entry_from_cache_table(get_hash(url), url);
570 }
571 
580 HTTPCacheTable::get_locked_entry_from_cache_table(int hash, const string &url) /*const*/
581 {
582  DBG(cerr << "url: " << url << "; hash: " << hash << endl);
583  DBG(cerr << "d_cache_table: " << hex << d_cache_table << dec << endl);
584  if (d_cache_table[hash]) {
585  CacheEntries *cp = d_cache_table[hash];
586  for (CacheEntriesIter i = cp->begin(); i != cp->end(); ++i) {
587  // Must test *i because perform_garbage_collection may have
588  // removed this entry; the CacheEntry will then be null.
589  if ((*i) && (*i)->url == url) {
590  (*i)->lock_read_response(); // Lock the response
591  return *i;
592  }
593  }
594  }
595 
596  return 0;
597 }
598 
605 HTTPCacheTable::CacheEntry *
607 {
608  int hash = get_hash(url);
609  if (d_cache_table[hash]) {
610  CacheEntries *cp = d_cache_table[hash];
611  for (CacheEntriesIter i = cp->begin(); i != cp->end(); ++i) {
612  // Must test *i because perform_garbage_collection may have
613  // removed this entry; the CacheEntry will then be null.
614  if ((*i) && (*i)->url == url) {
615  (*i)->lock_write_response(); // Lock the response
616  return *i;
617  }
618  }
619  }
620 
621  return 0;
622 }
623 
631 void
633 {
634  // This should never happen; all calls to this method are protected by
635  // the caller, hence the InternalErr.
636  if (entry->readers)
637  throw InternalErr(__FILE__, __LINE__, "Tried to delete a cache entry that is in use.");
638 
639  REMOVE(entry->cachename.c_str());
640  REMOVE(string(entry->cachename + CACHE_META).c_str());
641 
642  DBG(cerr << "remove_cache_entry, current_size: " << get_current_size() << endl);
643 
644  unsigned int eds = entry_disk_space(entry->size, get_block_size());
645  set_current_size((eds > get_current_size()) ? 0 : get_current_size() - eds);
646 
647  DBG(cerr << "remove_cache_entry, current_size: " << get_current_size() << endl);
648 }
649 
652 class DeleteCacheEntry: public unary_function<HTTPCacheTable::CacheEntry *&, void>
653 {
654  string d_url;
655  HTTPCacheTable *d_cache_table;
656 
657 public:
658  DeleteCacheEntry(HTTPCacheTable *c, const string &url)
659  : d_url(url), d_cache_table(c)
660  {}
661 
662  void operator()(HTTPCacheTable::CacheEntry *&e)
663  {
664  if (e && e->url == d_url) {
665  e->lock_write_response();
666  d_cache_table->remove_cache_entry(e);
667  e->unlock_write_response();
668  delete e; e = 0;
669  }
670  }
671 };
672 
679 void
681 {
682  int hash = get_hash(url);
683  if (d_cache_table[hash]) {
684  CacheEntries *cp = d_cache_table[hash];
685  for_each(cp->begin(), cp->end(), DeleteCacheEntry(this, url));
686  cp->erase(remove(cp->begin(), cp->end(), static_cast<HTTPCacheTable::CacheEntry*>(0)),
687  cp->end());
688  }
689 }
690 
693 class DeleteUnlockedCacheEntry: public unary_function<HTTPCacheTable::CacheEntry *&, void> {
694  HTTPCacheTable &d_table;
695 
696 public:
697  DeleteUnlockedCacheEntry(HTTPCacheTable &t) :
698  d_table(t)
699  {
700  }
701  void operator()(HTTPCacheTable::CacheEntry *&e)
702  {
703  if (e) {
704  d_table.remove_cache_entry(e);
705  delete e;
706  e = 0;
707  }
708  }
709 };
710 
712 {
713  // Walk through the cache table and, for every entry in the cache, delete
714  // it on disk and in the cache table.
715  for (int cnt = 0; cnt < CACHE_TABLE_SIZE; cnt++) {
716  HTTPCacheTable::CacheEntries *slot = get_cache_table()[cnt];
717  if (slot) {
718  for_each(slot->begin(), slot->end(), DeleteUnlockedCacheEntry(*this));
719  slot->erase(remove(slot->begin(), slot->end(), static_cast<HTTPCacheTable::CacheEntry *> (0)), slot->end());
720  }
721  }
722 
724 }
725 
739 void
740 HTTPCacheTable::calculate_time(HTTPCacheTable::CacheEntry *entry, int default_expiration, time_t request_time)
741 {
742  entry->response_time = time(NULL);
743  time_t apparent_age = max(0, static_cast<int>(entry->response_time - entry->date));
744  time_t corrected_received_age = max(apparent_age, entry->age);
745  time_t response_delay = entry->response_time - request_time;
746  entry->corrected_initial_age = corrected_received_age + response_delay;
747 
748  // Estimate an expires time using the max-age and expires time. If we
749  // don't have an explicit expires time then set it to 10% of the LM date
750  // (although max 24 h). If no LM date is available then use 24 hours.
751  time_t freshness_lifetime = entry->max_age;
752  if (freshness_lifetime < 0) {
753  if (entry->expires < 0) {
754  if (entry->lm < 0) {
755  freshness_lifetime = default_expiration;
756  }
757  else {
758  freshness_lifetime = LM_EXPIRATION(entry->date - entry->lm);
759  }
760  }
761  else
762  freshness_lifetime = entry->expires - entry->date;
763  }
764 
765  entry->freshness_lifetime = max(0, static_cast<int>(freshness_lifetime));
766 
767  DBG2(cerr << "Cache....... Received Age " << entry->age
768  << ", corrected " << entry->corrected_initial_age
769  << ", freshness lifetime " << entry->freshness_lifetime << endl);
770 }
771 
783 void HTTPCacheTable::parse_headers(HTTPCacheTable::CacheEntry *entry, unsigned long max_entry_size,
784  const vector<string> &headers)
785 {
786  vector<string>::const_iterator i;
787  for (i = headers.begin(); i != headers.end(); ++i) {
788  // skip a blank header.
789  if ((*i).empty())
790  continue;
791 
792  string::size_type colon = (*i).find(':');
793 
794  // skip a header with no colon in it.
795  if (colon == string::npos)
796  continue;
797 
798  string header = (*i).substr(0, (*i).find(':'));
799  string value = (*i).substr((*i).find(": ") + 2);
800  DBG2(cerr << "Header: " << header << endl);DBG2(cerr << "Value: " << value << endl);
801 
802  if (header == "ETag") {
803  entry->etag = value;
804  }
805  else if (header == "Last-Modified") {
806  entry->lm = parse_time(value.c_str());
807  }
808  else if (header == "Expires") {
809  entry->expires = parse_time(value.c_str());
810  }
811  else if (header == "Date") {
812  entry->date = parse_time(value.c_str());
813  }
814  else if (header == "Age") {
815  entry->age = parse_time(value.c_str());
816  }
817  else if (header == "Content-Length") {
818  unsigned long clength = strtoul(value.c_str(), 0, 0);
819  if (clength > max_entry_size)
820  entry->set_no_cache(true);
821  }
822  else if (header == "Cache-Control") {
823  // Ignored Cache-Control values: public, private, no-transform,
824  // proxy-revalidate, s-max-age. These are used by shared caches.
825  // See section 14.9 of RFC 2612. 10/02/02 jhrg
826  if (value == "no-cache" || value == "no-store")
827  // Note that we *can* store a 'no-store' response in volatile
828  // memory according to RFC 2616 (section 14.9.2) but those
829  // will be rare coming from DAP servers. 10/02/02 jhrg
830  entry->set_no_cache(true);
831  else if (value == "must-revalidate")
832  entry->must_revalidate = true;
833  else if (value.find("max-age") != string::npos) {
834  string max_age = value.substr(value.find("=" + 1));
835  entry->max_age = parse_time(max_age.c_str());
836  }
837  }
838  }
839 }
840 
842 
843 // @TODO Change name to record locked response
845  entry->hits++; // Mark hit
846  d_locked_entries[body] = entry; // record lock, see release_cached_r...
847 }
848 
850 
851  HTTPCacheTable::CacheEntry *entry = d_locked_entries[body];
852  if (!entry)
853  throw InternalErr("There is no cache entry for the response given.");
854 
855  d_locked_entries.erase(body);
856  entry->unlock_read_response();
857 
858  if (entry->readers < 0)
859  throw InternalErr("An unlocked entry was released");
860 }
861 
863  return !d_locked_entries.empty();
864 }
865 
866 } // 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:132
#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)
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
#define REMOVE(a)
void uncouple_entry_from_data(FILE *body)