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 
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