libdap++  Updated for version 3.12.0
HTTPCacheTable.h
Go to the documentation of this file.
1 
2 // -*- mode: c++; c-basic-offset:4 -*-
3 
4 // This file is part of libdap, A C++ implementation of the OPeNDAP Data
5 // Access Protocol.
6 
7 // Copyright (c) 2008 OPeNDAP, Inc.
8 // Author: James Gallagher <jgallagher@opendap.org>
9 //
10 // This library is free software; you can redistribute it and/or
11 // modify it under the terms of the GNU Lesser General Public
12 // License as published by the Free Software Foundation; either
13 // version 2.1 of the License, or (at your option) any later version.
14 //
15 // This library is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 // Lesser General Public License for more details.
19 //
20 // You should have received a copy of the GNU Lesser General Public
21 // License along with this library; if not, write to the Free Software
22 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 //
24 // You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
25 
26 #ifndef _http_cache_table_h
27 #define _http_cache_table_h
28 
29 //#define DODS_DEBUG
30 
31 #include <pthread.h>
32 
33 #ifdef WIN32
34 #include <io.h> // stat for win32? 09/05/02 jhrg
35 #endif
36 
37 #include <string>
38 #include <vector>
39 #include <map>
40 
41 #ifndef _http_cache_h
42 #include "HTTPCache.h"
43 #endif
44 
45 #ifndef _error_h
46 #include "Error.h"
47 #endif
48 
49 #ifndef _internalerr_h
50 #include "InternalErr.h"
51 #endif
52 
53 #ifndef _util_h
54 #include "util.h"
55 #endif
56 
57 #ifndef _debug_h
58 #include "debug.h"
59 #endif
60 
61 #define LOCK(m) do { \
62  int code = pthread_mutex_lock((m)); \
63  if (code != 0) \
64  throw InternalErr(__FILE__, __LINE__, "Mutex lock: " + long_to_string(code)); \
65  } while(0);
66 
67 #define UNLOCK(m) do { \
68  int code = pthread_mutex_unlock((m)); \
69  if (code != 0) \
70  throw InternalErr(__FILE__, __LINE__, "Mutex unlock: " + long_to_string(code)); \
71  } while(0);
72 
73 #define TRYLOCK(m) pthread_mutex_trylock((m))
74 #define INIT(m) pthread_mutex_init((m), 0)
75 #define DESTROY(m) pthread_mutex_destroy((m))
76 
77 
78 using namespace std;
79 
80 namespace libdap
81 {
82 
83 int get_hash(const string &url);
84 
101 public:
113  struct CacheEntry {
114  private:
115  string url; // Location
116  int hash;
117  int hits; // Hit counts
118  string cachename;
119 
120  string etag;
121  time_t lm; // Last modified
122  time_t expires;
123  time_t date; // From the response header.
124  time_t age;
125  time_t max_age; // From Cache-Control
126 
127  unsigned long size; // Size of cached entity body
128  bool range; // Range is not currently supported. 10/02/02 jhrg
129 
130  time_t freshness_lifetime;
131  time_t response_time;
132  time_t corrected_initial_age;
133 
134  bool must_revalidate;
135  bool no_cache; // This field is not saved in the index.
136 
137  int readers;
138  pthread_mutex_t d_response_lock; // set if being read
139  pthread_mutex_t d_response_write_lock; // set if being written
140 
141  // Allow HTTPCacheTable methods access and the test class, too
142  friend class HTTPCacheTable;
143  friend class HTTPCacheTest;
144 
145  // Allow access by the functors used in HTTPCacheTable
146  friend class DeleteCacheEntry;
147  friend class WriteOneCacheEntry;
148  friend class DeleteExpired;
149  friend class DeleteByHits;
150  friend class DeleteBySize;
151 
152  public:
153  string get_cachename()
154  {
155  return cachename;
156  }
157  string get_etag()
158  {
159  return etag;
160  }
161  time_t get_lm()
162  {
163  return lm;
164  }
165  time_t get_expires()
166  {
167  return expires;
168  }
169  time_t get_max_age()
170  {
171  return max_age;
172  }
173  void set_size(unsigned long sz)
174  {
175  size = sz;
176  }
178  {
179  return freshness_lifetime;
180  }
182  {
183  return response_time;
184  }
186  {
187  return corrected_initial_age;
188  }
190  {
191  return must_revalidate;
192  }
193  void set_no_cache(bool state)
194  {
195  no_cache = state;
196  }
197  bool is_no_cache()
198  {
199  return no_cache;
200  }
201 
203  {
204  DBG(cerr << "Try locking read response... (" << hex << &d_response_lock << dec << ") ");
205  int status = TRYLOCK(&d_response_lock);
206  if (status != 0 /*&& status == EBUSY*/) {
207  // If locked, wait for any writers
208  LOCK(&d_response_write_lock);
209  UNLOCK(&d_response_write_lock);
210  };
211  DBGN(cerr << "Done" << endl);
212  readers++; // REcord number of readers
213  }
214 
216  {
217  readers--;
218  if (readers == 0) {
219  DBG(cerr << "Unlocking read response... (" << hex << &d_response_lock << dec << ") ");
220  UNLOCK(&d_response_lock);
221  DBGN(cerr << "Done" << endl);
222  }
223  }
224 
226  {
227  DBG(cerr << "locking write response... (" << hex << &d_response_lock << dec << ") ");
228  LOCK(&d_response_lock);
229  LOCK(&d_response_write_lock);
230  DBGN(cerr << "Done" << endl);
231  }
232 
234  {
235  DBG(cerr << "Unlocking write response... (" << hex << &d_response_lock << dec << ") ");
236  UNLOCK(&d_response_write_lock);
237  UNLOCK(&d_response_lock);
238  DBGN(cerr << "Done" << endl);
239  }
240 
242  url(""), hash(-1), hits(0), cachename(""), etag(""), lm(-1), expires(-1), date(-1), age(-1), max_age(-1),
243  size(0), range(false), freshness_lifetime(0), response_time(0), corrected_initial_age(0),
244  must_revalidate(false), no_cache(false), readers(0)
245  {
246  INIT(&d_response_lock);
247  INIT(&d_response_write_lock);
248  }
249  CacheEntry(const string &u) :
250  url(u), hash(-1), hits(0), cachename(""), etag(""), lm(-1), expires(-1), date(-1), age(-1), max_age(-1),
251  size(0), range(false), freshness_lifetime(0), response_time(0), corrected_initial_age(0),
252  must_revalidate(false), no_cache(false), readers(0)
253  {
254  INIT(&d_response_lock);
255  INIT(&d_response_write_lock);
256  hash = get_hash(url);
257  }
258  };
259 
260  // Typedefs for CacheTable. A CacheTable is a vector of vectors of
261  // CacheEntries. The outer vector is accessed using the hash value.
262  // Entries with matching hashes occupy successive positions in the inner
263  // vector (that's how hash collisions are resolved). Search the inner
264  // vector for a specific match.
265  typedef vector<CacheEntry *> CacheEntries;
266  typedef CacheEntries::iterator CacheEntriesIter;
267 
268  typedef CacheEntries **CacheTable;// Array of pointers to CacheEntries
269 
270  friend class HTTPCacheTest;
271 
272 private:
273  CacheTable d_cache_table;
274 
275  string d_cache_root;
276  unsigned int d_block_size; // File block size.
277  unsigned long d_current_size;
278 
279  string d_cache_index;
280  int d_new_entries;
281 
282  map<FILE *, HTTPCacheTable::CacheEntry *> d_locked_entries;
283 
284  // Make these private to prevent use
286  {
287  throw InternalErr(__FILE__, __LINE__, "unimplemented");
288  }
289 
290  HTTPCacheTable &operator=(const HTTPCacheTable &)
291  {
292  throw InternalErr(__FILE__, __LINE__, "unimplemented");
293  }
294 
295  HTTPCacheTable()
296  {
297  throw InternalErr(__FILE__, __LINE__, "unimplemented");
298  }
299 
300  CacheTable &get_cache_table()
301  {
302  return d_cache_table;
303  }
304  CacheEntry *get_locked_entry_from_cache_table(int hash, const string &url); /*const*/
305 
306 public:
307  HTTPCacheTable(const string &cache_root, int block_size);
308  ~HTTPCacheTable();
309 
311  unsigned long get_current_size() const
312  {
313  return d_current_size;
314  }
315  void set_current_size(unsigned long sz)
316  {
317  d_current_size = sz;
318  }
319 
320  unsigned int get_block_size() const
321  {
322  return d_block_size;
323  }
324  void set_block_size(unsigned int sz)
325  {
326  d_block_size = sz;
327  }
328 
329  int get_new_entries() const
330  {
331  return d_new_entries;
332  }
334  {
335  ++d_new_entries;
336  }
337 
338  string get_cache_root()
339  {
340  return d_cache_root;
341  }
342  void set_cache_root(const string &cr)
343  {
344  d_cache_root = cr;
345  }
347 
348  void delete_expired_entries(time_t time = 0);
349  void delete_by_hits(int hits);
350  void delete_by_size(unsigned int size);
351  void delete_all_entries();
352 
353  bool cache_index_delete();
354  bool cache_index_read();
355  CacheEntry *cache_index_parse_line(const char *line);
356  void cache_index_write();
357 
358  string create_hash_directory(int hash);
359  void create_location(CacheEntry *entry);
360 
361  void add_entry_to_cache_table(CacheEntry *entry);
362  void remove_cache_entry(HTTPCacheTable::CacheEntry *entry);
363 
364  void remove_entry_from_cache_table(const string &url);
365  CacheEntry *get_locked_entry_from_cache_table(const string &url);
366  CacheEntry *get_write_locked_entry_from_cache_table(const string &url);
367 
368  void calculate_time(HTTPCacheTable::CacheEntry *entry, int default_expiration, time_t request_time);
369  void parse_headers(HTTPCacheTable::CacheEntry *entry, unsigned long max_entry_size, const vector<string> &headers);
370 
371  // These should move back to HTTPCache
372  void bind_entry_to_data(CacheEntry *entry, FILE *body);
373  void uncouple_entry_from_data(FILE *body);
374  bool is_locked_read_responses();
375 };
376 
377 } // namespace libdap
378 #endif