libdap++ Updated for version 3.8.2
|
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 <iostream> 00040 #include <sstream> 00041 #include <algorithm> 00042 #include <iterator> 00043 #include <set> 00044 00045 #include "Error.h" 00046 #include "InternalErr.h" 00047 #include "ResponseTooBigErr.h" 00048 #ifndef WIN32 00049 #include "SignalHandler.h" 00050 #endif 00051 #include "HTTPCacheInterruptHandler.h" 00052 #include "HTTPCacheTable.h" 00053 00054 #include "util_mit.h" 00055 #include "debug.h" 00056 00057 #ifdef WIN32 00058 #include <direct.h> 00059 #include <time.h> 00060 #include <fcntl.h> 00061 #define MKDIR(a,b) _mkdir((a)) 00062 #define REMOVE(a) remove((a)) 00063 #define MKSTEMP(a) _open(_mktemp((a)),_O_CREAT,_S_IREAD|_S_IWRITE) 00064 #define DIR_SEPARATOR_CHAR '\\' 00065 #define DIR_SEPARATOR_STR "\\" 00066 #else 00067 #define MKDIR(a,b) mkdir((a), (b)) 00068 #define REMOVE(a) remove((a)) 00069 #define MKSTEMP(a) mkstemp((a)) 00070 #define DIR_SEPARATOR_CHAR '/' 00071 #define DIR_SEPARATOR_STR "/" 00072 #endif 00073 00074 #define CACHE_META ".meta" 00075 #define CACHE_INDEX ".index" 00076 #define CACHE_EMPTY_ETAG "@cache@" 00077 00078 #define NO_LM_EXPIRATION 24*3600 // 24 hours 00079 #define MAX_LM_EXPIRATION 48*3600 // Max expiration from LM 00080 00081 // If using LM to find the expiration then take 10% and no more than 00082 // MAX_LM_EXPIRATION. 00083 #ifndef LM_EXPIRATION 00084 #define LM_EXPIRATION(t) (min((MAX_LM_EXPIRATION), static_cast<int>((t) / 10))) 00085 #endif 00086 00087 const int CACHE_TABLE_SIZE = 1499; 00088 00089 using namespace std; 00090 00091 namespace libdap { 00092 00096 int 00097 get_hash(const string &url) 00098 { 00099 int hash = 0; 00100 00101 for (const char *ptr = url.c_str(); *ptr; ptr++) 00102 hash = (int)((hash * 3 + (*(unsigned char *)ptr)) % CACHE_TABLE_SIZE); 00103 00104 return hash; 00105 } 00106 00107 HTTPCacheTable::HTTPCacheTable(const string &cache_root, int block_size) : 00108 d_cache_root(cache_root), 00109 d_block_size(block_size), 00110 d_current_size(0), 00111 d_new_entries(0) 00112 { 00113 d_cache_index = cache_root + CACHE_INDEX; 00114 00115 d_cache_table = new CacheEntries*[CACHE_TABLE_SIZE]; 00116 00117 // Initialize the cache table. 00118 for (int i = 0; i < CACHE_TABLE_SIZE; ++i) 00119 d_cache_table[i] = 0; 00120 00121 cache_index_read(); 00122 } 00123 00127 static inline void 00128 delete_cache_entry(HTTPCacheTable::CacheEntry *e) 00129 { 00130 DBG2(cerr << "Deleting CacheEntry: " << e << endl); 00131 #if 0 00132 DESTROY(&e->get_lock()); 00133 #endif 00134 delete e; 00135 } 00136 00137 HTTPCacheTable::~HTTPCacheTable() { 00138 for (int i = 0; i < CACHE_TABLE_SIZE; ++i) { 00139 HTTPCacheTable::CacheEntries *cp = get_cache_table()[i]; 00140 if (cp) { 00141 // delete each entry 00142 for_each(cp->begin(), cp->end(), delete_cache_entry); 00143 00144 // now delete the vector that held the entries 00145 delete get_cache_table()[i]; 00146 get_cache_table()[i] = 0; 00147 } 00148 } 00149 00150 delete[] d_cache_table; 00151 } 00152 00160 class DeleteExpired : public unary_function<HTTPCacheTable::CacheEntry *&, void> { 00161 time_t d_time; 00162 HTTPCacheTable &d_table; 00163 00164 public: 00165 DeleteExpired(HTTPCacheTable &table, time_t t) : 00166 d_time(t), d_table(table) { 00167 if (!t) 00168 d_time = time(0); // 0 == now 00169 } 00170 00171 void operator()(HTTPCacheTable::CacheEntry *&e) { 00172 if (e && !e->readers && (e->freshness_lifetime 00173 < (e->corrected_initial_age + (d_time - e->response_time)))) { 00174 DBG(cerr << "Deleting expired cache entry: " << e->url << endl); 00175 d_table.remove_cache_entry(e); 00176 delete e; e = 0; 00177 } 00178 } 00179 }; 00180 00181 // @param time base deletes againt this time, defaults to 0 (now) 00182 void HTTPCacheTable::delete_expired_entries(time_t time) { 00183 // Walk through and delete all the expired entries. 00184 for (int cnt = 0; cnt < CACHE_TABLE_SIZE; cnt++) { 00185 HTTPCacheTable::CacheEntries *slot = get_cache_table()[cnt]; 00186 if (slot) { 00187 for_each(slot->begin(), slot->end(), DeleteExpired(*this, time)); 00188 slot->erase(remove(slot->begin(), slot->end(), 00189 static_cast<HTTPCacheTable::CacheEntry *>(0)), slot->end()); 00190 } 00191 } 00192 } 00193 00200 class DeleteByHits : public unary_function<HTTPCacheTable::CacheEntry *&, void> { 00201 HTTPCacheTable &d_table; 00202 int d_hits; 00203 00204 public: 00205 DeleteByHits(HTTPCacheTable &table, int hits) : 00206 d_table(table), d_hits(hits) { 00207 } 00208 00209 void operator()(HTTPCacheTable::CacheEntry *&e) { 00210 if (e && !e->readers && e->hits <= d_hits) { 00211 DBG(cerr << "Deleting cache entry: " << e->url << endl); 00212 d_table.remove_cache_entry(e); 00213 delete e; e = 0; 00214 } 00215 } 00216 }; 00217 00218 void 00219 HTTPCacheTable::delete_by_hits(int hits) { 00220 for (int cnt = 0; cnt < CACHE_TABLE_SIZE; cnt++) { 00221 if (get_cache_table()[cnt]) { 00222 HTTPCacheTable::CacheEntries *slot = get_cache_table()[cnt]; 00223 for_each(slot->begin(), slot->end(), DeleteByHits(*this, hits)); 00224 slot->erase(remove(slot->begin(), slot->end(), 00225 static_cast<HTTPCacheTable::CacheEntry*>(0)), 00226 slot->end()); 00227 00228 } 00229 } 00230 } 00231 00236 class DeleteBySize : public unary_function<HTTPCacheTable::CacheEntry *&, void> { 00237 HTTPCacheTable &d_table; 00238 unsigned int d_size; 00239 00240 public: 00241 DeleteBySize(HTTPCacheTable &table, unsigned int size) : 00242 d_table(table), d_size(size) { 00243 } 00244 00245 void operator()(HTTPCacheTable::CacheEntry *&e) { 00246 if (e && !e->readers && e->size > d_size) { 00247 DBG(cerr << "Deleting cache entry: " << e->url << endl); 00248 d_table.remove_cache_entry(e); 00249 delete e; e = 0; 00250 } 00251 } 00252 }; 00253 00254 void HTTPCacheTable::delete_by_size(unsigned int size) { 00255 for (int cnt = 0; cnt < CACHE_TABLE_SIZE; cnt++) { 00256 if (get_cache_table()[cnt]) { 00257 HTTPCacheTable::CacheEntries *slot = get_cache_table()[cnt]; 00258 for_each(slot->begin(), slot->end(), DeleteBySize(*this, size)); 00259 slot->erase(remove(slot->begin(), slot->end(), 00260 static_cast<HTTPCacheTable::CacheEntry*>(0)), 00261 slot->end()); 00262 00263 } 00264 } 00265 } 00266 00273 00280 bool 00281 HTTPCacheTable::cache_index_delete() 00282 { 00283 d_new_entries = 0; 00284 00285 return (REMOVE(d_cache_index.c_str()) == 0); 00286 } 00287 00296 bool 00297 HTTPCacheTable::cache_index_read() 00298 { 00299 FILE *fp = fopen(d_cache_index.c_str(), "r"); 00300 // If the cache index can't be opened that's OK; start with an empty 00301 // cache. 09/05/02 jhrg 00302 if (!fp) { 00303 return false; 00304 } 00305 00306 char line[1024]; 00307 while (!feof(fp) && fgets(line, 1024, fp)) { 00308 add_entry_to_cache_table(cache_index_parse_line(line)); 00309 DBG2(cerr << line << endl); 00310 } 00311 00312 int res = fclose(fp) ; 00313 if (res) { 00314 DBG(cerr << "HTTPCache::cache_index_read - Failed to close " << (void *)fp << endl); 00315 } 00316 00317 d_new_entries = 0; 00318 00319 return true; 00320 } 00321 00329 HTTPCacheTable::CacheEntry * 00330 HTTPCacheTable::cache_index_parse_line(const char *line) 00331 { 00332 // Read the line and create the cache object 00333 HTTPCacheTable::CacheEntry *entry = new HTTPCacheTable::CacheEntry; 00334 #if 0 00335 INIT(&entry->d_lock); 00336 #endif 00337 istringstream iss(line); 00338 iss >> entry->url; 00339 iss >> entry->cachename; 00340 00341 iss >> entry->etag; 00342 if (entry->etag == CACHE_EMPTY_ETAG) 00343 entry->etag = ""; 00344 00345 iss >> entry->lm; 00346 iss >> entry->expires; 00347 iss >> entry->size; 00348 iss >> entry->range; // range is not used. 10/02/02 jhrg 00349 00350 iss >> entry->hash; 00351 iss >> entry->hits; 00352 iss >> entry->freshness_lifetime; 00353 iss >> entry->response_time; 00354 iss >> entry->corrected_initial_age; 00355 00356 iss >> entry->must_revalidate; 00357 00358 return entry; 00359 } 00360 00363 class WriteOneCacheEntry : 00364 public unary_function<HTTPCacheTable::CacheEntry *, void> 00365 { 00366 00367 FILE *d_fp; 00368 00369 public: 00370 WriteOneCacheEntry(FILE *fp) : d_fp(fp) 00371 {} 00372 00373 void operator()(HTTPCacheTable::CacheEntry *e) 00374 { 00375 if (e && fprintf(d_fp, 00376 "%s %s %s %ld %ld %ld %c %d %d %ld %ld %ld %c\r\n", 00377 e->url.c_str(), 00378 e->cachename.c_str(), 00379 e->etag == "" ? CACHE_EMPTY_ETAG : e->etag.c_str(), 00380 (long)(e->lm), 00381 (long)(e->expires), 00382 e->size, 00383 e->range ? '1' : '0', // not used. 10/02/02 jhrg 00384 e->hash, 00385 e->hits, 00386 (long)(e->freshness_lifetime), 00387 (long)(e->response_time), 00388 (long)(e->corrected_initial_age), 00389 e->must_revalidate ? '1' : '0') < 0) 00390 throw Error("Cache Index. Error writing cache index\n"); 00391 } 00392 }; 00393 00403 void 00404 HTTPCacheTable::cache_index_write() 00405 { 00406 DBG(cerr << "Cache Index. Writing index " << d_cache_index << endl); 00407 00408 // Open the file for writing. 00409 FILE * fp = NULL; 00410 if ((fp = fopen(d_cache_index.c_str(), "wb")) == NULL) { 00411 throw Error(string("Cache Index. Can't open `") + d_cache_index 00412 + string("' for writing")); 00413 } 00414 00415 // Walk through the list and write it out. The format is really 00416 // simple as we keep it all in ASCII. 00417 00418 for (int cnt = 0; cnt < CACHE_TABLE_SIZE; cnt++) { 00419 HTTPCacheTable::CacheEntries *cp = get_cache_table()[cnt]; 00420 if (cp) 00421 for_each(cp->begin(), cp->end(), WriteOneCacheEntry(fp)); 00422 } 00423 00424 /* Done writing */ 00425 int res = fclose(fp); 00426 if (res) { 00427 DBG(cerr << "HTTPCache::cache_index_write - Failed to close " 00428 << (void *)fp << endl); 00429 } 00430 00431 d_new_entries = 0; 00432 } 00433 00435 00448 string 00449 HTTPCacheTable::create_hash_directory(int hash) 00450 { 00451 struct stat stat_info; 00452 ostringstream path; 00453 00454 path << d_cache_root << hash; 00455 string p = path.str(); 00456 00457 if (stat(p.c_str(), &stat_info) == -1) { 00458 DBG2(cerr << "Cache....... Create dir " << p << endl); 00459 if (MKDIR(p.c_str(), 0777) < 0) { 00460 DBG2(cerr << "Cache....... Can't create..." << endl); 00461 throw Error("Could not create cache slot to hold response! Check the write permissions on your disk cache directory. Cache root: " + d_cache_root + "."); 00462 } 00463 } 00464 else { 00465 DBG2(cerr << "Cache....... Directory " << p << " already exists" 00466 << endl); 00467 } 00468 00469 return p; 00470 } 00471 00486 void 00487 HTTPCacheTable::create_location(HTTPCacheTable::CacheEntry *entry) 00488 { 00489 string hash_dir = create_hash_directory(entry->hash); 00490 #ifdef WIN32 00491 hash_dir += "\\dodsXXXXXX"; 00492 #else 00493 hash_dir += "/dodsXXXXXX"; // mkstemp uses six characters. 00494 #endif 00495 00496 // mkstemp uses the storage passed to it; must be writable and local. 00497 char *templat = new char[hash_dir.size() + 1]; 00498 strcpy(templat, hash_dir.c_str()); 00499 00500 // Open truncated for update. NB: mkstemp() returns a file descriptor. 00501 // man mkstemp says "... The file is opened with the O_EXCL flag, 00502 // guaranteeing that when mkstemp returns successfully we are the only 00503 // user." 09/19/02 jhrg 00504 int fd = MKSTEMP(templat); // fd mode is 666 or 600 (Unix) 00505 if (fd < 0) { 00506 delete[] templat; templat = 0; 00507 close(fd); 00508 throw Error("The HTTP Cache could not create a file to hold the response; it will not be cached."); 00509 } 00510 00511 entry->cachename = templat; 00512 delete[] templat; templat = 0; 00513 close(fd); 00514 } 00515 00516 00518 static inline int 00519 entry_disk_space(int size, unsigned int block_size) 00520 { 00521 unsigned int num_of_blocks = (size + block_size) / block_size; 00522 00523 DBG(cerr << "size: " << size << ", block_size: " << block_size 00524 << ", num_of_blocks: " << num_of_blocks << endl); 00525 00526 return num_of_blocks * block_size; 00527 } 00528 00532 00538 void 00539 HTTPCacheTable::add_entry_to_cache_table(CacheEntry *entry) 00540 { 00541 int hash = entry->hash; 00542 00543 if (!d_cache_table[hash]) 00544 d_cache_table[hash] = new CacheEntries; 00545 00546 d_cache_table[hash]->push_back(entry); 00547 00548 DBG(cerr << "add_entry_to_cache_table, current_size: " << d_current_size 00549 << ", entry->size: " << entry->size << ", block size: " << d_block_size 00550 << endl); 00551 00552 d_current_size += entry_disk_space(entry->size, d_block_size); 00553 00554 DBG(cerr << "add_entry_to_cache_table, current_size: " << d_current_size << endl); 00555 00556 increment_new_entries(); 00557 } 00558 00562 HTTPCacheTable::CacheEntry * 00563 HTTPCacheTable::get_locked_entry_from_cache_table(const string &url) /*const*/ 00564 { 00565 return get_locked_entry_from_cache_table(get_hash(url), url); 00566 } 00567 00575 HTTPCacheTable::CacheEntry * 00576 HTTPCacheTable::get_locked_entry_from_cache_table(int hash, const string &url) /*const*/ 00577 { 00578 DBG(cerr << "url: " << url << "; hash: " << hash << endl); 00579 DBG(cerr << "d_cache_table: " << hex << d_cache_table << dec << endl); 00580 if (d_cache_table[hash]) { 00581 CacheEntries *cp = d_cache_table[hash]; 00582 for (CacheEntriesIter i = cp->begin(); i != cp->end(); ++i) { 00583 // Must test *i because perform_garbage_collection may have 00584 // removed this entry; the CacheEntry will then be null. 00585 if ((*i) && (*i)->url == url) { 00586 (*i)->lock_read_response(); // Lock the response 00587 #if 0 00588 (*i)->lock(); 00589 #endif 00590 return *i; 00591 } 00592 } 00593 } 00594 00595 return 0; 00596 } 00597 00604 HTTPCacheTable::CacheEntry * 00605 HTTPCacheTable::get_write_locked_entry_from_cache_table(const string &url) 00606 { 00607 int hash = get_hash(url); 00608 if (d_cache_table[hash]) { 00609 CacheEntries *cp = d_cache_table[hash]; 00610 for (CacheEntriesIter i = cp->begin(); i != cp->end(); ++i) { 00611 // Must test *i because perform_garbage_collection may have 00612 // removed this entry; the CacheEntry will then be null. 00613 if ((*i) && (*i)->url == url) { 00614 (*i)->lock_write_response(); // Lock the response 00615 #if 0 00616 (*i)->lock(); 00617 #endif 00618 return *i; 00619 } 00620 } 00621 } 00622 00623 return 0; 00624 } 00625 00633 void 00634 HTTPCacheTable::remove_cache_entry(HTTPCacheTable::CacheEntry *entry) 00635 { 00636 // This should never happen; all calls to this method are protected by 00637 // the caller, hence the InternalErr. 00638 if (entry->readers) 00639 throw InternalErr(__FILE__, __LINE__, "Tried to delete a cache entry that is in use."); 00640 00641 REMOVE(entry->cachename.c_str()); 00642 REMOVE(string(entry->cachename + CACHE_META).c_str()); 00643 00644 DBG(cerr << "remove_cache_entry, current_size: " << get_current_size() << endl); 00645 00646 unsigned int eds = entry_disk_space(entry->size, get_block_size()); 00647 set_current_size((eds > get_current_size()) ? 0 : get_current_size() - eds); 00648 00649 DBG(cerr << "remove_cache_entry, current_size: " << get_current_size() << endl); 00650 } 00651 00654 class DeleteCacheEntry: public unary_function<HTTPCacheTable::CacheEntry *&, void> 00655 { 00656 string d_url; 00657 HTTPCacheTable *d_cache_table; 00658 00659 public: 00660 DeleteCacheEntry(HTTPCacheTable *c, const string &url) 00661 : d_url(url), d_cache_table(c) 00662 {} 00663 00664 void operator()(HTTPCacheTable::CacheEntry *&e) 00665 { 00666 if (e && e->url == d_url) { 00667 e->lock_write_response(); 00668 d_cache_table->remove_cache_entry(e); 00669 e->unlock_write_response(); 00670 delete e; e = 0; 00671 } 00672 } 00673 }; 00674 00681 void 00682 HTTPCacheTable::remove_entry_from_cache_table(const string &url) 00683 { 00684 int hash = get_hash(url); 00685 if (d_cache_table[hash]) { 00686 CacheEntries *cp = d_cache_table[hash]; 00687 for_each(cp->begin(), cp->end(), DeleteCacheEntry(this, url)); 00688 cp->erase(remove(cp->begin(), cp->end(), static_cast<HTTPCacheTable::CacheEntry*>(0)), 00689 cp->end()); 00690 } 00691 } 00692 00695 class DeleteUnlockedCacheEntry : 00696 public unary_function<HTTPCacheTable::CacheEntry *&, void> { 00697 HTTPCacheTable &d_table; 00698 00699 public: 00700 DeleteUnlockedCacheEntry(HTTPCacheTable &t) : 00701 d_table(t) { 00702 } 00703 void operator()(HTTPCacheTable::CacheEntry *&e) { 00704 if (e) { 00705 d_table.remove_cache_entry(e); 00706 delete e; e = 0; 00707 } 00708 } 00709 }; 00710 00711 void HTTPCacheTable::delete_all_entries() { 00712 // Walk through the cache table and, for every entry in the cache, delete 00713 // it on disk and in the cache table. 00714 for (int cnt = 0; cnt < CACHE_TABLE_SIZE; cnt++) { 00715 HTTPCacheTable::CacheEntries *slot = get_cache_table()[cnt]; 00716 if (slot) { 00717 for_each(slot->begin(), slot->end(), DeleteUnlockedCacheEntry(*this)); 00718 slot->erase(remove(slot->begin(), slot->end(), static_cast<HTTPCacheTable::CacheEntry *>(0)), 00719 slot->end()); 00720 } 00721 } 00722 00723 cache_index_delete(); 00724 } 00725 00739 void 00740 HTTPCacheTable::calculate_time(HTTPCacheTable::CacheEntry *entry, int default_expiration, time_t request_time) 00741 { 00742 entry->response_time = time(NULL); 00743 time_t apparent_age = max(0, static_cast<int>(entry->response_time - entry->date)); 00744 time_t corrected_received_age = max(apparent_age, entry->age); 00745 time_t response_delay = entry->response_time - request_time; 00746 entry->corrected_initial_age = corrected_received_age + response_delay; 00747 00748 // Estimate an expires time using the max-age and expires time. If we 00749 // don't have an explicit expires time then set it to 10% of the LM date 00750 // (although max 24 h). If no LM date is available then use 24 hours. 00751 time_t freshness_lifetime = entry->max_age; 00752 if (freshness_lifetime < 0) { 00753 if (entry->expires < 0) { 00754 if (entry->lm < 0) { 00755 freshness_lifetime = default_expiration; 00756 } 00757 else { 00758 freshness_lifetime = LM_EXPIRATION(entry->date - entry->lm); 00759 } 00760 } 00761 else 00762 freshness_lifetime = entry->expires - entry->date; 00763 } 00764 00765 entry->freshness_lifetime = max(0, static_cast<int>(freshness_lifetime)); 00766 00767 DBG2(cerr << "Cache....... Received Age " << entry->age 00768 << ", corrected " << entry->corrected_initial_age 00769 << ", freshness lifetime " << entry->freshness_lifetime << endl); 00770 } 00771 00783 void HTTPCacheTable::parse_headers(HTTPCacheTable::CacheEntry *entry, 00784 unsigned long max_entry_size, const vector<string> &headers) { 00785 vector<string>::const_iterator i; 00786 for (i = headers.begin(); i != headers.end(); ++i) { 00787 // skip a blank header. 00788 if ((*i).empty()) 00789 continue; 00790 00791 string::size_type colon = (*i).find(':'); 00792 00793 // skip a header with no colon in it. 00794 if (colon == string::npos) 00795 continue; 00796 00797 string header = (*i).substr(0, (*i).find(':')); 00798 string value = (*i).substr((*i).find(": ") + 2); 00799 DBG2(cerr << "Header: " << header << endl);DBG2(cerr << "Value: " << value << endl); 00800 00801 if (header == "ETag") { 00802 entry->etag = value; 00803 } else if (header == "Last-Modified") { 00804 entry->lm = parse_time(value.c_str()); 00805 } else if (header == "Expires") { 00806 entry->expires = parse_time(value.c_str()); 00807 } else if (header == "Date") { 00808 entry->date = parse_time(value.c_str()); 00809 } else if (header == "Age") { 00810 entry->age = parse_time(value.c_str()); 00811 } else if (header == "Content-Length") { 00812 unsigned long clength = strtoul(value.c_str(), 0, 0); 00813 if (clength > max_entry_size) 00814 entry->set_no_cache(true); 00815 } else if (header == "Cache-Control") { 00816 // Ignored Cache-Control values: public, private, no-transform, 00817 // proxy-revalidate, s-max-age. These are used by shared caches. 00818 // See section 14.9 of RFC 2612. 10/02/02 jhrg 00819 if (value == "no-cache" || value == "no-store") 00820 // Note that we *can* store a 'no-store' response in volatile 00821 // memory according to RFC 2616 (section 14.9.2) but those 00822 // will be rare coming from DAP servers. 10/02/02 jhrg 00823 entry->set_no_cache(true); 00824 else if (value == "must-revalidate") 00825 entry->must_revalidate = true; 00826 else if (value.find("max-age") != string::npos) { 00827 string max_age = value.substr(value.find("=" + 1)); 00828 entry->max_age = parse_time(max_age.c_str()); 00829 } 00830 } 00831 } 00832 } 00833 00835 00836 // @TODO Change name to record locked response 00837 void HTTPCacheTable::bind_entry_to_data(HTTPCacheTable::CacheEntry *entry, FILE *body) { 00838 entry->hits++; // Mark hit 00839 d_locked_entries[body] = entry; // record lock, see release_cached_r... 00840 #if 0 00841 entry->unlock(); // Unlock the entry object so others can read it 00842 #endif 00843 } 00844 00845 void HTTPCacheTable::uncouple_entry_from_data(FILE *body) { 00846 HTTPCacheTable::CacheEntry *entry = d_locked_entries[body]; 00847 if (!entry) 00848 throw InternalErr("There is no cache entry for the response given."); 00849 00850 d_locked_entries.erase(body); 00851 entry->unlock_read_response(); 00852 00853 if (entry->readers < 0) 00854 throw InternalErr("An unlocked entry was released"); 00855 } 00856 00857 bool HTTPCacheTable::is_locked_read_responses() { 00858 return !d_locked_entries.empty(); 00859 } 00860 00861 } // namespace libdap