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