59 for (
const char *ptr = url.c_str(); *ptr; ptr++)
66 : d_cache_root(cache_root), d_block_size(block_size) {
75 for (
auto &row : d_cache_table) {
76 for (
auto &entry : row) {
91 for (
auto &row : d_cache_table) {
92 for (
auto &entry : row) {
94 if (entry && !entry->readers &&
95 (entry->freshness_lifetime < (entry->corrected_initial_age + (t - entry->response_time)))) {
96 DBG(cerr <<
"Deleting expired cache entry: " << entry->url << endl);
97 remove_cache_entry(entry);
103 row.erase(remove(row.begin(), row.end(),
nullptr), row.end());
112 for (
auto &row : d_cache_table) {
113 for (
auto &entry : row) {
115 if (entry && !entry->readers && entry->hits <= hits) {
116 DBG(cerr <<
"Deleting cache entry (too few hits): " << entry->url << endl);
117 remove_cache_entry(entry);
123 row.erase(remove(row.begin(), row.end(),
nullptr), row.end());
132 for (
auto &row : d_cache_table) {
133 for (
auto &entry : row) {
135 if (entry && !entry->readers && entry->size > size) {
136 DBG(cerr <<
"Deleting cache entry (too few hits): " << entry->url << endl);
137 remove_cache_entry(entry);
143 row.erase(remove(row.begin(), row.end(),
nullptr), row.end());
160bool HTTPCacheTable::cache_index_delete() {
163 return (remove(d_cache_index.c_str()) == 0);
174bool HTTPCacheTable::cache_index_read() {
175 FILE *fp = fopen(d_cache_index.c_str(),
"r");
183 while (!feof(fp) && fgets(line, 1024, fp)) {
185 DBG2(cerr << line << endl);
188 int res = fclose(fp);
190 DBG(cerr <<
"HTTPCache::cache_index_read - Failed to close " << (
void *)fp << endl);
207 auto entry =
new HTTPCacheTable::CacheEntry;
208 istringstream iss(line);
210 iss >> entry->cachename;
217 iss >> entry->expires;
223 iss >> entry->freshness_lifetime;
224 iss >> entry->response_time;
225 iss >> entry->corrected_initial_age;
227 iss >> entry->must_revalidate;
234class WriteOneCacheEntry :
public unary_function<HTTPCacheTable::CacheEntry *, void> {
238 explicit WriteOneCacheEntry(FILE *fp) : d_fp(fp) {}
240 void operator()(
const HTTPCacheTable::CacheEntry *e) {
242 (fprintf(d_fp,
"%s %s %s %ld %ld %ld %c %d %d %ld %ld %ld %c\r\n", e->url.c_str(), e->cachename.c_str(),
243 e->etag.empty() ?
CACHE_EMPTY_ETAG.c_str() : e->etag.c_str(), (e->lm), (e->expires), e->size,
244 e->range ?
'1' :
'0',
245 e->hash, e->hits, e->freshness_lifetime, e->response_time, e->corrected_initial_age,
246 e->must_revalidate ?
'1' :
'0') < 0))
247 throw Error(
internal_error,
"Cache Index. Error writing cache index\n");
261 DBG(cerr <<
"Cache Index. Writing index " << d_cache_index << endl);
265 if ((fp = fopen(d_cache_index.c_str(),
"wb")) ==
nullptr) {
266 throw Error(
string(
"Cache Index. Can't open `") + d_cache_index +
"' for writing");
271 WriteOneCacheEntry woc(fp);
272 for (
const auto &row : d_cache_table) {
273 for (
auto &entry : row) {
281 int res = fclose(fp);
283 DBG(cerr <<
"HTTPCache::cache_index_write - Failed to close " << (
void *)fp << endl);
303string HTTPCacheTable::create_hash_directory(
int hash) {
305 path << d_cache_root << hash;
308 mode_t mask = umask(S_IRWXO);
312 if (mkdir(path.str().c_str(), S_IRWXU | S_IRWXG) < 0 && errno != EEXIST) {
315 "HTTPCacheTable::create_hash_directory: Could not create the directory for the cache at '" +
316 path.str() +
"' (" + strerror(errno) +
").");
340 string hash_dir = create_hash_directory(entry->hash);
341 hash_dir +=
"/dodsXXXXXX";
344 vector<char> templat(hash_dir.size() + 1);
345 strncpy(templat.data(), hash_dir.c_str(), hash_dir.size() + 1);
351 int fd = mkstemp(templat.data());
355 "The HTTP Cache could not create a file to hold the response; it will not be cached.");
358 entry->cachename = templat.data();
363static inline unsigned int entry_disk_space(
int size,
unsigned int block_size) {
364 unsigned int num_of_blocks = (size + block_size) / block_size;
366 DBG(cerr <<
"size: " << size <<
", block_size: " << block_size <<
", num_of_blocks: " << num_of_blocks << endl);
368 return num_of_blocks * block_size;
381 int hash = entry->hash;
383 throw InternalErr(__FILE__, __LINE__,
"Hash value too large!");
385 d_cache_table[hash].push_back(entry);
387 DBG(cerr <<
"add_entry_to_cache_table, current_size: " << d_current_size <<
", entry->size: " << entry->size
388 <<
", block size: " << d_block_size << endl);
390 d_current_size += entry_disk_space(entry->size, d_block_size);
392 DBG(cerr <<
"add_entry_to_cache_table, current_size: " << d_current_size << endl);
402 return get_read_locked_entry_from_cache_table(
get_hash(url), url);
417 DBG(cerr <<
"url: " << url <<
"; hash: " << hash << endl);
418 DBG(cerr <<
"d_cache_table: " << hex << d_cache_table << dec << endl);
419 if (!d_cache_table[hash].empty()) {
420 for (
auto entry : d_cache_table[hash]) {
424 if (entry && entry->url == url) {
425 entry->lock_read_response();
443 if (!d_cache_table[hash].empty()) {
444 for (
auto entry : d_cache_table[hash]) {
445 if (entry && entry->url == url) {
446 entry->lock_write_response();
466 throw InternalErr(__FILE__, __LINE__,
"Tried to delete a cache entry that is in use.");
468 remove(entry->cachename.c_str());
469 remove(
string(entry->cachename +
CACHE_META).c_str());
473 unsigned int eds = entry_disk_space(entry->size,
get_block_size());
487 auto &row = d_cache_table[hash];
488 for (
auto &entry : row) {
489 if (entry && entry->url == url) {
491 remove_cache_entry(entry);
498 row.erase(remove(row.begin(), row.end(),
nullptr), row.end());
502 for (
auto &row : d_cache_table) {
503 for (
auto &entry : row) {
505 remove_cache_entry(entry);
511 row.erase(remove(row.begin(), row.end(),
nullptr), row.end());
514 cache_index_delete();
531 entry->response_time = time(
nullptr);
532 time_t apparent_age = max(0,
static_cast<int>(entry->response_time - entry->date));
533 time_t corrected_received_age = max(apparent_age, entry->age);
534 time_t response_delay = entry->response_time - request_time;
535 entry->corrected_initial_age = corrected_received_age + response_delay;
540 time_t freshness_lifetime = entry->max_age;
541 if (freshness_lifetime < 0) {
542 if (entry->expires < 0) {
544 freshness_lifetime = default_expiration;
549 freshness_lifetime = entry->expires - entry->date;
552 entry->freshness_lifetime = max(0,
static_cast<int>(freshness_lifetime));
554 DBG2(cerr <<
"Cache....... Received Age " << entry->age <<
", corrected " << entry->corrected_initial_age
555 <<
", freshness lifetime " << entry->freshness_lifetime << endl);
570 const vector<string> &headers) {
571 for (
const auto &line : headers) {
576 string::size_type colon = line.find(
':');
579 if (colon == string::npos)
582 string header = line.substr(0, line.find(
':'));
583 string value = line.substr(line.find(
": ") + 2);
584 DBG2(cerr <<
"Header: " << header << endl);
585 DBG2(cerr <<
"Value: " << value << endl);
587 if (header ==
"ETag") {
589 }
else if (header ==
"Last-Modified") {
591 }
else if (header ==
"Expires") {
593 }
else if (header ==
"Date") {
595 }
else if (header ==
"Age") {
597 }
else if (header ==
"Content-Length") {
598 unsigned long clength = strtoul(value.c_str(), 0, 0);
599 if (clength > max_entry_size)
601 }
else if (header ==
"Cache-Control") {
605 if (value ==
"no-cache" || value ==
"no-store")
610 else if (value ==
"must-revalidate")
611 entry->must_revalidate =
true;
612 else if (value.find(
"max-age") != string::npos) {
613 string max_age = value.substr(value.find(
'=') + 1);
624 d_locked_entries[body] = entry;
630 throw InternalErr(
"There is no cache entry for the response given.");
632 d_locked_entries.erase(body);
635 if (entry->readers < 0)
636 throw InternalErr(
"An unlocked entry was released");
#define internal_error
Internal server error (500)
const int CACHE_TABLE_SIZE
const string CACHE_EMPTY_ETAG
A class for error processing.
void create_location(CacheEntry *entry)
void calculate_time(HTTPCacheTable::CacheEntry *entry, int default_expiration, time_t request_time)
void delete_expired_entries(time_t time=0)
Delete all the expired entries in the cache.
void bind_entry_to_data(CacheEntry *entry, FILE *body)
unsigned int get_block_size() const
void remove_entry_from_cache_table(const std::string &url)
void delete_by_size(unsigned long size)
Delete all the entries in the cache that are larger than size bytes.
void delete_by_hits(int hits)
Delete all the entries in the cache that have fewer than hits hits.
virtual ~HTTPCacheTable()
void add_entry_to_cache_table(CacheEntry *entry)
void set_current_size(unsigned long sz)
unsigned long get_current_size() const
CacheEntry * get_write_locked_entry_from_cache_table(const std::string &url)
void delete_all_entries()
void uncouple_entry_from_data(FILE *body)
void parse_headers(HTTPCacheTable::CacheEntry *entry, unsigned long max_entry_size, const std::vector< std::string > &headers)
void increment_new_entries()
bool is_locked_read_responses() const
A class for software fault reporting.
top level DAP object to house generic methods
int get_hash(const string &url)
time_t parse_time(const char *str, bool expand)
void unlock_write_response()
void set_no_cache(bool state)
void lock_write_response()
void unlock_read_response()