libdap  Updated for version 3.17.0
HTTPCacheTable.h
00001 // -*- mode: c++; c-basic-offset:4 -*-
00002 
00003 // This file is part of libdap, A C++ implementation of the OPeNDAP Data
00004 // Access Protocol.
00005 
00006 // Copyright (c) 2008 OPeNDAP, Inc.
00007 // Author: James Gallagher <jgallagher@opendap.org>
00008 //
00009 // This library is free software; you can redistribute it and/or
00010 // modify it under the terms of the GNU Lesser General Public
00011 // License as published by the Free Software Foundation; either
00012 // version 2.1 of the License, or (at your option) any later version.
00013 //
00014 // This library is distributed in the hope that it will be useful,
00015 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00016 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017 // Lesser General Public License for more details.
00018 //
00019 // You should have received a copy of the GNU Lesser General Public
00020 // License along with this library; if not, write to the Free Software
00021 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
00022 //
00023 // You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
00024 
00025 #ifndef _http_cache_table_h
00026 #define _http_cache_table_h
00027 
00028 //#define DODS_DEBUG
00029 
00030 #include <pthread.h>
00031 
00032 #ifdef WIN32
00033 #include <io.h>   // stat for win32? 09/05/02 jhrg
00034 #endif
00035 
00036 #include <cstring>
00037 
00038 #include <string>
00039 #include <vector>
00040 #include <map>
00041 
00042 #ifndef _http_cache_h
00043 #include "HTTPCache.h"
00044 #endif
00045 
00046 #ifndef _error_h
00047 #include "Error.h"
00048 #endif
00049 
00050 #ifndef _internalerr_h
00051 #include "InternalErr.h"
00052 #endif
00053 
00054 #ifndef _util_h
00055 #include "util.h"
00056 #endif
00057 
00058 #ifndef _debug_h
00059 #include "debug.h"
00060 #endif
00061 
00062  //long_to_string(code));
00063 #define LOCK(m) do { \
00064         int code = pthread_mutex_lock((m)); \
00065         if (code != 0) \
00066                 throw InternalErr(__FILE__, __LINE__, string("Mutex lock: ") + strerror(code)); \
00067     } while(0);
00068 
00069 //+ long_to_string(code));
00070 #define UNLOCK(m) do { \
00071         int code = pthread_mutex_unlock((m)); \
00072         if (code != 0) \
00073                 throw InternalErr(__FILE__, __LINE__, string("Mutex unlock: ") + strerror(code)); \
00074     } while(0);
00075 
00076 #define TRYLOCK(m) pthread_mutex_trylock((m))
00077 #define INIT(m) pthread_mutex_init((m), 0)
00078 #define DESTROY(m) pthread_mutex_destroy((m))
00079 
00080 //using namespace std;
00081 
00082 namespace libdap {
00083 
00084 int get_hash(const string &url);
00085 
00101 class HTTPCacheTable {
00102 public:
00114     struct CacheEntry {
00115     private:
00116         string url; // Location
00117         int hash;
00118         int hits; // Hit counts
00119         string cachename;
00120 
00121         string etag;
00122         time_t lm; // Last modified
00123         time_t expires;
00124         time_t date; // From the response header.
00125         time_t age;
00126         time_t max_age; // From Cache-Control
00127 
00128         unsigned long size; // Size of cached entity body
00129         bool range; // Range is not currently supported. 10/02/02 jhrg
00130 
00131         time_t freshness_lifetime;
00132         time_t response_time;
00133         time_t corrected_initial_age;
00134 
00135         bool must_revalidate;
00136         bool no_cache; // This field is not saved in the index.
00137 
00138         int readers;
00139         pthread_mutex_t d_response_lock; // set if being read
00140         pthread_mutex_t d_response_write_lock; // set if being written
00141 
00142         // Allow HTTPCacheTable methods access and the test class, too
00143         friend class HTTPCacheTable;
00144         friend class HTTPCacheTest;
00145 
00146         // Allow access by the functors used in HTTPCacheTable
00147         friend class DeleteCacheEntry;
00148         friend class WriteOneCacheEntry;
00149         friend class DeleteExpired;
00150         friend class DeleteByHits;
00151         friend class DeleteBySize;
00152 
00153     public:
00154         string get_cachename()
00155         {
00156             return cachename;
00157         }
00158         string get_etag()
00159         {
00160             return etag;
00161         }
00162         time_t get_lm()
00163         {
00164             return lm;
00165         }
00166         time_t get_expires()
00167         {
00168             return expires;
00169         }
00170         time_t get_max_age()
00171         {
00172             return max_age;
00173         }
00174         void set_size(unsigned long sz)
00175         {
00176             size = sz;
00177         }
00178         time_t get_freshness_lifetime()
00179         {
00180             return freshness_lifetime;
00181         }
00182         time_t get_response_time()
00183         {
00184             return response_time;
00185         }
00186         time_t get_corrected_initial_age()
00187         {
00188             return corrected_initial_age;
00189         }
00190         bool get_must_revalidate()
00191         {
00192             return must_revalidate;
00193         }
00194         void set_no_cache(bool state)
00195         {
00196             no_cache = state;
00197         }
00198         bool is_no_cache()
00199         {
00200             return no_cache;
00201         }
00202 
00203         void lock_read_response()
00204         {
00205             DBG(cerr << "Try locking read response... (" << hex << &d_response_lock << dec << ") ");
00206             int status = TRYLOCK(&d_response_lock);
00207             if (status != 0 /*&& status == EBUSY*/) {
00208                 // If locked, wait for any writers
00209                 LOCK(&d_response_write_lock);
00210                 UNLOCK(&d_response_write_lock);
00211             }
00212 
00213             readers++; // Record number of readers
00214 
00215             DBGN(cerr << "Done" << endl);
00216 
00217         }
00218 
00219         void unlock_read_response()
00220         {
00221             readers--;
00222             if (readers == 0) {
00223                 DBG(cerr << "Unlocking read response... (" << hex << &d_response_lock << dec << ") ");
00224                 UNLOCK(&d_response_lock); DBGN(cerr << "Done" << endl);
00225             }
00226         }
00227 
00228         void lock_write_response()
00229         {
00230             DBG(cerr << "locking write response... (" << hex << &d_response_lock << dec << ") ");
00231             LOCK(&d_response_lock);
00232             LOCK(&d_response_write_lock); DBGN(cerr << "Done" << endl);
00233         }
00234 
00235         void unlock_write_response()
00236         {
00237             DBG(cerr << "Unlocking write response... (" << hex << &d_response_lock << dec << ") ");
00238             UNLOCK(&d_response_write_lock);
00239             UNLOCK(&d_response_lock); DBGN(cerr << "Done" << endl);
00240         }
00241 
00242         CacheEntry() :
00243             url(""), hash(-1), hits(0), cachename(""), etag(""), lm(-1), expires(-1), date(-1), age(-1), max_age(-1), size(
00244                 0), range(false), freshness_lifetime(0), response_time(0), corrected_initial_age(0), must_revalidate(
00245                 false), no_cache(false), readers(0)
00246         {
00247             INIT(&d_response_lock);
00248             INIT(&d_response_write_lock);
00249         }
00250         CacheEntry(const string &u) :
00251             url(u), hash(-1), hits(0), cachename(""), etag(""), lm(-1), expires(-1), date(-1), age(-1), max_age(-1), size(
00252                 0), range(false), freshness_lifetime(0), response_time(0), corrected_initial_age(0), must_revalidate(
00253                 false), no_cache(false), readers(0)
00254         {
00255             INIT(&d_response_lock);
00256             INIT(&d_response_write_lock);
00257             hash = get_hash(url);
00258         }
00259     };
00260 
00261     // Typedefs for CacheTable. A CacheTable is a vector of vectors of
00262     // CacheEntries. The outer vector is accessed using the hash value.
00263     // Entries with matching hashes occupy successive positions in the inner
00264     // vector (that's how hash collisions are resolved). Search the inner
00265     // vector for a specific match.
00266     typedef vector<CacheEntry *> CacheEntries;
00267     typedef CacheEntries::iterator CacheEntriesIter;
00268 
00269     typedef CacheEntries **CacheTable;    // Array of pointers to CacheEntries
00270 
00271     friend class HTTPCacheTest;
00272 
00273 private:
00274     CacheTable d_cache_table;
00275 
00276     string d_cache_root;
00277     unsigned int d_block_size; // File block size.
00278     unsigned long d_current_size;
00279 
00280     string d_cache_index;
00281     int d_new_entries;
00282 
00283     map<FILE *, HTTPCacheTable::CacheEntry *> d_locked_entries;
00284 
00285     // Make these private to prevent use
00286     HTTPCacheTable(const HTTPCacheTable &);
00287     HTTPCacheTable &operator=(const HTTPCacheTable &);
00288     HTTPCacheTable();
00289 
00290     CacheTable &get_cache_table()
00291     {
00292         return d_cache_table;
00293     }
00294 
00295     CacheEntry *get_locked_entry_from_cache_table(int hash, const string &url); /*const*/
00296 
00297 public:
00298     HTTPCacheTable(const string &cache_root, int block_size);
00299     ~HTTPCacheTable();
00300 
00302     unsigned long get_current_size() const
00303     {
00304         return d_current_size;
00305     }
00306     void set_current_size(unsigned long sz)
00307     {
00308         d_current_size = sz;
00309     }
00310 
00311     unsigned int get_block_size() const
00312     {
00313         return d_block_size;
00314     }
00315     void set_block_size(unsigned int sz)
00316     {
00317         d_block_size = sz;
00318     }
00319 
00320     int get_new_entries() const
00321     {
00322         return d_new_entries;
00323     }
00324     void increment_new_entries()
00325     {
00326         ++d_new_entries;
00327     }
00328 
00329     string get_cache_root()
00330     {
00331         return d_cache_root;
00332     }
00333     void set_cache_root(const string &cr)
00334     {
00335         d_cache_root = cr;
00336     }
00338 
00339     void delete_expired_entries(time_t time = 0);
00340     void delete_by_hits(int hits);
00341     void delete_by_size(unsigned int size);
00342     void delete_all_entries();
00343 
00344     bool cache_index_delete();
00345     bool cache_index_read();
00346     CacheEntry *cache_index_parse_line(const char *line);
00347     void cache_index_write();
00348 
00349     string create_hash_directory(int hash);
00350     void create_location(CacheEntry *entry);
00351 
00352     void add_entry_to_cache_table(CacheEntry *entry);
00353     void remove_cache_entry(HTTPCacheTable::CacheEntry *entry);
00354 
00355     void remove_entry_from_cache_table(const string &url);
00356     CacheEntry *get_locked_entry_from_cache_table(const string &url);
00357     CacheEntry *get_write_locked_entry_from_cache_table(const string &url);
00358 
00359     void calculate_time(HTTPCacheTable::CacheEntry *entry, int default_expiration, time_t request_time);
00360     void parse_headers(HTTPCacheTable::CacheEntry *entry, unsigned long max_entry_size, const vector<string> &headers);
00361 
00362     // These should move back to HTTPCache
00363     void bind_entry_to_data(CacheEntry *entry, FILE *body);
00364     void uncouple_entry_from_data(FILE *body);
00365     bool is_locked_read_responses();
00366 };
00367 
00368 } // namespace libdap
00369 #endif