Blame HTTPCacheTable.h

Packit a4aae4
// -*- mode: c++; c-basic-offset:4 -*-
Packit a4aae4
Packit a4aae4
// This file is part of libdap, A C++ implementation of the OPeNDAP Data
Packit a4aae4
// Access Protocol.
Packit a4aae4
Packit a4aae4
// Copyright (c) 2008 OPeNDAP, Inc.
Packit a4aae4
// Author: James Gallagher <jgallagher@opendap.org>
Packit a4aae4
//
Packit a4aae4
// This library is free software; you can redistribute it and/or
Packit a4aae4
// modify it under the terms of the GNU Lesser General Public
Packit a4aae4
// License as published by the Free Software Foundation; either
Packit a4aae4
// version 2.1 of the License, or (at your option) any later version.
Packit a4aae4
//
Packit a4aae4
// This library is distributed in the hope that it will be useful,
Packit a4aae4
// but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit a4aae4
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit a4aae4
// Lesser General Public License for more details.
Packit a4aae4
//
Packit a4aae4
// You should have received a copy of the GNU Lesser General Public
Packit a4aae4
// License along with this library; if not, write to the Free Software
Packit a4aae4
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
Packit a4aae4
//
Packit a4aae4
// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
Packit a4aae4
Packit a4aae4
#ifndef _http_cache_table_h
Packit a4aae4
#define _http_cache_table_h
Packit a4aae4
Packit a4aae4
//#define DODS_DEBUG
Packit a4aae4
Packit a4aae4
#include <pthread.h>
Packit a4aae4
Packit a4aae4
#ifdef WIN32
Packit a4aae4
#include <io.h>   // stat for win32? 09/05/02 jhrg
Packit a4aae4
#endif
Packit a4aae4
Packit a4aae4
#include <cstring>
Packit a4aae4
Packit a4aae4
#include <string>
Packit a4aae4
#include <vector>
Packit a4aae4
#include <map>
Packit a4aae4
Packit a4aae4
#ifndef _http_cache_h
Packit a4aae4
#include "HTTPCache.h"
Packit a4aae4
#endif
Packit a4aae4
Packit a4aae4
#ifndef _error_h
Packit a4aae4
#include "Error.h"
Packit a4aae4
#endif
Packit a4aae4
Packit a4aae4
#ifndef _internalerr_h
Packit a4aae4
#include "InternalErr.h"
Packit a4aae4
#endif
Packit a4aae4
Packit a4aae4
#ifndef _util_h
Packit a4aae4
#include "util.h"
Packit a4aae4
#endif
Packit a4aae4
Packit a4aae4
#ifndef _debug_h
Packit a4aae4
#include "debug.h"
Packit a4aae4
#endif
Packit a4aae4
Packit a4aae4
 //long_to_string(code));
Packit a4aae4
#define LOCK(m) do { \
Packit a4aae4
	int code = pthread_mutex_lock((m)); \
Packit a4aae4
	if (code != 0) \
Packit a4aae4
		throw InternalErr(__FILE__, __LINE__, string("Mutex lock: ") + strerror(code)); \
Packit a4aae4
    } while(0);
Packit a4aae4
Packit a4aae4
//+ long_to_string(code));
Packit a4aae4
#define UNLOCK(m) do { \
Packit a4aae4
	int code = pthread_mutex_unlock((m)); \
Packit a4aae4
	if (code != 0) \
Packit a4aae4
		throw InternalErr(__FILE__, __LINE__, string("Mutex unlock: ") + strerror(code)); \
Packit a4aae4
    } while(0);
Packit a4aae4
Packit a4aae4
#define TRYLOCK(m) pthread_mutex_trylock((m))
Packit a4aae4
#define INIT(m) pthread_mutex_init((m), 0)
Packit a4aae4
#define DESTROY(m) pthread_mutex_destroy((m))
Packit a4aae4
Packit a4aae4
//using namespace std;
Packit a4aae4
Packit a4aae4
namespace libdap {
Packit a4aae4
Packit a4aae4
int get_hash(const string &url;;
Packit a4aae4
Packit a4aae4
/** The table of entries in the client-side cache. This class maintains a table
Packit a4aae4
 of CacheEntries, where one instance of CacheEntry is made for
Packit a4aae4
 each item in the cache. When an item is accessed it is either
Packit a4aae4
 locked for reading or writing. When locked for reading the entry is
Packit a4aae4
 recorded on a list of read-locked entries. The caller must explicitly
Packit a4aae4
 free the entry for it to be removed from this list (which is the only
Packit a4aae4
 way it can be opened for writing). An entry can be accessed by multiple
Packit a4aae4
 readers but only one writer.
Packit a4aae4
Packit a4aae4
 @note The CacheEntry class used to contain a lock that was used to ensure
Packit a4aae4
 that the entry was locked during any changes to any of its fields. That
Packit a4aae4
 has been removed - its now the responsibility of the caller. This change
Packit a4aae4
 was made because it's likely the caller will need to lock all of the methods
Packit a4aae4
 that operate on a CacheEntry anyway, so the CacheEntry-specific lock was
Packit a4aae4
 redundant. */
Packit a4aae4
class HTTPCacheTable {
Packit a4aae4
public:
Packit a4aae4
    /** A struct used to store information about responses in the
Packit a4aae4
     cache's volatile memory.
Packit a4aae4
Packit a4aae4
     About entry locking: An entry is locked using both a mutex and a
Packit a4aae4
     counter. The counter keeps track of how many clients are accessing a
Packit a4aae4
     given entry while the mutex provides a guarantee that updates to the
Packit a4aae4
     counter are MT-safe. In addition, the HTTPCacheTable object maintains a
Packit a4aae4
     map which binds the FILE* returned to a client with a given entry.
Packit a4aae4
     This way the client can tell the HTTPCacheTable object that it is done
Packit a4aae4
     with FILE *response and the class can arrange to update
Packit a4aae4
     the lock counter and mutex. */
Packit a4aae4
    struct CacheEntry {
Packit a4aae4
    private:
Packit a4aae4
        string url; // Location
Packit a4aae4
        int hash;
Packit a4aae4
        int hits; // Hit counts
Packit a4aae4
        string cachename;
Packit a4aae4
Packit a4aae4
        string etag;
Packit a4aae4
        time_t lm; // Last modified
Packit a4aae4
        time_t expires;
Packit a4aae4
        time_t date; // From the response header.
Packit a4aae4
        time_t age;
Packit a4aae4
        time_t max_age; // From Cache-Control
Packit a4aae4
Packit a4aae4
        unsigned long size; // Size of cached entity body
Packit a4aae4
        bool range; // Range is not currently supported. 10/02/02 jhrg
Packit a4aae4
Packit a4aae4
        time_t freshness_lifetime;
Packit a4aae4
        time_t response_time;
Packit a4aae4
        time_t corrected_initial_age;
Packit a4aae4
Packit a4aae4
        bool must_revalidate;
Packit a4aae4
        bool no_cache; // This field is not saved in the index.
Packit a4aae4
Packit a4aae4
        int readers;
Packit a4aae4
        pthread_mutex_t d_response_lock; // set if being read
Packit a4aae4
        pthread_mutex_t d_response_write_lock; // set if being written
Packit a4aae4
Packit a4aae4
        // Allow HTTPCacheTable methods access and the test class, too
Packit a4aae4
        friend class HTTPCacheTable;
Packit a4aae4
        friend class HTTPCacheTest;
Packit a4aae4
Packit a4aae4
        // Allow access by the functors used in HTTPCacheTable
Packit a4aae4
        friend class DeleteCacheEntry;
Packit a4aae4
        friend class WriteOneCacheEntry;
Packit a4aae4
        friend class DeleteExpired;
Packit a4aae4
        friend class DeleteByHits;
Packit a4aae4
        friend class DeleteBySize;
Packit a4aae4
Packit a4aae4
    public:
Packit a4aae4
        string get_cachename()
Packit a4aae4
        {
Packit a4aae4
            return cachename;
Packit a4aae4
        }
Packit a4aae4
        string get_etag()
Packit a4aae4
        {
Packit a4aae4
            return etag;
Packit a4aae4
        }
Packit a4aae4
        time_t get_lm()
Packit a4aae4
        {
Packit a4aae4
            return lm;
Packit a4aae4
        }
Packit a4aae4
        time_t get_expires()
Packit a4aae4
        {
Packit a4aae4
            return expires;
Packit a4aae4
        }
Packit a4aae4
        time_t get_max_age()
Packit a4aae4
        {
Packit a4aae4
            return max_age;
Packit a4aae4
        }
Packit a4aae4
        void set_size(unsigned long sz)
Packit a4aae4
        {
Packit a4aae4
            size = sz;
Packit a4aae4
        }
Packit a4aae4
        time_t get_freshness_lifetime()
Packit a4aae4
        {
Packit a4aae4
            return freshness_lifetime;
Packit a4aae4
        }
Packit a4aae4
        time_t get_response_time()
Packit a4aae4
        {
Packit a4aae4
            return response_time;
Packit a4aae4
        }
Packit a4aae4
        time_t get_corrected_initial_age()
Packit a4aae4
        {
Packit a4aae4
            return corrected_initial_age;
Packit a4aae4
        }
Packit a4aae4
        bool get_must_revalidate()
Packit a4aae4
        {
Packit a4aae4
            return must_revalidate;
Packit a4aae4
        }
Packit a4aae4
        void set_no_cache(bool state)
Packit a4aae4
        {
Packit a4aae4
            no_cache = state;
Packit a4aae4
        }
Packit a4aae4
        bool is_no_cache()
Packit a4aae4
        {
Packit a4aae4
            return no_cache;
Packit a4aae4
        }
Packit a4aae4
Packit a4aae4
        void lock_read_response()
Packit a4aae4
        {
Packit a4aae4
            DBG(cerr << "Try locking read response... (" << hex << &d_response_lock << dec << ") ");
Packit a4aae4
            int status = TRYLOCK(&d_response_lock);
Packit a4aae4
            if (status != 0 /*&& status == EBUSY*/) {
Packit a4aae4
                // If locked, wait for any writers
Packit a4aae4
                LOCK(&d_response_write_lock);
Packit a4aae4
                UNLOCK(&d_response_write_lock);
Packit a4aae4
            }
Packit a4aae4
Packit a4aae4
            readers++; // Record number of readers
Packit a4aae4
Packit a4aae4
            DBGN(cerr << "Done" << endl);
Packit a4aae4
Packit a4aae4
        }
Packit a4aae4
Packit a4aae4
        void unlock_read_response()
Packit a4aae4
        {
Packit a4aae4
            readers--;
Packit a4aae4
            if (readers == 0) {
Packit a4aae4
                DBG(cerr << "Unlocking read response... (" << hex << &d_response_lock << dec << ") ");
Packit a4aae4
                UNLOCK(&d_response_lock); DBGN(cerr << "Done" << endl);
Packit a4aae4
            }
Packit a4aae4
        }
Packit a4aae4
Packit a4aae4
        void lock_write_response()
Packit a4aae4
        {
Packit a4aae4
            DBG(cerr << "locking write response... (" << hex << &d_response_lock << dec << ") ");
Packit a4aae4
            LOCK(&d_response_lock);
Packit a4aae4
            LOCK(&d_response_write_lock); DBGN(cerr << "Done" << endl);
Packit a4aae4
        }
Packit a4aae4
Packit a4aae4
        void unlock_write_response()
Packit a4aae4
        {
Packit a4aae4
            DBG(cerr << "Unlocking write response... (" << hex << &d_response_lock << dec << ") ");
Packit a4aae4
            UNLOCK(&d_response_write_lock);
Packit a4aae4
            UNLOCK(&d_response_lock); DBGN(cerr << "Done" << endl);
Packit a4aae4
        }
Packit a4aae4
Packit a4aae4
        CacheEntry() :
Packit a4aae4
            url(""), hash(-1), hits(0), cachename(""), etag(""), lm(-1), expires(-1), date(-1), age(-1), max_age(-1), size(
Packit a4aae4
                0), range(false), freshness_lifetime(0), response_time(0), corrected_initial_age(0), must_revalidate(
Packit a4aae4
                false), no_cache(false), readers(0)
Packit a4aae4
        {
Packit a4aae4
            INIT(&d_response_lock);
Packit a4aae4
            INIT(&d_response_write_lock);
Packit a4aae4
        }
Packit a4aae4
        CacheEntry(const string &u) :
Packit a4aae4
            url(u), hash(-1), hits(0), cachename(""), etag(""), lm(-1), expires(-1), date(-1), age(-1), max_age(-1), size(
Packit a4aae4
                0), range(false), freshness_lifetime(0), response_time(0), corrected_initial_age(0), must_revalidate(
Packit a4aae4
                false), no_cache(false), readers(0)
Packit a4aae4
        {
Packit a4aae4
            INIT(&d_response_lock);
Packit a4aae4
            INIT(&d_response_write_lock);
Packit a4aae4
            hash = get_hash(url);
Packit a4aae4
        }
Packit a4aae4
    };
Packit a4aae4
Packit a4aae4
    // Typedefs for CacheTable. A CacheTable is a vector of vectors of
Packit a4aae4
    // CacheEntries. The outer vector is accessed using the hash value.
Packit a4aae4
    // Entries with matching hashes occupy successive positions in the inner
Packit a4aae4
    // vector (that's how hash collisions are resolved). Search the inner
Packit a4aae4
    // vector for a specific match.
Packit a4aae4
    typedef vector<CacheEntry *> CacheEntries;
Packit a4aae4
    typedef CacheEntries::iterator CacheEntriesIter;
Packit a4aae4
Packit a4aae4
    typedef CacheEntries **CacheTable;    // Array of pointers to CacheEntries
Packit a4aae4
Packit a4aae4
    friend class HTTPCacheTest;
Packit a4aae4
Packit a4aae4
private:
Packit a4aae4
    CacheTable d_cache_table;
Packit a4aae4
Packit a4aae4
    string d_cache_root;
Packit a4aae4
    unsigned int d_block_size; // File block size.
Packit a4aae4
    unsigned long d_current_size;
Packit a4aae4
Packit a4aae4
    string d_cache_index;
Packit a4aae4
    int d_new_entries;
Packit a4aae4
Packit a4aae4
    map<FILE *, HTTPCacheTable::CacheEntry *> d_locked_entries;
Packit a4aae4
Packit a4aae4
    // Make these private to prevent use
Packit a4aae4
    HTTPCacheTable(const HTTPCacheTable &);
Packit a4aae4
    HTTPCacheTable &operator=(const HTTPCacheTable &);
Packit a4aae4
    HTTPCacheTable();
Packit a4aae4
Packit a4aae4
    CacheTable &get_cache_table()
Packit a4aae4
    {
Packit a4aae4
        return d_cache_table;
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    CacheEntry *get_locked_entry_from_cache_table(int hash, const string &url;; /*const*/
Packit a4aae4
Packit a4aae4
public:
Packit a4aae4
    HTTPCacheTable(const string &cache_root, int block_size);
Packit a4aae4
    ~HTTPCacheTable();
Packit a4aae4
Packit a4aae4
    //@{ @name Accessors/Mutators
Packit a4aae4
    unsigned long get_current_size() const
Packit a4aae4
    {
Packit a4aae4
        return d_current_size;
Packit a4aae4
    }
Packit a4aae4
    void set_current_size(unsigned long sz)
Packit a4aae4
    {
Packit a4aae4
        d_current_size = sz;
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    unsigned int get_block_size() const
Packit a4aae4
    {
Packit a4aae4
        return d_block_size;
Packit a4aae4
    }
Packit a4aae4
    void set_block_size(unsigned int sz)
Packit a4aae4
    {
Packit a4aae4
        d_block_size = sz;
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    int get_new_entries() const
Packit a4aae4
    {
Packit a4aae4
        return d_new_entries;
Packit a4aae4
    }
Packit a4aae4
    void increment_new_entries()
Packit a4aae4
    {
Packit a4aae4
        ++d_new_entries;
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    string get_cache_root()
Packit a4aae4
    {
Packit a4aae4
        return d_cache_root;
Packit a4aae4
    }
Packit a4aae4
    void set_cache_root(const string &cr)
Packit a4aae4
    {
Packit a4aae4
        d_cache_root = cr;
Packit a4aae4
    }
Packit a4aae4
    //@}
Packit a4aae4
Packit a4aae4
    void delete_expired_entries(time_t time = 0);
Packit a4aae4
    void delete_by_hits(int hits);
Packit a4aae4
    void delete_by_size(unsigned int size);
Packit a4aae4
    void delete_all_entries();
Packit a4aae4
Packit a4aae4
    bool cache_index_delete();
Packit a4aae4
    bool cache_index_read();
Packit a4aae4
    CacheEntry *cache_index_parse_line(const char *line);
Packit a4aae4
    void cache_index_write();
Packit a4aae4
Packit a4aae4
    string create_hash_directory(int hash);
Packit a4aae4
    void create_location(CacheEntry *entry);
Packit a4aae4
Packit a4aae4
    void add_entry_to_cache_table(CacheEntry *entry);
Packit a4aae4
    void remove_cache_entry(HTTPCacheTable::CacheEntry *entry);
Packit a4aae4
Packit a4aae4
    void remove_entry_from_cache_table(const string &url;;
Packit a4aae4
    CacheEntry *get_locked_entry_from_cache_table(const string &url;;
Packit a4aae4
    CacheEntry *get_write_locked_entry_from_cache_table(const string &url;;
Packit a4aae4
Packit a4aae4
    void calculate_time(HTTPCacheTable::CacheEntry *entry, int default_expiration, time_t request_time);
Packit a4aae4
    void parse_headers(HTTPCacheTable::CacheEntry *entry, unsigned long max_entry_size, const vector<string> &headers);
Packit a4aae4
Packit a4aae4
    // These should move back to HTTPCache
Packit a4aae4
    void bind_entry_to_data(CacheEntry *entry, FILE *body);
Packit a4aae4
    void uncouple_entry_from_data(FILE *body);
Packit a4aae4
    bool is_locked_read_responses();
Packit a4aae4
};
Packit a4aae4
Packit a4aae4
} // namespace libdap
Packit a4aae4
#endif