93 set_cache_root(cache_root);
95 create_cache_root(d_cache_root);
97 d_cache_lock_fd = m_initialize_cache_lock(lock);
98 d_cache_lock_file = lock;
102 if (stat(cache_root.c_str(), &s) == 0)
103 block_size = s.st_blksize;
107 d_http_cache_table =
new HTTPCacheTable(d_cache_root, block_size);
108 d_cache_enabled =
true;
109 }
catch (
const Error &) {
111 d_cache_enabled =
false;
112 DBG(cerr <<
"Failure to get the cache lock" << endl);
127 perform_garbage_collection();
129 d_http_cache_table->cache_index_write();
130 delete d_http_cache_table;
131 }
catch (
const Error &e) {
140 close(d_cache_lock_fd);
150bool HTTPCache::stopGC()
const {
151 return (d_http_cache_table->
get_current_size() + d_folder_size < d_total_size - d_gc_buffer);
158bool HTTPCache::startGC()
const {
160 return (d_http_cache_table->
get_current_size() + d_folder_size > d_total_size);
177void HTTPCache::perform_garbage_collection() {
178 DBG(cerr <<
"Performing garbage collection" << endl);
196void HTTPCache::expired_gc() {
197 if (!d_expire_ignored) {
198 d_http_cache_table->delete_expired_entries();
217void HTTPCache::hits_gc() {
222 d_http_cache_table->delete_by_hits(hits);
232void HTTPCache::too_big_gc() {
234 d_http_cache_table->delete_by_size(d_max_entry_size);
242static inline string get_errno() {
243 const char *s_err = strerror(errno);
244 return s_err ? s_err :
"unknown error";
251static inline struct flock *lock(
short type) {
252 static struct flock lock;
254 lock.l_whence = SEEK_SET;
257 lock.l_pid = getpid();
271static bool create_locked_file(
const string &file_name,
int &ref_fd) {
272 DBG(cerr <<
"BEGIN file: " << file_name << endl);
275 if ((fd = open(file_name.c_str(), O_CREAT | O_EXCL | O_RDWR, 0660)) < 0) {
276 if (errno == EEXIST) {
279 throw InternalErr(__FILE__, __LINE__, file_name +
": " + get_errno());
283 struct flock *l = lock(F_WRLCK);
285 if (fcntl(fd, F_SETLKW, l) == -1) {
288 oss <<
"cache process: " << l->l_pid <<
" triggered a locking error for '" << file_name <<
"': " << get_errno();
292 DBG(cerr <<
"END file: " << file_name << endl);
310int HTTPCache::m_initialize_cache_lock(
const string &cache_lock)
const {
311 DBG(cerr <<
"BEGIN" << endl);
314 if (create_locked_file(cache_lock, fd)) {
318 if ((fd = open(cache_lock.c_str(), O_RDWR)) == -1) {
319 throw InternalErr(__FILE__, __LINE__,
320 "Failed to open cache lock file: " + cache_lock +
" errno: " + get_errno());
324 DBG(cerr <<
"END" << endl);
336void HTTPCache::m_lock_cache_write(
int fd) {
337 DBG(cerr <<
"d_cache_info_fd: " << d_cache_info_fd << endl);
340 if (fcntl(fd, F_SETLKW, lock(F_WRLCK)) == -1) {
341 throw InternalErr(__FILE__, __LINE__,
"An error occurred trying to lock the cache-control file" + get_errno());
344 DBG(cerr <<
"lock status: " << lockStatus(d_cache_info_fd) << endl);
350void HTTPCache::m_lock_cache_read(
int fd) {
351 DBG(cerr <<
"d_cache_info_fd: " << d_cache_info_fd << endl);
353 if (fcntl(fd, F_SETLKW, lock(F_RDLCK)) == -1) {
354 throw InternalErr(__FILE__, __LINE__,
"An error occurred trying to lock the cache-control file" + get_errno());
357 DBG(cerr <<
"lock status: " << lockStatus(d_cache_info_fd) << endl);
365void HTTPCache::m_unlock_cache(
int fd) {
366 DBG(cerr <<
"d_cache_info_fd: " << d_cache_info_fd << endl);
368 if (fcntl(fd, F_SETLK, lock(F_UNLCK)) == -1) {
369 throw InternalErr(__FILE__, __LINE__,
370 "An error occurred trying to unlock the cache-control file" + get_errno());
373 DBG(cerr <<
"lock status: " << lockStatus(d_cache_info_fd) << endl);
391void HTTPCache::m_exclusive_to_shared_lock(
int fd) {
393 lock.l_type = F_RDLCK;
394 lock.l_whence = SEEK_SET;
397 lock.l_pid = getpid();
399 if (fcntl(fd, F_SETLKW, &lock) == -1) {
400 throw InternalErr(__FILE__, __LINE__, get_errno());
403 DBG(cerr <<
"lock status: " << lockStatus(fd) << endl);
424void HTTPCache::create_cache_root(
const string &cache_root)
const {
426 mode_t mask = umask(S_IRWXO);
430 if (mkdir(cache_root.c_str(), S_IRWXU | S_IRWXG) < 0 && errno != EEXIST) {
432 throw Error(
"HTTPCache::create_cache_root: Could not create the directory for the cache at '" + cache_root +
433 "' (" + strerror(errno) +
").");
440inline bool ends_with(std::string
const &value, std::string
const &ending) {
441 if (ending.size() > value.size())
443 return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
457void HTTPCache::set_cache_root(
const string &root) {
476 if (d_http_cache_table)
477 d_http_cache_table->set_cache_root(d_cache_root);
492 lock_guard<mutex> lock{d_cache_mutex};
494 d_cache_enabled = mode;
512 lock_guard<mutex> lock{d_cache_mutex};
514 d_cache_disconnected = mode;
530 lock_guard<mutex> lock{d_cache_mutex};
532 d_expire_ignored = mode;
556 lock_guard<mutex> lock{d_cache_mutex};
559 unsigned long old_size = d_total_size;
560 d_total_size = new_size;
564 if (new_size < old_size && startGC()) {
565 perform_garbage_collection();
566 d_http_cache_table->cache_index_write();
583 lock_guard<mutex> lock{d_cache_mutex};
585 unsigned long new_size = size *
MEGA;
586 if (new_size > 0 && new_size < d_total_size - d_folder_size) {
587 unsigned long old_size = d_max_entry_size;
588 d_max_entry_size = new_size;
589 if (new_size < old_size && startGC()) {
590 perform_garbage_collection();
591 d_http_cache_table->cache_index_write();
613 lock_guard<mutex> lock{d_cache_mutex};
615 d_default_expiration = exp_time;
650 lock_guard<mutex> lock{d_cache_mutex};
652 d_cache_control = cc;
654 for (
auto &line : cc) {
655 string header = line.substr(0, line.find(
':'));
656 string value = line.substr(line.find(
": ") + 2);
657 if (header !=
"Cache-Control") {
658 throw InternalErr(__FILE__, __LINE__,
"Expected cache control header not found.");
660 if (value ==
"no-cache" || value ==
"no-store")
661 d_cache_enabled =
false;
662 else if (value.find(
"max-age") != string::npos) {
663 string max_age = value.substr(value.find(
'=') + 1);
665 }
else if (value ==
"max-stale")
667 else if (value.find(
"max-stale") != string::npos) {
668 string max_stale = value.substr(value.find(
'=') + 1);
670 }
else if (value.find(
"min-fresh") != string::npos) {
671 string min_fresh = value.substr(value.find(
'=') + 1);
698bool HTTPCache::is_url_in_cache(
const string &url) {
699 lock_guard<mutex> lock{d_cache_mutex};
701 HTTPCacheTable::CacheEntry *entry = d_http_cache_table->get_read_locked_entry_from_cache_table(url);
704 entry->unlock_read_response();
716 return header.find(
"Connection") != string::npos || header.find(
"Keep-Alive") != string::npos ||
717 header.find(
"Proxy-Authenticate") != string::npos || header.find(
"Proxy-Authorization") != string::npos ||
718 header.find(
"Transfer-Encoding") != string::npos || header.find(
"Upgrade") != string::npos;
732void HTTPCache::write_metadata(
const string &cachename,
const vector<string> &headers) {
734 d_open_files.push_back(fname);
736 FILE *dest = fopen(fname.c_str(),
"w");
738 throw InternalErr(__FILE__, __LINE__,
"Could not open named cache entry file.");
741 vector<string>::const_iterator i;
742 for (
auto &header : headers) {
744 size_t s = fwrite(header.c_str(), header.size(), 1, dest);
747 throw InternalErr(__FILE__, __LINE__,
"could not write header: '" + (*i) +
"' " +
long_to_string(s));
749 s = fwrite(
"\n", 1, 1, dest);
752 throw InternalErr(__FILE__, __LINE__,
"could not write header: " +
long_to_string(s));
757 int res = fclose(dest);
759 DBG(cerr <<
"HTTPCache::write_metadata - Failed to close " << dest << endl);
762 d_open_files.pop_back();
775void HTTPCache::read_metadata(
const string &cachename, vector<string> &headers)
const {
776 FILE *md = fopen(
string(cachename +
CACHE_META).c_str(),
"r");
778 throw InternalErr(__FILE__, __LINE__,
"Could not open named cache entry meta data file.");
781 const size_t line_buf_len = 1024;
782 char line[line_buf_len];
783 while (!feof(md) && fgets(line, line_buf_len, md)) {
784 line[std::min(line_buf_len, strnlen(line, line_buf_len)) - 1] =
'\0';
785 headers.emplace_back(line);
788 int res = fclose(md);
790 DBG(cerr <<
"HTTPCache::read_metadata - Failed to close " << md << endl);
815int HTTPCache::write_body(
const string &cachename,
const FILE *src) {
816 d_open_files.push_back(cachename);
818 FILE *dest = fopen(cachename.c_str(),
"wb");
820 throw InternalErr(__FILE__, __LINE__,
"Could not open named cache entry file.");
828 while ((n = fread(line, 1, 1024,
const_cast<FILE *
>(src))) > 0) {
829 total += fwrite(line, 1, n, dest);
833 if (ferror(
const_cast<FILE *
>(src)) || ferror(dest)) {
834 int res = fclose(dest);
835 res = res & unlink(cachename.c_str());
837 DBG(cerr <<
"HTTPCache::write_body - Failed to close/unlink " << dest << endl);
839 throw InternalErr(__FILE__, __LINE__,
"I/O error transferring data to the cache.");
842 rewind(
const_cast<FILE *
>(src));
844 int res = fclose(dest);
846 DBG(cerr <<
"HTTPCache::write_body - Failed to close " << dest << endl);
849 d_open_files.pop_back();
862FILE *HTTPCache::open_body(
const string &cachename) {
863 DBG(cerr <<
"cachename: " << cachename << endl);
865 FILE *src = fopen(cachename.c_str(),
"rb");
867 throw InternalErr(__FILE__, __LINE__,
"Could not open cache file.");
902 if (url.find(
"http:") == string::npos && url.find(
"https:") == string::npos) {
906 lock_guard<mutex> lock{d_cache_mutex};
907 mp_lock_guard write_lock{d_cache_lock_fd,
908 mp_lock_guard::operation::write};
913 d_http_cache_table->remove_entry_from_cache_table(url);
915 entry->lock_write_response();
918 d_http_cache_table->parse_headers(entry, d_max_entry_size, headers);
919 if (entry->is_no_cache()) {
920 DBG(cerr <<
"Not cache-able; deleting HTTPCacheTable::CacheEntry: " << entry <<
"(" << url <<
")" << endl);
921 entry->unlock_write_response();
927 d_http_cache_table->calculate_time(entry, d_default_expiration, request_time);
929 d_http_cache_table->create_location(entry);
931 entry->set_size(write_body(entry->get_cachename(), body));
932 write_metadata(entry->get_cachename(), headers);
933 d_http_cache_table->add_entry_to_cache_table(entry);
934 entry->unlock_write_response();
938 remove(entry->get_cachename().c_str());
939 remove(
string(entry->get_cachename() +
CACHE_META).c_str());
940 entry->unlock_write_response();
947 perform_garbage_collection();
949 d_http_cache_table->cache_index_write();
976 vector<string> headers;
978 lock_guard<mutex> lock{d_cache_mutex};
979 mp_lock_guard read_lock{d_cache_lock_fd, mp_lock_guard::operation::read};
982 entry = d_http_cache_table->get_read_locked_entry_from_cache_table(url);
987 headers.push_back(
string(
"If-None-Match: ") + entry->
get_etag());
989 if (entry->
get_lm() > 0) {
990 time_t lm = entry->
get_lm();
991 headers.push_back(
string(
"If-Modified-Since: ") +
date_time_str(&lm));
994 headers.push_back(
string(
"If-Modified-Since: ") +
date_time_str(&max_age));
997 headers.push_back(
string(
"If-Modified-Since: ") +
date_time_str(&expires));
1013struct HeaderLess : binary_function<const string &, const string &, bool> {
1014 bool operator()(
const string &s1,
const string &s2)
const {
1015 return s1.substr(0, s1.find(
':')) < s2.substr(0, s2.find(
':'));
1036 lock_guard<mutex> lock{d_cache_mutex};
1037 mp_lock_guard write_lock{d_cache_lock_fd,
1038 mp_lock_guard::operation::write};
1040 entry = d_http_cache_table->get_write_locked_entry_from_cache_table(url);
1045 d_http_cache_table->parse_headers(entry, d_max_entry_size, headers);
1048 d_http_cache_table->calculate_time(entry, d_default_expiration, request_time);
1056 set<string, HeaderLess> merged_headers;
1059 copy(headers.begin(), headers.end(), inserter(merged_headers, merged_headers.begin()));
1062 vector<string> old_headers;
1064 copy(old_headers.begin(), old_headers.end(), inserter(merged_headers, merged_headers.begin()));
1069 vector<string> result;
1070 copy(merged_headers.rbegin(), merged_headers.rend(), back_inserter(result));
1099 if (d_always_validate) {
1103 lock_guard<mutex> lock{d_cache_mutex};
1104 mp_lock_guard read_lock{d_cache_lock_fd, mp_lock_guard::operation::read};
1106 entry = d_http_cache_table->get_read_locked_entry_from_cache_table(url);
1125 if (d_max_age >= 0 && current_age > d_max_age) {
1176 FILE *body =
nullptr;
1180 lock_guard<mutex> lock{d_cache_mutex};
1181 mp_lock_guard read_lock{d_cache_lock_fd, mp_lock_guard::operation::read};
1183 DBG(cerr <<
"Getting the cached response for " << url << endl);
1185 entry = d_http_cache_table->get_read_locked_entry_from_cache_table(url);
1193 DBG(cerr <<
"Headers just read from cache: " << endl);
1194 DBGN(copy(headers.begin(), headers.end(), ostream_iterator<string>(cerr,
"\n")));
1198 DBG(cerr <<
"Returning: " << url <<
" from the cache." << endl);
1200 d_http_cache_table->bind_entry_to_data(entry, body);
1204 read_lock.release();
1206 if (body !=
nullptr)
1226 string discard_name;
1241 string discard_name;
1242 vector<string> discard_headers;
1259 lock_guard<mutex> lock{d_cache_mutex};
1262 d_http_cache_table->uncouple_entry_from_data(body);
1263 m_unlock_cache(d_cache_lock_fd);
1279 lock_guard<mutex> lock{d_cache_mutex};
1280 mp_lock_guard write_lock{d_cache_lock_fd, mp_lock_guard::operation::write};
1282 if (d_http_cache_table->is_locked_read_responses())
1285 d_http_cache_table->delete_all_entries();
#define internal_error
Internal server error (500)
const string CACHE_LOCATION
const string CACHE_EMPTY_ETAG
const string DIR_SEPARATOR_CHAR
#define MIN_CACHE_TOTAL_SIZE
A class for error processing.
std::string get_error_message() const
unsigned long get_current_size() const
CacheDisconnectedMode get_cache_disconnected() const
void set_expire_ignored(bool mode)
void set_default_expiration(int exp_time)
bool is_url_valid(const std::string &url)
std::vector< std::string > get_conditional_request_headers(const std::string &url)
std::string get_cache_root() const
void set_cache_disconnected(CacheDisconnectedMode mode)
void release_cached_response(FILE *response)
unsigned long get_max_entry_size() const
void set_cache_enabled(bool mode)
FILE * get_cached_response(const std::string &url, std::vector< std::string > &headers, std::string &cacheName)
unsigned long get_max_size() const
bool cache_response(const std::string &url, time_t request_time, const std::vector< std::string > &headers, const FILE *body)
std::vector< std::string > get_cache_control() const
void set_max_entry_size(unsigned long size)
bool get_always_validate() const
int get_default_expiration() const
void set_always_validate(bool validate)
void update_response(const std::string &url, time_t request_time, const std::vector< std::string > &headers)
void set_max_size(unsigned long size)
bool is_expire_ignored() const
void set_cache_control(const std::vector< std::string > &cc)
bool is_cache_enabled() const
A class for software fault reporting.
top level DAP object to house generic methods
bool is_hop_by_hop_header(const string &header)
string long_to_string(long val, int base)
bool ends_with(std::string const &value, std::string const &ending)
string date_time_str(time_t *calendar, bool local)
time_t parse_time(const char *str, bool expand)
void unlock_write_response()
std::string get_etag() const
bool get_must_revalidate() const
time_t get_response_time() const
time_t get_max_age() const
time_t get_corrected_initial_age() const
void unlock_read_response()
std::string get_cachename() const
time_t get_freshness_lifetime() const
time_t get_expires() const