35 #include <sys/types.h> 61 #define MKDIR(a,b) _mkdir((a)) 62 #define REMOVE(a) remove((a)) 63 #define MKSTEMP(a) _open(_mktemp((a)),_O_CREAT,_S_IREAD|_S_IWRITE) 64 #define DIR_SEPARATOR_CHAR '\\' 65 #define DIR_SEPARATOR_STR "\\" 67 #define MKDIR(a,b) mkdir((a), (b)) 68 #define REMOVE(a) remove((a)) 69 #define MKSTEMP(a) mkstemp((a)) 70 #define DIR_SEPARATOR_CHAR '/' 71 #define DIR_SEPARATOR_STR "/" 74 #define CACHE_META ".meta" 75 #define CACHE_INDEX ".index" 76 #define CACHE_EMPTY_ETAG "@cache@" 78 #define NO_LM_EXPIRATION 24*3600 // 24 hours 79 #define MAX_LM_EXPIRATION 48*3600 // Max expiration from LM 84 #define LM_EXPIRATION(t) (min((MAX_LM_EXPIRATION), static_cast<int>((t) / 10))) 101 for (
const char *ptr = url.c_str(); *ptr; ptr++)
107 HTTPCacheTable::HTTPCacheTable(
const string &cache_root,
int block_size) :
108 d_cache_root(cache_root),
109 d_block_size(block_size),
119 d_cache_table[i] = 0;
130 DBG2(cerr <<
"Deleting CacheEntry: " << e << endl);
139 for_each(cp->begin(), cp->end(), delete_cache_entry);
142 delete get_cache_table()[i];
143 get_cache_table()[i] = 0;
147 delete[] d_cache_table;
157 class DeleteExpired :
public unary_function<HTTPCacheTable::CacheEntry *&, void> {
163 d_time(t), d_table(table) {
169 if (e && !e->readers && (e->freshness_lifetime
170 < (e->corrected_initial_age + (d_time - e->response_time)))) {
171 DBG(cerr <<
"Deleting expired cache entry: " << e->url << endl);
184 for_each(slot->begin(), slot->end(), DeleteExpired(*
this, time));
185 slot->erase(
remove(slot->begin(), slot->end(),
197 class DeleteByHits :
public unary_function<HTTPCacheTable::CacheEntry *&, void> {
203 d_table(table), d_hits(hits) {
207 if (e && !e->readers && e->hits <= d_hits) {
208 DBG(cerr <<
"Deleting cache entry: " << e->url << endl);
218 if (get_cache_table()[cnt]) {
220 for_each(slot->begin(), slot->end(), DeleteByHits(*
this, hits));
221 slot->erase(
remove(slot->begin(), slot->end(),
233 class DeleteBySize :
public unary_function<HTTPCacheTable::CacheEntry *&, void> {
239 d_table(table), d_size(size) {
243 if (e && !e->readers && e->size > d_size) {
244 DBG(cerr <<
"Deleting cache entry: " << e->url << endl);
253 if (get_cache_table()[cnt]) {
255 for_each(slot->begin(), slot->end(), DeleteBySize(*
this, size));
256 slot->erase(
remove(slot->begin(), slot->end(),
282 return (
REMOVE(d_cache_index.c_str()) == 0);
296 FILE *fp = fopen(d_cache_index.c_str(),
"r");
304 while (!feof(fp) && fgets(line, 1024, fp)) {
306 DBG2(cerr << line << endl);
309 int res = fclose(fp) ;
311 DBG(cerr <<
"HTTPCache::cache_index_read - Failed to close " << (
void *)fp << endl);
331 istringstream iss(line);
333 iss >> entry->cachename;
340 iss >> entry->expires;
346 iss >> entry->freshness_lifetime;
347 iss >> entry->response_time;
348 iss >> entry->corrected_initial_age;
350 iss >> entry->must_revalidate;
357 class WriteOneCacheEntry :
358 public unary_function<HTTPCacheTable::CacheEntry *, void>
364 WriteOneCacheEntry(FILE *fp) : d_fp(fp)
369 if (e && fprintf(d_fp,
370 "%s %s %s %ld %ld %ld %c %d %d %ld %ld %ld %c\r\n",
372 e->cachename.c_str(),
377 e->range ?
'1' :
'0',
380 (long)(e->freshness_lifetime),
381 (long)(e->response_time),
382 (long)(e->corrected_initial_age),
383 e->must_revalidate ?
'1' :
'0') < 0)
384 throw Error(
"Cache Index. Error writing cache index\n");
400 DBG(cerr <<
"Cache Index. Writing index " << d_cache_index << endl);
404 if ((fp = fopen(d_cache_index.c_str(),
"wb")) == NULL) {
405 throw Error(
string(
"Cache Index. Can't open `") + d_cache_index
406 +
string(
"' for writing"));
415 for_each(cp->begin(), cp->end(), WriteOneCacheEntry(fp));
419 int res = fclose(fp);
421 DBG(cerr <<
"HTTPCache::cache_index_write - Failed to close " 422 << (
void *)fp << endl);
445 struct stat stat_info;
448 path << d_cache_root << hash;
449 string p = path.str();
451 if (stat(p.c_str(), &stat_info) == -1) {
452 DBG2(cerr <<
"Cache....... Create dir " << p << endl);
453 if (
MKDIR(p.c_str(), 0777) < 0) {
454 DBG2(cerr <<
"Cache....... Can't create..." << endl);
455 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 DBG2(cerr <<
"Cache....... Directory " << p <<
" already exists" 485 hash_dir +=
"\\dodsXXXXXX";
487 hash_dir +=
"/dodsXXXXXX";
491 char *templat =
new char[hash_dir.size() + 1];
492 strcpy(templat, hash_dir.c_str());
500 delete[] templat; templat = 0;
502 throw Error(
"The HTTP Cache could not create a file to hold the response; it will not be cached.");
505 entry->cachename = templat;
506 delete[] templat; templat = 0;
513 entry_disk_space(
int size,
unsigned int block_size)
515 unsigned int num_of_blocks = (size + block_size) / block_size;
517 DBG(cerr <<
"size: " << size <<
", block_size: " << block_size
518 <<
", num_of_blocks: " << num_of_blocks << endl);
520 return num_of_blocks * block_size;
535 int hash = entry->hash;
537 if (!d_cache_table[hash])
540 d_cache_table[hash]->push_back(entry);
542 DBG(cerr <<
"add_entry_to_cache_table, current_size: " << d_current_size
543 <<
", entry->size: " << entry->size <<
", block size: " << d_block_size
546 d_current_size += entry_disk_space(entry->size, d_block_size);
548 DBG(cerr <<
"add_entry_to_cache_table, current_size: " << d_current_size << endl);
557 HTTPCacheTable::get_locked_entry_from_cache_table(
const string &url)
559 return get_locked_entry_from_cache_table(
get_hash(url), url);
570 HTTPCacheTable::get_locked_entry_from_cache_table(
int hash,
const string &url)
572 DBG(cerr <<
"url: " << url <<
"; hash: " << hash << endl);
573 DBG(cerr <<
"d_cache_table: " << hex << d_cache_table << dec << endl);
574 if (d_cache_table[hash]) {
579 if ((*i) && (*i)->url == url) {
580 (*i)->lock_read_response();
599 if (d_cache_table[hash]) {
604 if ((*i) && (*i)->url == url) {
605 (*i)->lock_write_response();
627 throw InternalErr(__FILE__, __LINE__,
"Tried to delete a cache entry that is in use.");
629 REMOVE(entry->cachename.c_str());
634 unsigned int eds = entry_disk_space(entry->size,
get_block_size());
642 class DeleteCacheEntry:
public unary_function<HTTPCacheTable::CacheEntry *&, void>
649 : d_url(url), d_cache_table(c)
654 if (e && e->url == d_url) {
673 if (d_cache_table[hash]) {
675 for_each(cp->begin(), cp->end(), DeleteCacheEntry(
this, url));
683 class DeleteUnlockedCacheEntry :
684 public unary_function<HTTPCacheTable::CacheEntry *&, void> {
705 for_each(slot->begin(), slot->end(), DeleteUnlockedCacheEntry(*
this));
730 entry->response_time = time(NULL);
731 time_t apparent_age = max(0, static_cast<int>(entry->response_time - entry->date));
732 time_t corrected_received_age = max(apparent_age, entry->age);
733 time_t response_delay = entry->response_time - request_time;
734 entry->corrected_initial_age = corrected_received_age + response_delay;
739 time_t freshness_lifetime = entry->max_age;
740 if (freshness_lifetime < 0) {
741 if (entry->expires < 0) {
743 freshness_lifetime = default_expiration;
750 freshness_lifetime = entry->expires - entry->date;
753 entry->freshness_lifetime = max(0, static_cast<int>(freshness_lifetime));
755 DBG2(cerr <<
"Cache....... Received Age " << entry->age
756 <<
", corrected " << entry->corrected_initial_age
757 <<
", freshness lifetime " << entry->freshness_lifetime << endl);
772 unsigned long max_entry_size,
const vector<string> &headers) {
773 vector<string>::const_iterator i;
774 for (i = headers.begin(); i != headers.end(); ++i) {
779 string::size_type colon = (*i).find(
':');
782 if (colon == string::npos)
785 string header = (*i).substr(0, (*i).find(
':'));
786 string value = (*i).substr((*i).find(
": ") + 2);
787 DBG2(cerr <<
"Header: " << header << endl);
DBG2(cerr <<
"Value: " << value << endl);
789 if (header ==
"ETag") {
791 }
else if (header ==
"Last-Modified") {
793 }
else if (header ==
"Expires") {
795 }
else if (header ==
"Date") {
797 }
else if (header ==
"Age") {
799 }
else if (header ==
"Content-Length") {
800 unsigned long clength = strtoul(value.c_str(), 0, 0);
801 if (clength > max_entry_size)
803 }
else if (header ==
"Cache-Control") {
807 if (value ==
"no-cache" || value ==
"no-store")
812 else if (value ==
"must-revalidate")
813 entry->must_revalidate =
true;
814 else if (value.find(
"max-age") != string::npos) {
815 string max_age = value.substr(value.find(
"=" + 1));
827 d_locked_entries[body] = entry;
833 throw InternalErr(
"There is no cache entry for the response given.");
835 d_locked_entries.erase(body);
838 if (entry->readers < 0)
839 throw InternalErr(
"An unlocked entry was released");
843 return !d_locked_entries.empty();
void remove_cache_entry(HTTPCacheTable::CacheEntry *entry)
const int CACHE_TABLE_SIZE
time_t parse_time(const char *str, bool expand)
void create_location(CacheEntry *entry)
void unlock_read_response()
void parse_headers(HTTPCacheTable::CacheEntry *entry, unsigned long max_entry_size, const vector< string > &headers)
void increment_new_entries()
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)
void lock_write_response()
CacheEntries::iterator CacheEntriesIter
void set_no_cache(bool state)
A class for software fault reporting.
void delete_all_entries()
void set_current_size(unsigned long sz)
unsigned int get_block_size() const
int get_hash(const string &url)
void bind_entry_to_data(CacheEntry *entry, FILE *body)
void delete_expired_entries(time_t time=0)
unsigned long get_current_size() const
void unlock_write_response()
CacheEntry * cache_index_parse_line(const char *line)
void remove_entry_from_cache_table(const string &url)
CacheEntry * get_write_locked_entry_from_cache_table(const string &url)
void delete_by_hits(int hits)
A class for error processing.
string create_hash_directory(int hash)
bool cache_index_delete()
void uncouple_entry_from_data(FILE *body)
bool is_locked_read_responses()