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