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 #include "HTTPCacheMacros.h" 00054 00055 #include "util_mit.h" 00056 #include "debug.h" 00057 00058 #define NO_LM_EXPIRATION 24*3600 // 24 hours 00059 #define MAX_LM_EXPIRATION 48*3600 // Max expiration from LM 00060 00061 // If using LM to find the expiration then take 10% and no more than 00062 // MAX_LM_EXPIRATION. 00063 #ifndef LM_EXPIRATION 00064 #define LM_EXPIRATION(t) (min((MAX_LM_EXPIRATION), static_cast<int>((t) / 10))) 00065 #endif 00066 00067 const int CACHE_TABLE_SIZE = 1499; 00068 00069 using namespace std; 00070 00071 namespace libdap { 00072 00076 int 00077 get_hash(const string &url) 00078 { 00079 int hash = 0; 00080 00081 for (const char *ptr = url.c_str(); *ptr; ptr++) 00082 hash = (int)((hash * 3 + (*(unsigned char *)ptr)) % CACHE_TABLE_SIZE); 00083 00084 return hash; 00085 } 00086 00087 HTTPCacheTable::HTTPCacheTable(const string &cache_root, int block_size) : 00088 d_cache_root(cache_root), d_block_size(block_size), d_current_size(0), d_new_entries(0) 00089 { 00090 d_cache_index = cache_root + CACHE_INDEX; 00091 00092 d_cache_table = new CacheEntries*[CACHE_TABLE_SIZE]; 00093 00094 // Initialize the cache table. 00095 for (int i = 0; i < CACHE_TABLE_SIZE; ++i) 00096 d_cache_table[i] = 0; 00097 00098 cache_index_read(); 00099 } 00100 00104 static inline void 00105 delete_cache_entry(HTTPCacheTable::CacheEntry *e) 00106 { 00107 DBG2(cerr << "Deleting CacheEntry: " << e << endl); 00108 delete e; 00109 } 00110 00111 HTTPCacheTable::~HTTPCacheTable() 00112 { 00113 for (int i = 0; i < CACHE_TABLE_SIZE; ++i) { 00114 HTTPCacheTable::CacheEntries *cp = get_cache_table()[i]; 00115 if (cp) { 00116 // delete each entry 00117 for_each(cp->begin(), cp->end(), delete_cache_entry); 00118 00119 // now delete the vector that held the entries 00120 delete get_cache_table()[i]; 00121 get_cache_table()[i] = 0; 00122 } 00123 } 00124 00125 delete[] d_cache_table; 00126 } 00127 00135 class DeleteExpired : public unary_function<HTTPCacheTable::CacheEntry *&, void> { 00136 time_t d_time; 00137 HTTPCacheTable &d_table; 00138 00139 public: 00140 DeleteExpired(HTTPCacheTable &table, time_t t) : 00141 d_time(t), d_table(table) { 00142 if (!t) 00143 d_time = time(0); // 0 == now 00144 } 00145 00146 void operator()(HTTPCacheTable::CacheEntry *&e) { 00147 if (e && !e->readers && (e->freshness_lifetime 00148 < (e->corrected_initial_age + (d_time - e->response_time)))) { 00149 DBG(cerr << "Deleting expired cache entry: " << e->url << endl); 00150 d_table.remove_cache_entry(e); 00151 delete e; e = 0; 00152 } 00153 } 00154 }; 00155 00156 // @param time base deletes againt this time, defaults to 0 (now) 00157 void HTTPCacheTable::delete_expired_entries(time_t time) { 00158 // Walk through and delete all the expired entries. 00159 for (int cnt = 0; cnt < CACHE_TABLE_SIZE; cnt++) { 00160 HTTPCacheTable::CacheEntries *slot = get_cache_table()[cnt]; 00161 if (slot) { 00162 for_each(slot->begin(), slot->end(), DeleteExpired(*this, time)); 00163 slot->erase(remove(slot->begin(), slot->end(), 00164 static_cast<HTTPCacheTable::CacheEntry *>(0)), slot->end()); 00165 } 00166 } 00167 } 00168 00175 class DeleteByHits : public unary_function<HTTPCacheTable::CacheEntry *&, void> { 00176 HTTPCacheTable &d_table; 00177 int d_hits; 00178 00179 public: 00180 DeleteByHits(HTTPCacheTable &table, int hits) : 00181 d_table(table), d_hits(hits) { 00182 } 00183 00184 void operator()(HTTPCacheTable::CacheEntry *&e) { 00185 if (e && !e->readers && e->hits <= d_hits) { 00186 DBG(cerr << "Deleting cache entry: " << e->url << endl); 00187 d_table.remove_cache_entry(e); 00188 delete e; e = 0; 00189 } 00190 } 00191 }; 00192 00193 void 00194 HTTPCacheTable::delete_by_hits(int hits) { 00195 for (int cnt = 0; cnt < CACHE_TABLE_SIZE; cnt++) { 00196 if (get_cache_table()[cnt]) { 00197 HTTPCacheTable::CacheEntries *slot = get_cache_table()[cnt]; 00198 for_each(slot->begin(), slot->end(), DeleteByHits(*this, hits)); 00199 slot->erase(remove(slot->begin(), slot->end(), 00200 static_cast<HTTPCacheTable::CacheEntry*>(0)), 00201 slot->end()); 00202 00203 } 00204 } 00205 } 00206 00211 class DeleteBySize : public unary_function<HTTPCacheTable::CacheEntry *&, void> { 00212 HTTPCacheTable &d_table; 00213 unsigned int d_size; 00214 00215 public: 00216 DeleteBySize(HTTPCacheTable &table, unsigned int size) : 00217 d_table(table), d_size(size) { 00218 } 00219 00220 void operator()(HTTPCacheTable::CacheEntry *&e) { 00221 if (e && !e->readers && e->size > d_size) { 00222 DBG(cerr << "Deleting cache entry: " << e->url << endl); 00223 d_table.remove_cache_entry(e); 00224 delete e; e = 0; 00225 } 00226 } 00227 }; 00228 00229 void HTTPCacheTable::delete_by_size(unsigned int size) { 00230 for (int cnt = 0; cnt < CACHE_TABLE_SIZE; cnt++) { 00231 if (get_cache_table()[cnt]) { 00232 HTTPCacheTable::CacheEntries *slot = get_cache_table()[cnt]; 00233 for_each(slot->begin(), slot->end(), DeleteBySize(*this, size)); 00234 slot->erase(remove(slot->begin(), slot->end(), 00235 static_cast<HTTPCacheTable::CacheEntry*>(0)), 00236 slot->end()); 00237 00238 } 00239 } 00240 } 00241 00248 00255 bool 00256 HTTPCacheTable::cache_index_delete() 00257 { 00258 d_new_entries = 0; 00259 00260 return (REMOVE_BOOL(d_cache_index.c_str()) == 0); 00261 } 00262 00271 bool 00272 HTTPCacheTable::cache_index_read() 00273 { 00274 FILE *fp = fopen(d_cache_index.c_str(), "r"); 00275 // If the cache index can't be opened that's OK; start with an empty 00276 // cache. 09/05/02 jhrg 00277 if (!fp) { 00278 return false; 00279 } 00280 00281 char line[1024]; 00282 while (!feof(fp) && fgets(line, 1024, fp)) { 00283 add_entry_to_cache_table(cache_index_parse_line(line)); 00284 DBG2(cerr << line << endl); 00285 } 00286 00287 int res = fclose(fp) ; 00288 if (res) { 00289 DBG(cerr << "HTTPCache::cache_index_read - Failed to close " << (void *)fp << endl); 00290 } 00291 00292 d_new_entries = 0; 00293 00294 return true; 00295 } 00296 00304 HTTPCacheTable::CacheEntry * 00305 HTTPCacheTable::cache_index_parse_line(const char *line) 00306 { 00307 // Read the line and create the cache object 00308 HTTPCacheTable::CacheEntry *entry = new HTTPCacheTable::CacheEntry; 00309 istringstream iss(line); 00310 iss >> entry->url; 00311 iss >> entry->cachename; 00312 00313 iss >> entry->etag; 00314 if (entry->etag == CACHE_EMPTY_ETAG) 00315 entry->etag = ""; 00316 00317 iss >> entry->lm; 00318 iss >> entry->expires; 00319 iss >> entry->size; 00320 iss >> entry->range; // range is not used. 10/02/02 jhrg 00321 00322 iss >> entry->hash; 00323 iss >> entry->hits; 00324 iss >> entry->freshness_lifetime; 00325 iss >> entry->response_time; 00326 iss >> entry->corrected_initial_age; 00327 00328 iss >> entry->must_revalidate; 00329 00330 return entry; 00331 } 00332 00335 class WriteOneCacheEntry : 00336 public unary_function<HTTPCacheTable::CacheEntry *, void> 00337 { 00338 00339 FILE *d_fp; 00340 00341 public: 00342 WriteOneCacheEntry(FILE *fp) : d_fp(fp) 00343 {} 00344 00345 void operator()(HTTPCacheTable::CacheEntry *e) 00346 { 00347 if (e && fprintf(d_fp, 00348 "%s %s %s %ld %ld %ld %c %d %d %ld %ld %ld %c\r\n", 00349 e->url.c_str(), 00350 e->cachename.c_str(), 00351 e->etag == "" ? CACHE_EMPTY_ETAG : e->etag.c_str(), 00352 (long)(e->lm), 00353 (long)(e->expires), 00354 e->size, 00355 e->range ? '1' : '0', // not used. 10/02/02 jhrg 00356 e->hash, 00357 e->hits, 00358 (long)(e->freshness_lifetime), 00359 (long)(e->response_time), 00360 (long)(e->corrected_initial_age), 00361 e->must_revalidate ? '1' : '0') < 0) 00362 throw Error("Cache Index. Error writing cache index\n"); 00363 } 00364 }; 00365 00375 void 00376 HTTPCacheTable::cache_index_write() 00377 { 00378 DBG(cerr << "Cache Index. Writing index " << d_cache_index << endl); 00379 00380 // Open the file for writing. 00381 FILE * fp = NULL; 00382 if ((fp = fopen(d_cache_index.c_str(), "wb")) == NULL) { 00383 throw Error(string("Cache Index. Can't open `") + d_cache_index 00384 + string("' for writing")); 00385 } 00386 00387 // Walk through the list and write it out. The format is really 00388 // simple as we keep it all in ASCII. 00389 00390 for (int cnt = 0; cnt < CACHE_TABLE_SIZE; cnt++) { 00391 HTTPCacheTable::CacheEntries *cp = get_cache_table()[cnt]; 00392 if (cp) 00393 for_each(cp->begin(), cp->end(), WriteOneCacheEntry(fp)); 00394 } 00395 00396 /* Done writing */ 00397 int res = fclose(fp); 00398 if (res) { 00399 DBG(cerr << "HTTPCache::cache_index_write - Failed to close " 00400 << (void *)fp << endl); 00401 } 00402 00403 d_new_entries = 0; 00404 } 00405 00407 00420 string 00421 HTTPCacheTable::create_hash_directory(int hash) 00422 { 00423 struct stat stat_info; 00424 ostringstream path; 00425 00426 path << d_cache_root << hash; 00427 string p = path.str(); 00428 00429 if (stat(p.c_str(), &stat_info) == -1) { 00430 DBG2(cerr << "Cache....... Create dir " << p << endl); 00431 if (MKDIR(p.c_str(), 0777) < 0) { 00432 DBG2(cerr << "Cache....... Can't create..." << endl); 00433 throw Error("Could not create cache slot to hold response! Check the write permissions on your disk cache directory. Cache root: " + d_cache_root + "."); 00434 } 00435 } 00436 else { 00437 DBG2(cerr << "Cache....... Directory " << p << " already exists" 00438 << endl); 00439 } 00440 00441 return p; 00442 } 00443 00458 void 00459 HTTPCacheTable::create_location(HTTPCacheTable::CacheEntry *entry) 00460 { 00461 string hash_dir = create_hash_directory(entry->hash); 00462 #ifdef WIN32 00463 hash_dir += "\\dodsXXXXXX"; 00464 #else 00465 hash_dir += "/dodsXXXXXX"; // mkstemp uses six characters. 00466 #endif 00467 00468 // mkstemp uses the storage passed to it; must be writable and local. 00469 // char *templat = new char[hash_dir.size() + 1]; 00470 vector<char> templat(hash_dir.size() + 1); 00471 strncpy(&templat[0], hash_dir.c_str(), hash_dir.size() + 1); 00472 00473 // Open truncated for update. NB: mkstemp() returns a file descriptor. 00474 // man mkstemp says "... The file is opened with the O_EXCL flag, 00475 // guaranteeing that when mkstemp returns successfully we are the only 00476 // user." 09/19/02 jhrg 00477 #ifndef WIN32 00478 // Make sure that temp files are accessible only by the owner. 00479 umask(077); 00480 #endif 00481 int fd = MKSTEMP(&templat[0]); // fd mode is 666 or 600 (Unix) 00482 if (fd < 0) { 00483 // delete[] templat; templat = 0; 00484 close(fd); 00485 throw Error("The HTTP Cache could not create a file to hold the response; it will not be cached."); 00486 } 00487 00488 entry->cachename = &templat[0]; 00489 // delete[] templat; templat = 0; 00490 close(fd); 00491 } 00492 00493 00495 static inline int 00496 entry_disk_space(int size, unsigned int block_size) 00497 { 00498 unsigned int num_of_blocks = (size + block_size) / block_size; 00499 00500 DBG(cerr << "size: " << size << ", block_size: " << block_size 00501 << ", num_of_blocks: " << num_of_blocks << endl); 00502 00503 return num_of_blocks * block_size; 00504 } 00505 00509 00515 void 00516 HTTPCacheTable::add_entry_to_cache_table(CacheEntry *entry) 00517 { 00518 int hash = entry->hash; 00519 00520 if (!d_cache_table[hash]) 00521 d_cache_table[hash] = new CacheEntries; 00522 00523 d_cache_table[hash]->push_back(entry); 00524 00525 DBG(cerr << "add_entry_to_cache_table, current_size: " << d_current_size 00526 << ", entry->size: " << entry->size << ", block size: " << d_block_size 00527 << endl); 00528 00529 d_current_size += entry_disk_space(entry->size, d_block_size); 00530 00531 DBG(cerr << "add_entry_to_cache_table, current_size: " << d_current_size << endl); 00532 00533 increment_new_entries(); 00534 } 00535 00539 HTTPCacheTable::CacheEntry * 00540 HTTPCacheTable::get_locked_entry_from_cache_table(const string &url) /*const*/ 00541 { 00542 return get_locked_entry_from_cache_table(get_hash(url), url); 00543 } 00544 00552 HTTPCacheTable::CacheEntry * 00553 HTTPCacheTable::get_locked_entry_from_cache_table(int hash, const string &url) /*const*/ 00554 { 00555 DBG(cerr << "url: " << url << "; hash: " << hash << endl); 00556 DBG(cerr << "d_cache_table: " << hex << d_cache_table << dec << endl); 00557 if (d_cache_table[hash]) { 00558 CacheEntries *cp = d_cache_table[hash]; 00559 for (CacheEntriesIter i = cp->begin(); i != cp->end(); ++i) { 00560 // Must test *i because perform_garbage_collection may have 00561 // removed this entry; the CacheEntry will then be null. 00562 if ((*i) && (*i)->url == url) { 00563 (*i)->lock_read_response(); // Lock the response 00564 return *i; 00565 } 00566 } 00567 } 00568 00569 return 0; 00570 } 00571 00578 HTTPCacheTable::CacheEntry * 00579 HTTPCacheTable::get_write_locked_entry_from_cache_table(const string &url) 00580 { 00581 int hash = get_hash(url); 00582 if (d_cache_table[hash]) { 00583 CacheEntries *cp = d_cache_table[hash]; 00584 for (CacheEntriesIter i = cp->begin(); i != cp->end(); ++i) { 00585 // Must test *i because perform_garbage_collection may have 00586 // removed this entry; the CacheEntry will then be null. 00587 if ((*i) && (*i)->url == url) { 00588 (*i)->lock_write_response(); // Lock the response 00589 return *i; 00590 } 00591 } 00592 } 00593 00594 return 0; 00595 } 00596 00604 void 00605 HTTPCacheTable::remove_cache_entry(HTTPCacheTable::CacheEntry *entry) 00606 { 00607 // This should never happen; all calls to this method are protected by 00608 // the caller, hence the InternalErr. 00609 if (entry->readers) 00610 throw InternalErr(__FILE__, __LINE__, "Tried to delete a cache entry that is in use."); 00611 00612 REMOVE(entry->cachename.c_str()); 00613 REMOVE(string(entry->cachename + CACHE_META).c_str()); 00614 00615 DBG(cerr << "remove_cache_entry, current_size: " << get_current_size() << endl); 00616 00617 unsigned int eds = entry_disk_space(entry->size, get_block_size()); 00618 set_current_size((eds > get_current_size()) ? 0 : get_current_size() - eds); 00619 00620 DBG(cerr << "remove_cache_entry, current_size: " << get_current_size() << endl); 00621 } 00622 00625 class DeleteCacheEntry: public unary_function<HTTPCacheTable::CacheEntry *&, void> 00626 { 00627 string d_url; 00628 HTTPCacheTable *d_cache_table; 00629 00630 public: 00631 DeleteCacheEntry(HTTPCacheTable *c, const string &url) 00632 : d_url(url), d_cache_table(c) 00633 {} 00634 00635 void operator()(HTTPCacheTable::CacheEntry *&e) 00636 { 00637 if (e && e->url == d_url) { 00638 e->lock_write_response(); 00639 d_cache_table->remove_cache_entry(e); 00640 e->unlock_write_response(); 00641 delete e; e = 0; 00642 } 00643 } 00644 }; 00645 00652 void 00653 HTTPCacheTable::remove_entry_from_cache_table(const string &url) 00654 { 00655 int hash = get_hash(url); 00656 if (d_cache_table[hash]) { 00657 CacheEntries *cp = d_cache_table[hash]; 00658 for_each(cp->begin(), cp->end(), DeleteCacheEntry(this, url)); 00659 cp->erase(remove(cp->begin(), cp->end(), static_cast<HTTPCacheTable::CacheEntry*>(0)), 00660 cp->end()); 00661 } 00662 } 00663 00666 class DeleteUnlockedCacheEntry: public unary_function<HTTPCacheTable::CacheEntry *&, void> { 00667 HTTPCacheTable &d_table; 00668 00669 public: 00670 DeleteUnlockedCacheEntry(HTTPCacheTable &t) : 00671 d_table(t) 00672 { 00673 } 00674 void operator()(HTTPCacheTable::CacheEntry *&e) 00675 { 00676 if (e) { 00677 d_table.remove_cache_entry(e); 00678 delete e; 00679 e = 0; 00680 } 00681 } 00682 }; 00683 00684 void HTTPCacheTable::delete_all_entries() 00685 { 00686 // Walk through the cache table and, for every entry in the cache, delete 00687 // it on disk and in the cache table. 00688 for (int cnt = 0; cnt < CACHE_TABLE_SIZE; cnt++) { 00689 HTTPCacheTable::CacheEntries *slot = get_cache_table()[cnt]; 00690 if (slot) { 00691 for_each(slot->begin(), slot->end(), DeleteUnlockedCacheEntry(*this)); 00692 slot->erase(remove(slot->begin(), slot->end(), static_cast<HTTPCacheTable::CacheEntry *> (0)), slot->end()); 00693 } 00694 } 00695 00696 cache_index_delete(); 00697 } 00698 00712 void 00713 HTTPCacheTable::calculate_time(HTTPCacheTable::CacheEntry *entry, int default_expiration, time_t request_time) 00714 { 00715 entry->response_time = time(NULL); 00716 time_t apparent_age = max(0, static_cast<int>(entry->response_time - entry->date)); 00717 time_t corrected_received_age = max(apparent_age, entry->age); 00718 time_t response_delay = entry->response_time - request_time; 00719 entry->corrected_initial_age = corrected_received_age + response_delay; 00720 00721 // Estimate an expires time using the max-age and expires time. If we 00722 // don't have an explicit expires time then set it to 10% of the LM date 00723 // (although max 24 h). If no LM date is available then use 24 hours. 00724 time_t freshness_lifetime = entry->max_age; 00725 if (freshness_lifetime < 0) { 00726 if (entry->expires < 0) { 00727 if (entry->lm < 0) { 00728 freshness_lifetime = default_expiration; 00729 } 00730 else { 00731 freshness_lifetime = LM_EXPIRATION(entry->date - entry->lm); 00732 } 00733 } 00734 else 00735 freshness_lifetime = entry->expires - entry->date; 00736 } 00737 00738 entry->freshness_lifetime = max(0, static_cast<int>(freshness_lifetime)); 00739 00740 DBG2(cerr << "Cache....... Received Age " << entry->age 00741 << ", corrected " << entry->corrected_initial_age 00742 << ", freshness lifetime " << entry->freshness_lifetime << endl); 00743 } 00744 00756 void HTTPCacheTable::parse_headers(HTTPCacheTable::CacheEntry *entry, unsigned long max_entry_size, 00757 const vector<string> &headers) 00758 { 00759 vector<string>::const_iterator i; 00760 for (i = headers.begin(); i != headers.end(); ++i) { 00761 // skip a blank header. 00762 if ((*i).empty()) 00763 continue; 00764 00765 string::size_type colon = (*i).find(':'); 00766 00767 // skip a header with no colon in it. 00768 if (colon == string::npos) 00769 continue; 00770 00771 string header = (*i).substr(0, (*i).find(':')); 00772 string value = (*i).substr((*i).find(": ") + 2); 00773 DBG2(cerr << "Header: " << header << endl);DBG2(cerr << "Value: " << value << endl); 00774 00775 if (header == "ETag") { 00776 entry->etag = value; 00777 } 00778 else if (header == "Last-Modified") { 00779 entry->lm = parse_time(value.c_str()); 00780 } 00781 else if (header == "Expires") { 00782 entry->expires = parse_time(value.c_str()); 00783 } 00784 else if (header == "Date") { 00785 entry->date = parse_time(value.c_str()); 00786 } 00787 else if (header == "Age") { 00788 entry->age = parse_time(value.c_str()); 00789 } 00790 else if (header == "Content-Length") { 00791 unsigned long clength = strtoul(value.c_str(), 0, 0); 00792 if (clength > max_entry_size) 00793 entry->set_no_cache(true); 00794 } 00795 else if (header == "Cache-Control") { 00796 // Ignored Cache-Control values: public, private, no-transform, 00797 // proxy-revalidate, s-max-age. These are used by shared caches. 00798 // See section 14.9 of RFC 2612. 10/02/02 jhrg 00799 if (value == "no-cache" || value == "no-store") 00800 // Note that we *can* store a 'no-store' response in volatile 00801 // memory according to RFC 2616 (section 14.9.2) but those 00802 // will be rare coming from DAP servers. 10/02/02 jhrg 00803 entry->set_no_cache(true); 00804 else if (value == "must-revalidate") 00805 entry->must_revalidate = true; 00806 else if (value.find("max-age") != string::npos) { 00807 string max_age = value.substr(value.find("=" + 1)); 00808 entry->max_age = parse_time(max_age.c_str()); 00809 } 00810 } 00811 } 00812 } 00813 00815 00816 // @TODO Change name to record locked response 00817 void HTTPCacheTable::bind_entry_to_data(HTTPCacheTable::CacheEntry *entry, FILE *body) { 00818 entry->hits++; // Mark hit 00819 d_locked_entries[body] = entry; // record lock, see release_cached_r... 00820 } 00821 00822 void HTTPCacheTable::uncouple_entry_from_data(FILE *body) { 00823 00824 HTTPCacheTable::CacheEntry *entry = d_locked_entries[body]; 00825 if (!entry) 00826 throw InternalErr("There is no cache entry for the response given."); 00827 00828 d_locked_entries.erase(body); 00829 entry->unlock_read_response(); 00830 00831 if (entry->readers < 0) 00832 throw InternalErr("An unlocked entry was released"); 00833 } 00834 00835 bool HTTPCacheTable::is_locked_read_responses() { 00836 return !d_locked_entries.empty(); 00837 } 00838 00839 } // namespace libdap