Objectively
Ultra-lightweight object oriented framework for GNU C.
Loading...
Searching...
No Matches
URLCache.c File Reference
#include <assert.h>
#include <ctype.h>
#include <stdlib.h>
#include "String.h"
#include "URLCache.h"

Go to the source code of this file.

Macros

#define _Class   _URLCache
 
#define URLCACHE_DEFAULT_MAX_SIZE   (1024 * 1024)
 

Functions

Class_URLCache (void)
 
static bool cacheControlDisablesCaching (const char *value)
 Returns true if the provided Cache-Control value disables caching.
 
static URLCachedResponsecachedResponseForRequest (URLCache *self, const URLRequest *request)
 
static void dealloc (Object *self)
 
static void evictIfNeeded (URLCache *self)
 Removes the oldest cached response while this cache exceeds its maximum size.
 
static const StringheaderValueForField (const Dictionary *headers, const char *field)
 Returns the value for the specified HTTP header field, case-insensitively.
 
static URLCacheinit (URLCache *self)
 
static void initialize (Class *clazz)
 
static void removeAllCachedResponses (URLCache *self)
 
static void removeCachedResponseForRequest (URLCache *self, const URLRequest *request)
 
static bool requestIsCacheable (const URLRequest *request)
 Returns true if this request is cacheable.
 
static bool responseIsCacheable (const URLResponse *response)
 Returns true if this response is cacheable.
 
static void setMaxSize (URLCache *self, size_t maxSize)
 
static void storeCachedResponseForRequest (URLCache *self, const URLRequest *request, const URLResponse *response, const Data *data)
 
static bool stringEqualsIgnoreCase (const char *a, const char *b)
 Tests two C strings for case-insensitive equality.
 
static bool stringEqualsIgnoreCaseN (const char *a, const char *b, size_t length)
 Tests the first length characters of two C strings for case-insensitive equality.
 

Macro Definition Documentation

◆ _Class

#define _Class   _URLCache

Definition at line 31 of file URLCache.c.

◆ URLCACHE_DEFAULT_MAX_SIZE

#define URLCACHE_DEFAULT_MAX_SIZE   (1024 * 1024)

Definition at line 33 of file URLCache.c.

Function Documentation

◆ _URLCache()

Class * _URLCache ( void  )

Definition at line 411 of file URLCache.c.

411 {
412 static Class *clazz;
413 static Once once;
414
415 do_once(&once, {
416 clazz = _initialize(&(const ClassDef) {
417 .name = "URLCache",
418 .superclass = _Object(),
419 .instanceSize = sizeof(URLCache),
420 .interfaceOffset = offsetof(URLCache, interface),
421 .interfaceSize = sizeof(URLCacheInterface),
423 });
424 });
425
426 return clazz;
427}
Class * _initialize(const ClassDef *def)
Initializes the given Class.
Definition Class.c:86
Class * _Object(void)
Definition Object.c:136
static void initialize(Class *clazz)
Definition URLCache.c:395
long Once
The Once type.
Definition Once.h:37
#define do_once(once, block)
Executes the given block at most one time.
Definition Once.h:43
ClassDefs are passed to _initialize via an archetype to initialize a Class.
Definition Class.h:41
The runtime representation of a Class.
Definition Class.h:95
A cache for HTTP responses.
Definition URLCache.h:44

◆ cacheControlDisablesCaching()

static bool cacheControlDisablesCaching ( const char *  value)
static

Returns true if the provided Cache-Control value disables caching.

Definition at line 104 of file URLCache.c.

104 {
105
106 if (value == NULL) {
107 return false;
108 }
109
110 const char *cursor = value;
111 while (*cursor) {
112
113 while (*cursor == ' ' || *cursor == '\t' || *cursor == ',') {
114 cursor++;
115 }
116
117 const char *end = cursor;
118 while (*end && *end != ',') {
119 end++;
120 }
121
122 const char *start = cursor;
123 while (start < end && isspace((unsigned char) *start)) {
124 start++;
125 }
126
127 while (end > start && isspace((unsigned char) *(end - 1))) {
128 end--;
129 }
130
131 const size_t length = (size_t) (end - start);
132 if (length > 0) {
133
134 if (length == 8 && stringEqualsIgnoreCaseN(start, "no-cache", 8)) {
135 return true;
136 }
137
138 if (length == 8 && stringEqualsIgnoreCaseN(start, "no-store", 8)) {
139 return true;
140 }
141
142 if (length >= 8 && stringEqualsIgnoreCaseN(start, "max-age=", 8)) {
143 if (strtol(start + 8, NULL, 10) <= 0) {
144 return true;
145 }
146 }
147 }
148
149 cursor = end;
150 if (*cursor == ',') {
151 cursor++;
152 }
153 }
154
155 return false;
156}
static void start(Operation *self)
Definition Operation.c:171
static bool stringEqualsIgnoreCaseN(const char *a, const char *b, size_t length)
Tests the first length characters of two C strings for case-insensitive equality.
Definition URLCache.c:61

◆ cachedResponseForRequest()

static URLCachedResponse * cachedResponseForRequest ( URLCache self,
const URLRequest request 
)
static

Definition at line 268 of file URLCache.c.

268 {
269
270 if (requestIsCacheable(request) == false) {
271 return NULL;
272 }
273
274 URLCachedResponse *cachedResponse = NULL;
275 const Dictionary *dictionary = (Dictionary *) self->responses;
276 synchronized(self->lock, {
277 cachedResponse = $(dictionary, objectForKey, (ident) request);
278 if (cachedResponse) {
279 retain(cachedResponse);
280 }
281 });
282
283 return cachedResponse;
284}
ident retain(ident obj)
Atomically increment the given Object's reference count.
Definition Class.c:210
static Dictionary * dictionary(void)
Definition Dictionary.c:242
static ident objectForKey(const Dictionary *self, const ident key)
Definition Dictionary.c:439
static int request(RESTClient *self, HTTPMethod method, const char *url_string, const Data *body, Data **out_data)
Definition RESTClient.c:57
void * ident
The identity type, similar to Objective-C id.
Definition Types.h:49
static bool requestIsCacheable(const URLRequest *request)
Returns true if this request is cacheable.
Definition URLCache.c:161
Key-value stores.
Definition Dictionary.h:60
Lock * lock
The lock protecting this cache.
Definition URLCache.h:60
Dictionary * responses
The cached responses, keyed by URLRequest.
Definition URLCache.h:65
A cached HTTP response.

◆ dealloc()

static void dealloc ( Object self)
static
See also
Object::dealloc(Object *)

Definition at line 252 of file URLCache.c.

252 {
253
254 URLCache *this = (URLCache *) self;
255
256 release(this->lock);
257 release(this->responses);
258
259 super(Object, self, dealloc);
260}
ident release(ident obj)
Atomically decrement the given Object's reference count. If the resulting reference count is 0,...
Definition Class.c:195
#define super(type, obj, method,...)
static void lock(Lock *self)
Definition Lock.c:81
static void dealloc(Object *self)
Definition URLCache.c:252
Object is the root Class of The Objectively Class hierarchy.
Definition Object.h:46

◆ evictIfNeeded()

static void evictIfNeeded ( URLCache self)
static

Removes the oldest cached response while this cache exceeds its maximum size.

Definition at line 213 of file URLCache.c.

213 {
214
215 const Dictionary *dictionary = (Dictionary *) self->responses;
216
217 while (self->currentSize > self->maxSize && self->responses->count > 0) {
218
219 Array *keys = $(dictionary, allKeys);
220
221 const URLRequest *oldestKey = NULL;
222 URLCachedResponse *oldestResponse = NULL;
223
224 for (size_t i = 0; i < keys->count; i++) {
225
226 const URLRequest *key = $(keys, objectAtIndex, i);
227 URLCachedResponse *candidate = $(dictionary, objectForKey, (ident) key);
228
229 if (candidate && (oldestResponse == NULL
230 || $(candidate->timestamp, compareTo, oldestResponse->timestamp) == OrderAscending)) {
231 oldestKey = key;
232 oldestResponse = candidate;
233 }
234 }
235
236 release(keys);
237
238 if (oldestKey == NULL || oldestResponse == NULL) {
239 break;
240 }
241
242 self->currentSize -= oldestResponse->size;
243 $(self->responses, removeObjectForKey, (ident) oldestKey);
244 }
245}
static ident objectAtIndex(const Array *self, size_t index)
Definition Array.c:578
static Order compareTo(const Date *self, const Date *other)
Definition Date.c:74
static void removeObjectForKey(Dictionary *self, const ident key)
Definition Dictionary.c:547
static Array * allKeys(const Dictionary *self)
Definition Dictionary.c:193
@ OrderAscending
Definition Types.h:71
Arrays.
Definition Array.h:56
size_t count
The count of elements.
Definition Array.h:72
size_t count
The count of elements.
Definition Dictionary.h:82
size_t currentSize
The current cached body size.
Definition URLCache.h:70
size_t maxSize
The maximum cached body size.
Definition URLCache.h:75
Date * timestamp
The time this response was cached.
size_t size
The cached body size.
A protocol-agnostic abstraction for requesting resources via URLs.
Definition URLRequest.h:58

◆ headerValueForField()

static const String * headerValueForField ( const Dictionary headers,
const char *  field 
)
static

Returns the value for the specified HTTP header field, case-insensitively.

Definition at line 79 of file URLCache.c.

79 {
80
81 if (headers && field) {
82
83 Array *keys = $(headers, allKeys);
84 for (size_t i = 0; i < keys->count; i++) {
85
86 const String *key = $(keys, objectAtIndex, i);
87 if (stringEqualsIgnoreCase(key->chars, field)) {
88
89 const String *value = $(headers, objectForKey, (ident) key);
90 release(keys);
91 return value;
92 }
93 }
94
95 release(keys);
96 }
97
98 return NULL;
99}
static bool stringEqualsIgnoreCase(const char *a, const char *b)
Tests two C strings for case-insensitive equality.
Definition URLCache.c:40
UTF-8 strings.
Definition String.h:69
char * chars
The backing null-terminated UTF-8 encoded character array.
Definition String.h:85

◆ init()

static URLCache * init ( URLCache self)
static

Definition at line 290 of file URLCache.c.

290 {
291
292 self = (URLCache *) super(Object, self, init);
293 if (self) {
294 self->lock = $(alloc(Lock), init);
295 assert(self->lock);
296
297 self->responses = $(alloc(Dictionary), init);
298 assert(self->responses);
299
301 }
302
303 return self;
304}
#define alloc(type)
Allocate and initialize and instance of type.
Definition Class.h:176
#define URLCACHE_DEFAULT_MAX_SIZE
Definition URLCache.c:33
static URLCache * init(URLCache *self)
Definition URLCache.c:290
POSIX Threads locks.
Definition Lock.h:42

◆ initialize()

static void initialize ( Class clazz)
static
See also
Class::initialize(Class *)

Definition at line 395 of file URLCache.c.

395 {
396
397 ((ObjectInterface *) clazz->interface)->dealloc = dealloc;
398
399 ((URLCacheInterface *) clazz->interface)->cachedResponseForRequest = cachedResponseForRequest;
400 ((URLCacheInterface *) clazz->interface)->init = init;
401 ((URLCacheInterface *) clazz->interface)->removeAllCachedResponses = removeAllCachedResponses;
402 ((URLCacheInterface *) clazz->interface)->removeCachedResponseForRequest = removeCachedResponseForRequest;
403 ((URLCacheInterface *) clazz->interface)->setMaxSize = setMaxSize;
404 ((URLCacheInterface *) clazz->interface)->storeCachedResponseForRequest = storeCachedResponseForRequest;
405}
static URLCachedResponse * cachedResponseForRequest(URLCache *self, const URLRequest *request)
Definition URLCache.c:268
static void removeCachedResponseForRequest(URLCache *self, const URLRequest *request)
Definition URLCache.c:322
static void storeCachedResponseForRequest(URLCache *self, const URLRequest *request, const URLResponse *response, const Data *data)
Definition URLCache.c:356
static void setMaxSize(URLCache *self, size_t maxSize)
Definition URLCache.c:338
static void removeAllCachedResponses(URLCache *self)
Definition URLCache.c:310
ident interface
The interface of the Class.
Definition Class.h:105
Object * init(Object *self)
Initializes this Object.
Definition Object.c:83
void removeAllCachedResponses(URLCache *self)
Removes all cached responses.
Definition URLCache.c:310

◆ removeAllCachedResponses()

static void removeAllCachedResponses ( URLCache self)
static

Definition at line 310 of file URLCache.c.

310 {
311
312 synchronized(self->lock, {
313 $(self->responses, removeAllObjects);
314 self->currentSize = 0;
315 });
316}
static void removeAllObjects(Array *self)
Definition Array.c:604

◆ removeCachedResponseForRequest()

static void removeCachedResponseForRequest ( URLCache self,
const URLRequest request 
)
static

Definition at line 322 of file URLCache.c.

322 {
323
324 synchronized(self->lock, {
325 const Dictionary *dictionary = (Dictionary *) self->responses;
326 URLCachedResponse *cachedResponse = $(dictionary, objectForKey, (ident) request);
327 if (cachedResponse) {
328 self->currentSize -= cachedResponse->size;
330 }
331 });
332}

◆ requestIsCacheable()

static bool requestIsCacheable ( const URLRequest request)
static

Returns true if this request is cacheable.

Definition at line 161 of file URLCache.c.

161 {
162
163 if (request == NULL) {
164 return false;
165 }
166
167 switch (request->httpMethod) {
168 case HTTP_NONE:
169 case HTTP_GET:
170 break;
171 default:
172 return false;
173 }
174
175 const String *cacheControl = headerValueForField(request->httpHeaders, "Cache-Control");
176 if (cacheControl && cacheControlDisablesCaching(cacheControl->chars)) {
177 return false;
178 }
179
180 const String *pragma = headerValueForField(request->httpHeaders, "Pragma");
181 if (pragma && stringEqualsIgnoreCase(pragma->chars, "no-cache")) {
182 return false;
183 }
184
185 return true;
186}
static const String * headerValueForField(const Dictionary *headers, const char *field)
Returns the value for the specified HTTP header field, case-insensitively.
Definition URLCache.c:79
static bool cacheControlDisablesCaching(const char *value)
Returns true if the provided Cache-Control value disables caching.
Definition URLCache.c:104
@ HTTP_NONE
Definition URLRequest.h:43
@ HTTP_GET
Definition URLRequest.h:46

◆ responseIsCacheable()

static bool responseIsCacheable ( const URLResponse response)
static

Returns true if this response is cacheable.

Definition at line 191 of file URLCache.c.

191 {
192
193 if (response == NULL || response->httpStatusCode != 200) {
194 return false;
195 }
196
197 const String *cacheControl = headerValueForField(response->httpHeaders, "Cache-Control");
198 if (cacheControl && cacheControlDisablesCaching(cacheControl->chars)) {
199 return false;
200 }
201
202 const String *pragma = headerValueForField(response->httpHeaders, "Pragma");
203 if (pragma && stringEqualsIgnoreCase(pragma->chars, "no-cache")) {
204 return false;
205 }
206
207 return true;
208}
Dictionary * httpHeaders
The HTTP response headers.
Definition URLResponse.h:58
int httpStatusCode
The HTTP response status code.
Definition URLResponse.h:63

◆ setMaxSize()

static void setMaxSize ( URLCache self,
size_t  maxSize 
)
static

Definition at line 338 of file URLCache.c.

338 {
339
340 synchronized(self->lock, {
341 self->maxSize = maxSize;
342
343 if (self->maxSize == 0) {
344 $(self->responses, removeAllObjects);
345 self->currentSize = 0;
346 } else {
347 evictIfNeeded(self);
348 }
349 });
350}
static void evictIfNeeded(URLCache *self)
Removes the oldest cached response while this cache exceeds its maximum size.
Definition URLCache.c:213

◆ storeCachedResponseForRequest()

static void storeCachedResponseForRequest ( URLCache self,
const URLRequest request,
const URLResponse response,
const Data data 
)
static

Definition at line 356 of file URLCache.c.

357 {
358
359 if (requestIsCacheable(request) == false || responseIsCacheable(response) == false) {
360 return;
361 }
362
363 URLCachedResponse *cachedResponse = $(alloc(URLCachedResponse), initWithResponseData, response, data);
364 if (cachedResponse == NULL) {
365 return;
366 }
367
368 synchronized(self->lock, {
369 if (cachedResponse->size <= self->maxSize && self->maxSize > 0) {
370
371 const Dictionary *dictionary = (Dictionary *) self->responses;
373 if (existing) {
374 self->currentSize -= existing->size;
376 }
377
378 URLRequest *key = (URLRequest *) $((Object *) request, copy);
379 $(self->responses, setObjectForKey, cachedResponse, key);
380 release(key);
381
382 self->currentSize += cachedResponse->size;
383 evictIfNeeded(self);
384 }
385 });
386
387 release(cachedResponse);
388}
static Object * copy(const Object *self)
Definition Array.c:84
static Data * data(void)
Definition Data.c:286
static void setObjectForKey(Dictionary *self, const ident obj, const ident key)
Definition Dictionary.c:634
static bool responseIsCacheable(const URLResponse *response)
Returns true if this response is cacheable.
Definition URLCache.c:191
static URLCachedResponse * initWithResponseData(URLCachedResponse *self, const URLResponse *response, const Data *data)

◆ stringEqualsIgnoreCase()

static bool stringEqualsIgnoreCase ( const char *  a,
const char *  b 
)
static

Tests two C strings for case-insensitive equality.

Definition at line 40 of file URLCache.c.

40 {
41
42 if (a == NULL || b == NULL) {
43 return false;
44 }
45
46 while (*a && *b) {
47 if (tolower((unsigned char) *a) != tolower((unsigned char) *b)) {
48 return false;
49 }
50
51 a++;
52 b++;
53 }
54
55 return *a == 0 && *b == 0;
56}

◆ stringEqualsIgnoreCaseN()

static bool stringEqualsIgnoreCaseN ( const char *  a,
const char *  b,
size_t  length 
)
static

Tests the first length characters of two C strings for case-insensitive equality.

Definition at line 61 of file URLCache.c.

61 {
62
63 if (a == NULL || b == NULL) {
64 return false;
65 }
66
67 for (size_t i = 0; i < length; i++) {
68 if (tolower((unsigned char) a[i]) != tolower((unsigned char) b[i])) {
69 return false;
70 }
71 }
72
73 return true;
74}