libdap++  Updated for version 3.8.2
HTTPCacheTable.cc
Go to the documentation of this file.
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