libdap
Updated for version 3.17.0
|
00001 00002 // -*- mode: c++; c-basic-offset:4 -*- 00003 00004 // This file is part of libdap, A C++ implementation of the OPeNDAP Data 00005 // Access Protocol. 00006 00007 // Copyright (c) 2002,2003 OPeNDAP, Inc. 00008 // Author: James Gallagher <jgallagher@opendap.org> 00009 // 00010 // This library is free software; you can redistribute it and/or 00011 // modify it under the terms of the GNU Lesser General Public 00012 // License as published by the Free Software Foundation; either 00013 // version 2.1 of the License, or (at your option) any later version. 00014 // 00015 // This library is distributed in the hope that it will be useful, 00016 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00017 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00018 // Lesser General Public License for more details. 00019 // 00020 // You should have received a copy of the GNU Lesser General Public 00021 // License along with this library; if not, write to the Free Software 00022 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00023 // 00024 // You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. 00025 00026 #include "config.h" 00027 00028 // #define DODS_DEBUG 00029 00030 // TODO: Remove unneeded includes. 00031 00032 #include <pthread.h> 00033 #include <limits.h> 00034 #include <unistd.h> // for stat 00035 #include <sys/types.h> // for stat and mkdir 00036 #include <sys/stat.h> 00037 00038 #include <cstring> 00039 #include <cerrno> 00040 00041 #include <iostream> 00042 #include <sstream> 00043 #include <algorithm> 00044 #include <iterator> 00045 #include <set> 00046 00047 #include "Error.h" 00048 #include "InternalErr.h" 00049 #include "ResponseTooBigErr.h" 00050 #ifndef WIN32 00051 #include "SignalHandler.h" 00052 #endif 00053 #include "HTTPCacheInterruptHandler.h" 00054 #include "HTTPCacheTable.h" 00055 #include "HTTPCacheMacros.h" 00056 00057 #include "util_mit.h" 00058 #include "debug.h" 00059 00060 #ifdef WIN32 00061 #include <direct.h> 00062 #include <time.h> 00063 #include <fcntl.h> 00064 #define MKDIR(a,b) _mkdir((a)) 00065 #define REMOVE(a) do { \ 00066 int s = remove((a)); \ 00067 if (s != 0) \ 00068 throw InternalErr(__FILE__, __LINE__, "Cache error; could not remove file: " + long_to_string(s)); \ 00069 } while(0); 00070 #define MKSTEMP(a) _open(_mktemp((a)),_O_CREAT,_S_IREAD|_S_IWRITE) 00071 #define DIR_SEPARATOR_CHAR '\\' 00072 #define DIR_SEPARATOR_STR "\\" 00073 #else 00074 #define MKDIR(a,b) mkdir((a), (b)) 00075 #define MKSTEMP(a) mkstemp((a)) 00076 #define DIR_SEPARATOR_CHAR '/' 00077 #define DIR_SEPARATOR_STR "/" 00078 #endif 00079 00080 #define CACHE_META ".meta" 00081 #define CACHE_INDEX ".index" 00082 #define CACHE_EMPTY_ETAG "@cache@" 00083 00084 #define NO_LM_EXPIRATION 24*3600 // 24 hours 00085 #define MAX_LM_EXPIRATION 48*3600 // Max expiration from LM 00086 00087 // If using LM to find the expiration then take 10% and no more than 00088 // MAX_LM_EXPIRATION. 00089 #ifndef LM_EXPIRATION 00090 #define LM_EXPIRATION(t) (min((MAX_LM_EXPIRATION), static_cast<int>((t) / 10))) 00091 #endif 00092 00093 const int CACHE_TABLE_SIZE = 1499; 00094 00095 using namespace std; 00096 00097 namespace libdap { 00098 00102 int 00103 get_hash(const string &url) 00104 { 00105 int hash = 0; 00106 00107 for (const char *ptr = url.c_str(); *ptr; ptr++) 00108 hash = (int)((hash * 3 + (*(unsigned char *)ptr)) % CACHE_TABLE_SIZE); 00109 00110 return hash; 00111 } 00112 00113 HTTPCacheTable::HTTPCacheTable(const string &cache_root, int block_size) : 00114 d_cache_root(cache_root), d_block_size(block_size), d_current_size(0), d_new_entries(0) 00115 { 00116 d_cache_index = cache_root + CACHE_INDEX; 00117 00118 d_cache_table = new CacheEntries*[CACHE_TABLE_SIZE]; 00119 00120 // Initialize the cache table. 00121 for (int i = 0; i < CACHE_TABLE_SIZE; ++i) 00122 d_cache_table[i] = 0; 00123 00124 cache_index_read(); 00125 } 00126 00130 static inline void 00131 delete_cache_entry(HTTPCacheTable::CacheEntry *e) 00132 { 00133 DBG2(cerr << "Deleting CacheEntry: " << e << endl); 00134 delete e; 00135 } 00136 00137 HTTPCacheTable::~HTTPCacheTable() 00138 { 00139 for (int i = 0; i < CACHE_TABLE_SIZE; ++i) { 00140 HTTPCacheTable::CacheEntries *cp = get_cache_table()[i]; 00141 if (cp) { 00142 // delete each entry 00143 for_each(cp->begin(), cp->end(), delete_cache_entry); 00144 00145 // now delete the vector that held the entries 00146 delete get_cache_table()[i]; 00147 get_cache_table()[i] = 0; 00148 } 00149 } 00150 00151 delete[] d_cache_table; 00152 } 00153 00161 class DeleteExpired : public unary_function<HTTPCacheTable::CacheEntry *&, void> { 00162 time_t d_time; 00163 HTTPCacheTable &d_table; 00164 00165 public: 00166 DeleteExpired(HTTPCacheTable &table, time_t t) : 00167 d_time(t), d_table(table) { 00168 if (!t) 00169 d_time = time(0); // 0 == now 00170 } 00171 00172 void operator()(HTTPCacheTable::CacheEntry *&e) { 00173 if (e && !e->readers && (e->freshness_lifetime 00174 < (e->corrected_initial_age + (d_time - e->response_time)))) { 00175 DBG(cerr << "Deleting expired cache entry: " << e->url << endl); 00176 d_table.remove_cache_entry(e); 00177 delete e; e = 0; 00178 } 00179 } 00180 }; 00181 00182 // @param time base deletes againt this time, defaults to 0 (now) 00183 void HTTPCacheTable::delete_expired_entries(time_t time) { 00184 // Walk through and delete all the expired entries. 00185 for (int cnt = 0; cnt < CACHE_TABLE_SIZE; cnt++) { 00186 HTTPCacheTable::CacheEntries *slot = get_cache_table()[cnt]; 00187 if (slot) { 00188 for_each(slot->begin(), slot->end(), DeleteExpired(*this, time)); 00189 slot->erase(remove(slot->begin(), slot->end(), 00190 static_cast<HTTPCacheTable::CacheEntry *>(0)), slot->end()); 00191 } 00192 } 00193 } 00194 00201 class DeleteByHits : public unary_function<HTTPCacheTable::CacheEntry *&, void> { 00202 HTTPCacheTable &d_table; 00203 int d_hits; 00204 00205 public: 00206 DeleteByHits(HTTPCacheTable &table, int hits) : 00207 d_table(table), d_hits(hits) { 00208 } 00209 00210 void operator()(HTTPCacheTable::CacheEntry *&e) { 00211 if (e && !e->readers && e->hits <= d_hits) { 00212 DBG(cerr << "Deleting cache entry: " << e->url << endl); 00213 d_table.remove_cache_entry(e); 00214 delete e; e = 0; 00215 } 00216 } 00217 }; 00218 00219 void 00220 HTTPCacheTable::delete_by_hits(int hits) { 00221 for (int cnt = 0; cnt < CACHE_TABLE_SIZE; cnt++) { 00222 if (get_cache_table()[cnt]) { 00223 HTTPCacheTable::CacheEntries *slot = get_cache_table()[cnt]; 00224 for_each(slot->begin(), slot->end(), DeleteByHits(*this, hits)); 00225 slot->erase(remove(slot->begin(), slot->end(), 00226 static_cast<HTTPCacheTable::CacheEntry*>(0)), 00227 slot->end()); 00228 00229 } 00230 } 00231 } 00232 00237 class DeleteBySize : public unary_function<HTTPCacheTable::CacheEntry *&, void> { 00238 HTTPCacheTable &d_table; 00239 unsigned int d_size; 00240 00241 public: 00242 DeleteBySize(HTTPCacheTable &table, unsigned int size) : 00243 d_table(table), d_size(size) { 00244 } 00245 00246 void operator()(HTTPCacheTable::CacheEntry *&e) { 00247 if (e && !e->readers && e->size > d_size) { 00248 DBG(cerr << "Deleting cache entry: " << e->url << endl); 00249 d_table.remove_cache_entry(e); 00250 delete e; e = 0; 00251 } 00252 } 00253 }; 00254 00255 void HTTPCacheTable::delete_by_size(unsigned int size) { 00256 for (int cnt = 0; cnt < CACHE_TABLE_SIZE; cnt++) { 00257 if (get_cache_table()[cnt]) { 00258 HTTPCacheTable::CacheEntries *slot = get_cache_table()[cnt]; 00259 for_each(slot->begin(), slot->end(), DeleteBySize(*this, size)); 00260 slot->erase(remove(slot->begin(), slot->end(), 00261 static_cast<HTTPCacheTable::CacheEntry*>(0)), 00262 slot->end()); 00263 00264 } 00265 } 00266 } 00267 00274 00281 bool 00282 HTTPCacheTable::cache_index_delete() 00283 { 00284 d_new_entries = 0; 00285 00286 return (REMOVE_BOOL(d_cache_index.c_str()) == 0); 00287 } 00288 00297 bool 00298 HTTPCacheTable::cache_index_read() 00299 { 00300 FILE *fp = fopen(d_cache_index.c_str(), "r"); 00301 // If the cache index can't be opened that's OK; start with an empty 00302 // cache. 09/05/02 jhrg 00303 if (!fp) { 00304 return false; 00305 } 00306 00307 char line[1024]; 00308 while (!feof(fp) && fgets(line, 1024, fp)) { 00309 add_entry_to_cache_table(cache_index_parse_line(line)); 00310 DBG2(cerr << line << endl); 00311 } 00312 00313 int res = fclose(fp) ; 00314 if (res) { 00315 DBG(cerr << "HTTPCache::cache_index_read - Failed to close " << (void *)fp << endl); 00316 } 00317 00318 d_new_entries = 0; 00319 00320 return true; 00321 } 00322 00330 HTTPCacheTable::CacheEntry * 00331 HTTPCacheTable::cache_index_parse_line(const char *line) 00332 { 00333 // Read the line and create the cache object 00334 HTTPCacheTable::CacheEntry *entry = new HTTPCacheTable::CacheEntry; 00335 istringstream iss(line); 00336 iss >> entry->url; 00337 iss >> entry->cachename; 00338 00339 iss >> entry->etag; 00340 if (entry->etag == CACHE_EMPTY_ETAG) 00341 entry->etag = ""; 00342 00343 iss >> entry->lm; 00344 iss >> entry->expires; 00345 iss >> entry->size; 00346 iss >> entry->range; // range is not used. 10/02/02 jhrg 00347 00348 iss >> entry->hash; 00349 iss >> entry->hits; 00350 iss >> entry->freshness_lifetime; 00351 iss >> entry->response_time; 00352 iss >> entry->corrected_initial_age; 00353 00354 iss >> entry->must_revalidate; 00355 00356 return entry; 00357 } 00358 00361 class WriteOneCacheEntry : 00362 public unary_function<HTTPCacheTable::CacheEntry *, void> 00363 { 00364 00365 FILE *d_fp; 00366 00367 public: 00368 WriteOneCacheEntry(FILE *fp) : d_fp(fp) 00369 {} 00370 00371 void operator()(HTTPCacheTable::CacheEntry *e) 00372 { 00373 if (e && fprintf(d_fp, 00374 "%s %s %s %ld %ld %ld %c %d %d %ld %ld %ld %c\r\n", 00375 e->url.c_str(), 00376 e->cachename.c_str(), 00377 e->etag == "" ? CACHE_EMPTY_ETAG : e->etag.c_str(), 00378 (long)(e->lm), 00379 (long)(e->expires), 00380 e->size, 00381 e->range ? '1' : '0', // not used. 10/02/02 jhrg 00382 e->hash, 00383 e->hits, 00384 (long)(e->freshness_lifetime), 00385 (long)(e->response_time), 00386 (long)(e->corrected_initial_age), 00387 e->must_revalidate ? '1' : '0') < 0) 00388 throw Error(internal_error, "Cache Index. Error writing cache index\n"); 00389 } 00390 }; 00391 00401 void 00402 HTTPCacheTable::cache_index_write() 00403 { 00404 DBG(cerr << "Cache Index. Writing index " << d_cache_index << endl); 00405 00406 // Open the file for writing. 00407 FILE * fp = NULL; 00408 if ((fp = fopen(d_cache_index.c_str(), "wb")) == NULL) { 00409 throw Error(string("Cache Index. Can't open `") + d_cache_index 00410 + string("' for writing")); 00411 } 00412 00413 // Walk through the list and write it out. The format is really 00414 // simple as we keep it all in ASCII. 00415 00416 for (int cnt = 0; cnt < CACHE_TABLE_SIZE; cnt++) { 00417 HTTPCacheTable::CacheEntries *cp = get_cache_table()[cnt]; 00418 if (cp) 00419 for_each(cp->begin(), cp->end(), WriteOneCacheEntry(fp)); 00420 } 00421 00422 /* Done writing */ 00423 int res = fclose(fp); 00424 if (res) { 00425 DBG(cerr << "HTTPCache::cache_index_write - Failed to close " 00426 << (void *)fp << endl); 00427 } 00428 00429 d_new_entries = 0; 00430 } 00431 00433 00446 string 00447 HTTPCacheTable::create_hash_directory(int hash) 00448 { 00449 #if 0 00450 struct stat stat_info; 00451 ostringstream path; 00452 00453 path << d_cache_root << hash; 00454 string p = path.str(); 00455 00456 if (stat(p.c_str(), &stat_info) == -1) { 00457 DBG2(cerr << "Cache....... Create dir " << p << endl); 00458 if (MKDIR(p.c_str(), 0777) < 0) { 00459 DBG2(cerr << "Cache....... Can't create..." << endl); 00460 throw Error("Could not create cache slot to hold response! Check the write permissions on your disk cache directory. Cache root: " + d_cache_root + "."); 00461 } 00462 } 00463 else { 00464 DBG2(cerr << "Cache....... Directory " << p << " already exists" 00465 << endl); 00466 } 00467 00468 return p; 00469 #endif 00470 00471 ostringstream path; 00472 path << d_cache_root << hash; 00473 00474 // Save the mask 00475 mode_t mask = umask(0); 00476 00477 // Ignore the error if the directory exists 00478 errno = 0; 00479 if (mkdir(path.str().c_str(), 0777) < 0 && errno != EEXIST) { 00480 umask(mask); 00481 throw Error(internal_error, "Could not create the directory for the cache at '" + path.str() + "' (" + strerror(errno) + ")."); 00482 } 00483 00484 // Restore themask 00485 umask(mask); 00486 00487 return path.str(); 00488 } 00489 00504 void 00505 HTTPCacheTable::create_location(HTTPCacheTable::CacheEntry *entry) 00506 { 00507 string hash_dir = create_hash_directory(entry->hash); 00508 #ifdef WIN32 00509 hash_dir += "\\dodsXXXXXX"; 00510 #else 00511 hash_dir += "/dodsXXXXXX"; // mkstemp uses six characters. 00512 #endif 00513 00514 // mkstemp uses the storage passed to it; must be writable and local. 00515 // char *templat = new char[hash_dir.size() + 1]; 00516 vector<char> templat(hash_dir.size() + 1); 00517 strncpy(&templat[0], hash_dir.c_str(), hash_dir.size() + 1); 00518 00519 // Open truncated for update. NB: mkstemp() returns a file descriptor. 00520 // man mkstemp says "... The file is opened with the O_EXCL flag, 00521 // guaranteeing that when mkstemp returns successfully we are the only 00522 // user." 09/19/02 jhrg 00523 #ifndef WIN32 00524 // Make sure that temp files are accessible only by the owner. 00525 umask(077); 00526 #endif 00527 int fd = MKSTEMP(&templat[0]); // fd mode is 666 or 600 (Unix) 00528 if (fd < 0) { 00529 // delete[] templat; templat = 0; 00530 // close(fd); Calling close() when fd is < 0 is a bad idea! jhrg 7/2/15 00531 throw Error(internal_error, "The HTTP Cache could not create a file to hold the response; it will not be cached."); 00532 } 00533 00534 entry->cachename = &templat[0]; 00535 // delete[] templat; templat = 0; 00536 close(fd); 00537 } 00538 00539 00541 static inline int 00542 entry_disk_space(int size, unsigned int block_size) 00543 { 00544 unsigned int num_of_blocks = (size + block_size) / block_size; 00545 00546 DBG(cerr << "size: " << size << ", block_size: " << block_size 00547 << ", num_of_blocks: " << num_of_blocks << endl); 00548 00549 return num_of_blocks * block_size; 00550 } 00551 00555 00561 void 00562 HTTPCacheTable::add_entry_to_cache_table(CacheEntry *entry) 00563 { 00564 int hash = entry->hash; 00565 if (hash > CACHE_TABLE_SIZE-1 || hash < 0) 00566 throw InternalErr(__FILE__, __LINE__, "Hash value too large!"); 00567 00568 if (!d_cache_table[hash]) 00569 d_cache_table[hash] = new CacheEntries; 00570 00571 d_cache_table[hash]->push_back(entry); 00572 00573 DBG(cerr << "add_entry_to_cache_table, current_size: " << d_current_size 00574 << ", entry->size: " << entry->size << ", block size: " << d_block_size 00575 << endl); 00576 00577 d_current_size += entry_disk_space(entry->size, d_block_size); 00578 00579 DBG(cerr << "add_entry_to_cache_table, current_size: " << d_current_size << endl); 00580 00581 increment_new_entries(); 00582 } 00583 00587 HTTPCacheTable::CacheEntry * 00588 HTTPCacheTable::get_locked_entry_from_cache_table(const string &url) /*const*/ 00589 { 00590 return get_locked_entry_from_cache_table(get_hash(url), url); 00591 } 00592 00600 HTTPCacheTable::CacheEntry * 00601 HTTPCacheTable::get_locked_entry_from_cache_table(int hash, const string &url) /*const*/ 00602 { 00603 DBG(cerr << "url: " << url << "; hash: " << hash << endl); 00604 DBG(cerr << "d_cache_table: " << hex << d_cache_table << dec << endl); 00605 if (d_cache_table[hash]) { 00606 CacheEntries *cp = d_cache_table[hash]; 00607 for (CacheEntriesIter i = cp->begin(); i != cp->end(); ++i) { 00608 // Must test *i because perform_garbage_collection may have 00609 // removed this entry; the CacheEntry will then be null. 00610 if ((*i) && (*i)->url == url) { 00611 (*i)->lock_read_response(); // Lock the response 00612 return *i; 00613 } 00614 } 00615 } 00616 00617 return 0; 00618 } 00619 00626 HTTPCacheTable::CacheEntry * 00627 HTTPCacheTable::get_write_locked_entry_from_cache_table(const string &url) 00628 { 00629 int hash = get_hash(url); 00630 if (d_cache_table[hash]) { 00631 CacheEntries *cp = d_cache_table[hash]; 00632 for (CacheEntriesIter i = cp->begin(); i != cp->end(); ++i) { 00633 // Must test *i because perform_garbage_collection may have 00634 // removed this entry; the CacheEntry will then be null. 00635 if ((*i) && (*i)->url == url) { 00636 (*i)->lock_write_response(); // Lock the response 00637 return *i; 00638 } 00639 } 00640 } 00641 00642 return 0; 00643 } 00644 00652 void 00653 HTTPCacheTable::remove_cache_entry(HTTPCacheTable::CacheEntry *entry) 00654 { 00655 // This should never happen; all calls to this method are protected by 00656 // the caller, hence the InternalErr. 00657 if (entry->readers) 00658 throw InternalErr(__FILE__, __LINE__, "Tried to delete a cache entry that is in use."); 00659 00660 REMOVE(entry->cachename.c_str()); 00661 REMOVE(string(entry->cachename + CACHE_META).c_str()); 00662 00663 DBG(cerr << "remove_cache_entry, current_size: " << get_current_size() << endl); 00664 00665 unsigned int eds = entry_disk_space(entry->size, get_block_size()); 00666 set_current_size((eds > get_current_size()) ? 0 : get_current_size() - eds); 00667 00668 DBG(cerr << "remove_cache_entry, current_size: " << get_current_size() << endl); 00669 } 00670 00673 class DeleteCacheEntry: public unary_function<HTTPCacheTable::CacheEntry *&, void> 00674 { 00675 string d_url; 00676 HTTPCacheTable *d_cache_table; 00677 00678 public: 00679 DeleteCacheEntry(HTTPCacheTable *c, const string &url) 00680 : d_url(url), d_cache_table(c) 00681 {} 00682 00683 void operator()(HTTPCacheTable::CacheEntry *&e) 00684 { 00685 if (e && e->url == d_url) { 00686 e->lock_write_response(); 00687 d_cache_table->remove_cache_entry(e); 00688 e->unlock_write_response(); 00689 delete e; e = 0; 00690 } 00691 } 00692 }; 00693 00700 void 00701 HTTPCacheTable::remove_entry_from_cache_table(const string &url) 00702 { 00703 int hash = get_hash(url); 00704 if (d_cache_table[hash]) { 00705 CacheEntries *cp = d_cache_table[hash]; 00706 for_each(cp->begin(), cp->end(), DeleteCacheEntry(this, url)); 00707 cp->erase(remove(cp->begin(), cp->end(), static_cast<HTTPCacheTable::CacheEntry*>(0)), 00708 cp->end()); 00709 } 00710 } 00711 00714 class DeleteUnlockedCacheEntry: public unary_function<HTTPCacheTable::CacheEntry *&, void> { 00715 HTTPCacheTable &d_table; 00716 00717 public: 00718 DeleteUnlockedCacheEntry(HTTPCacheTable &t) : 00719 d_table(t) 00720 { 00721 } 00722 void operator()(HTTPCacheTable::CacheEntry *&e) 00723 { 00724 if (e) { 00725 d_table.remove_cache_entry(e); 00726 delete e; 00727 e = 0; 00728 } 00729 } 00730 }; 00731 00732 void HTTPCacheTable::delete_all_entries() 00733 { 00734 // Walk through the cache table and, for every entry in the cache, delete 00735 // it on disk and in the cache table. 00736 for (int cnt = 0; cnt < CACHE_TABLE_SIZE; cnt++) { 00737 HTTPCacheTable::CacheEntries *slot = get_cache_table()[cnt]; 00738 if (slot) { 00739 for_each(slot->begin(), slot->end(), DeleteUnlockedCacheEntry(*this)); 00740 slot->erase(remove(slot->begin(), slot->end(), static_cast<HTTPCacheTable::CacheEntry *> (0)), slot->end()); 00741 } 00742 } 00743 00744 cache_index_delete(); 00745 } 00746 00760 void 00761 HTTPCacheTable::calculate_time(HTTPCacheTable::CacheEntry *entry, int default_expiration, time_t request_time) 00762 { 00763 entry->response_time = time(NULL); 00764 time_t apparent_age = max(0, static_cast<int>(entry->response_time - entry->date)); 00765 time_t corrected_received_age = max(apparent_age, entry->age); 00766 time_t response_delay = entry->response_time - request_time; 00767 entry->corrected_initial_age = corrected_received_age + response_delay; 00768 00769 // Estimate an expires time using the max-age and expires time. If we 00770 // don't have an explicit expires time then set it to 10% of the LM date 00771 // (although max 24 h). If no LM date is available then use 24 hours. 00772 time_t freshness_lifetime = entry->max_age; 00773 if (freshness_lifetime < 0) { 00774 if (entry->expires < 0) { 00775 if (entry->lm < 0) { 00776 freshness_lifetime = default_expiration; 00777 } 00778 else { 00779 freshness_lifetime = LM_EXPIRATION(entry->date - entry->lm); 00780 } 00781 } 00782 else 00783 freshness_lifetime = entry->expires - entry->date; 00784 } 00785 00786 entry->freshness_lifetime = max(0, static_cast<int>(freshness_lifetime)); 00787 00788 DBG2(cerr << "Cache....... Received Age " << entry->age 00789 << ", corrected " << entry->corrected_initial_age 00790 << ", freshness lifetime " << entry->freshness_lifetime << endl); 00791 } 00792 00804 void HTTPCacheTable::parse_headers(HTTPCacheTable::CacheEntry *entry, unsigned long max_entry_size, 00805 const vector<string> &headers) 00806 { 00807 vector<string>::const_iterator i; 00808 for (i = headers.begin(); i != headers.end(); ++i) { 00809 // skip a blank header. 00810 if ((*i).empty()) 00811 continue; 00812 00813 string::size_type colon = (*i).find(':'); 00814 00815 // skip a header with no colon in it. 00816 if (colon == string::npos) 00817 continue; 00818 00819 string header = (*i).substr(0, (*i).find(':')); 00820 string value = (*i).substr((*i).find(": ") + 2); 00821 DBG2(cerr << "Header: " << header << endl);DBG2(cerr << "Value: " << value << endl); 00822 00823 if (header == "ETag") { 00824 entry->etag = value; 00825 } 00826 else if (header == "Last-Modified") { 00827 entry->lm = parse_time(value.c_str()); 00828 } 00829 else if (header == "Expires") { 00830 entry->expires = parse_time(value.c_str()); 00831 } 00832 else if (header == "Date") { 00833 entry->date = parse_time(value.c_str()); 00834 } 00835 else if (header == "Age") { 00836 entry->age = parse_time(value.c_str()); 00837 } 00838 else if (header == "Content-Length") { 00839 unsigned long clength = strtoul(value.c_str(), 0, 0); 00840 if (clength > max_entry_size) 00841 entry->set_no_cache(true); 00842 } 00843 else if (header == "Cache-Control") { 00844 // Ignored Cache-Control values: public, private, no-transform, 00845 // proxy-revalidate, s-max-age. These are used by shared caches. 00846 // See section 14.9 of RFC 2612. 10/02/02 jhrg 00847 if (value == "no-cache" || value == "no-store") 00848 // Note that we *can* store a 'no-store' response in volatile 00849 // memory according to RFC 2616 (section 14.9.2) but those 00850 // will be rare coming from DAP servers. 10/02/02 jhrg 00851 entry->set_no_cache(true); 00852 else if (value == "must-revalidate") 00853 entry->must_revalidate = true; 00854 else if (value.find("max-age") != string::npos) { 00855 string max_age = value.substr(value.find("=" + 1)); 00856 entry->max_age = parse_time(max_age.c_str()); 00857 } 00858 } 00859 } 00860 } 00861 00863 00864 // @TODO Change name to record locked response 00865 void HTTPCacheTable::bind_entry_to_data(HTTPCacheTable::CacheEntry *entry, FILE *body) { 00866 entry->hits++; // Mark hit 00867 d_locked_entries[body] = entry; // record lock, see release_cached_r... 00868 } 00869 00870 void HTTPCacheTable::uncouple_entry_from_data(FILE *body) { 00871 00872 HTTPCacheTable::CacheEntry *entry = d_locked_entries[body]; 00873 if (!entry) 00874 throw InternalErr("There is no cache entry for the response given."); 00875 00876 d_locked_entries.erase(body); 00877 entry->unlock_read_response(); 00878 00879 if (entry->readers < 0) 00880 throw InternalErr("An unlocked entry was released"); 00881 } 00882 00883 bool HTTPCacheTable::is_locked_read_responses() { 00884 return !d_locked_entries.empty(); 00885 } 00886 00887 } // namespace libdap