libdap Updated for version 3.21.1
libdap4 is an implementation of OPeNDAP's DAP protocol.
HTTPCacheTable.h
Go to the documentation of this file.
1// -*- mode: c++; c-basic-offset:4 -*-
2
3// This file is part of libdap, A C++ implementation of the OPeNDAP Data
4// Access Protocol.
5
6// Copyright (c) 2008 OPeNDAP, Inc.
7// Author: James Gallagher <jgallagher@opendap.org>
8//
9// This library is free software; you can redistribute it and/or
10// modify it under the terms of the GNU Lesser General Public
11// License as published by the Free Software Foundation; either
12// version 2.1 of the License, or (at your option) any later version.
13//
14// This library is distributed in the hope that it will be useful,
15// but WITHOUT ANY WARRANTY; without even the implied warranty of
16// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17// Lesser General Public License for more details.
18//
19// You should have received a copy of the GNU Lesser General Public
20// License along with this library; if not, write to the Free Software
21// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22//
23// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
24
25#ifndef _http_cache_table_h
26#define _http_cache_table_h
27
28#include <map>
29#include <mutex>
30#include <string>
31#include <vector>
32
33// These are defined in HTTPCache.cc. jhrg 2/21/23
34extern const std::string CACHE_META;
35extern const std::string CACHE_INDEX;
36extern const std::string CACHE_EMPTY_ETAG;
37
38#define NO_LM_EXPIRATION (24 * 3600) // 24 hours
39#define MAX_LM_EXPIRATION (48 * 3600) // Max expiration from LM
40
41// If using LM to find the expiration then take 10% and no more than
42// MAX_LM_EXPIRATION.
43#ifndef LM_EXPIRATION
44#define LM_EXPIRATION(t) (min((MAX_LM_EXPIRATION), static_cast<int>((t) / 10)))
45#endif
46
47namespace libdap {
48
49int get_hash(const std::string &url);
50
67public:
79 struct CacheEntry {
80 private:
81 std::string url; // Location
82 int hash = -1;
83 int hits = 0; // Hit counts
84 std::string cachename;
85
86 std::string etag;
87 time_t lm = -1; // Last modified
88 time_t expires = -1;
89 time_t date = -1; // From the response header.
90 time_t age = -1;
91 time_t max_age = -1; // From Cache-Control
92
93 unsigned long size = 0; // Size of cached entity body
94 bool range = false; // Range is not currently supported. 10/02/02 jhrg
95
96 time_t freshness_lifetime = 0;
97 time_t response_time = 0;
98 time_t corrected_initial_age = 0;
99
100 bool must_revalidate = false;
101 bool no_cache = false; // This field is not saved in the index.
102
103 int readers = 0;
104 std::mutex d_readers_lock;
105 std::mutex d_response_read_lock; // set if being read
106 std::mutex d_response_write_lock; // set if being written
107
108 // Allow HTTPCacheTable methods access and the test class, too
109 friend class HTTPCacheTable;
110 friend class HTTPCacheTest;
111
112 // Allow access by the functors used in HTTPCacheTable
113 friend class WriteOneCacheEntry;
114
115 public:
116 std::string get_cachename() const { return cachename; }
117
118 std::string get_etag() const { return etag; }
119
120 time_t get_lm() const { return lm; }
121
122 time_t get_expires() const { return expires; }
123
124 time_t get_max_age() const { return max_age; }
125
126 void set_size(unsigned long sz) { size = sz; }
127
128 time_t get_freshness_lifetime() const { return freshness_lifetime; }
129
130 time_t get_response_time() const { return response_time; }
131
132 time_t get_corrected_initial_age() const { return corrected_initial_age; }
133
134 bool get_must_revalidate() const { return must_revalidate; }
135
136 void set_no_cache(bool state) { no_cache = state; }
137
138 bool is_no_cache() const { return no_cache; }
139
141 // if the response_lock cannot be acquired, it might be a reader or a writer. If it is a writer, then
142 // we need to wait for the writer to finish. If it is a reader, then we don't care and increment the
143 // reader count.
144 if (d_response_read_lock.try_lock()) {
145 d_response_write_lock.lock();
146 d_response_write_lock.unlock();
147 }
148 std::lock_guard<std::mutex> lock(d_readers_lock);
149 readers++; // Record number of readers
150 }
151
153 std::lock_guard<std::mutex> lock(d_readers_lock);
154 readers--;
155 if (readers == 0) {
156 d_response_read_lock.unlock();
157 }
158 }
159
160 void lock_write_response() { std::lock(d_response_read_lock, d_response_write_lock); }
161
163 d_response_read_lock.unlock();
164 d_response_write_lock.unlock();
165 }
166
167 CacheEntry() = default;
168
169 explicit CacheEntry(std::string u) : url(std::move(u)) { hash = get_hash(url); }
170 }; // CacheEntry
171
172 // Typedefs for CacheTable. A CacheTable is a vector of vectors of
173 // CacheEntries. The outer vector is accessed using the hash value.
174 // Entries with matching hashes occupy successive positions in the inner
175 // vector (that's how hash collisions are resolved). Search the inner
176 // vector for a specific match.
177
178 using CacheEntries = std::vector<CacheEntry *>;
179 using CacheTable = std::vector<CacheEntries>;
180
181 friend class HTTPCacheTest;
182
183private:
184 CacheTable d_cache_table;
185
186 std::string d_cache_root;
187 unsigned int d_block_size; // File block size.
188 unsigned long d_current_size = 0;
189
190 std::string d_cache_index;
191 int d_new_entries = 0;
192
193 std::map<FILE *, HTTPCacheTable::CacheEntry *> d_locked_entries;
194
195 CacheEntry *get_read_locked_entry_from_cache_table(int hash, const std::string &url);
196 bool cache_index_delete();
197 bool cache_index_read();
198 CacheEntry *cache_index_parse_line(const char *line);
199 std::string create_hash_directory(int hash);
200 void remove_cache_entry(const HTTPCacheTable::CacheEntry *entry);
201
202public:
203 HTTPCacheTable(const std::string &cache_root, int block_size);
206 HTTPCacheTable() = delete;
207
208 virtual ~HTTPCacheTable();
209
211 unsigned long get_current_size() const { return d_current_size; }
212
213 void set_current_size(unsigned long sz) { d_current_size = sz; }
214
215 unsigned int get_block_size() const { return d_block_size; }
216
217 void set_block_size(unsigned int sz) { d_block_size = sz; }
218
219 int get_new_entries() const { return d_new_entries; }
220
221 void increment_new_entries() { ++d_new_entries; }
222
223 std::string get_cache_root() const { return d_cache_root; }
224
225 void set_cache_root(const std::string &cr) { d_cache_root = cr; }
227
228 void delete_expired_entries(time_t time = 0);
229 void delete_by_hits(int hits);
230 void delete_by_size(unsigned long size);
231 void delete_all_entries();
232
233 void create_location(CacheEntry *entry);
234 void add_entry_to_cache_table(CacheEntry *entry);
235 void cache_index_write();
236 void remove_entry_from_cache_table(const std::string &url);
237
238 CacheEntry *get_read_locked_entry_from_cache_table(const std::string &url);
239 CacheEntry *get_write_locked_entry_from_cache_table(const std::string &url);
240
241 void calculate_time(HTTPCacheTable::CacheEntry *entry, int default_expiration, time_t request_time);
242 void parse_headers(HTTPCacheTable::CacheEntry *entry, unsigned long max_entry_size,
243 const std::vector<std::string> &headers);
244
245 // These should move back to HTTPCache
246 void bind_entry_to_data(CacheEntry *entry, FILE *body);
247 void uncouple_entry_from_data(FILE *body);
248
249 bool is_locked_read_responses() const;
250};
251
252#if 0
262class cache_entry_guard {
263public:
264 enum class operation { read, write };
265
266private:
267 operation d_op;
268 HTTPCacheTable::CacheEntry *d_entry;
269 bool d_managed = true; // Use this so instances can go out of scope without releasing the lock.
270 bool d_delete_after_unlock = false;
271public:
272 cache_entry_guard() = delete;
273
274 cache_entry_guard(const cache_entry_guard &) = delete;
275
276 cache_entry_guard &operator=(const cache_entry_guard &) = delete;
277
278 HTTPCacheTable::CacheEntry* &operator->() {
279 return d_entry;
280 }
281
283 cache_entry_guard(operation op, HTTPCacheTable::CacheEntry *entry) : d_op(op), d_entry(entry) {
284 }
285
286 ~cache_entry_guard() {
287 if (d_managed) {
288 if (d_op == operation::read)
289 d_entry->unlock_read_response();
290 else
291 d_entry->unlock_write_response();
292
293 if (d_delete_after_unlock)
294 delete d_entry;
295 }
296 }
297
299 void release() { d_managed = false; }
300
302 void delete_after() { d_delete_after_unlock = true; }
303};
304#endif
305
306} // namespace libdap
307#endif
const string CACHE_INDEX
Definition HTTPCache.cc:58
const string CACHE_EMPTY_ETAG
Definition HTTPCache.cc:61
const string CACHE_META
Definition HTTPCache.cc:60
void create_location(CacheEntry *entry)
void calculate_time(HTTPCacheTable::CacheEntry *entry, int default_expiration, time_t request_time)
void delete_expired_entries(time_t time=0)
Delete all the expired entries in the cache.
void bind_entry_to_data(CacheEntry *entry, FILE *body)
std::vector< CacheEntry * > CacheEntries
void set_cache_root(const std::string &cr)
unsigned int get_block_size() const
void set_block_size(unsigned int sz)
void remove_entry_from_cache_table(const std::string &url)
void delete_by_size(unsigned long size)
Delete all the entries in the cache that are larger than size bytes.
void delete_by_hits(int hits)
Delete all the entries in the cache that have fewer than hits hits.
HTTPCacheTable & operator=(const HTTPCacheTable &)=delete
std::string get_cache_root() const
void add_entry_to_cache_table(CacheEntry *entry)
HTTPCacheTable(const std::string &cache_root, int block_size)
void set_current_size(unsigned long sz)
std::vector< CacheEntries > CacheTable
HTTPCacheTable(const HTTPCacheTable &)=delete
unsigned long get_current_size() const
CacheEntry * get_write_locked_entry_from_cache_table(const std::string &url)
void uncouple_entry_from_data(FILE *body)
void parse_headers(HTTPCacheTable::CacheEntry *entry, unsigned long max_entry_size, const std::vector< std::string > &headers)
bool is_locked_read_responses() const
top level DAP object to house generic methods
Definition AISConnect.cc:30
int get_hash(const string &url)