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