summaryrefslogtreecommitdiff
path: root/libsoup/soup-cache.c
diff options
context:
space:
mode:
Diffstat (limited to 'libsoup/soup-cache.c')
-rw-r--r--libsoup/soup-cache.c121
1 files changed, 102 insertions, 19 deletions
diff --git a/libsoup/soup-cache.c b/libsoup/soup-cache.c
index c6a349cc..c1d69131 100644
--- a/libsoup/soup-cache.c
+++ b/libsoup/soup-cache.c
@@ -42,6 +42,7 @@ typedef struct _SoupCacheEntry
gboolean writing;
gboolean dirty;
gboolean got_body;
+ gboolean being_validated;
SoupMessageHeaders *headers;
GOutputStream *stream;
GError *error;
@@ -110,14 +111,18 @@ get_cacheability (SoupCache *cache, SoupMessage *msg)
switch (msg->status_code) {
case SOUP_STATUS_PARTIAL_CONTENT:
- case SOUP_STATUS_NOT_MODIFIED:
/* We don't cache partial responses, but they only
* invalidate cached full responses if the headers
- * don't match. Likewise with 304 Not Modified.
+ * don't match.
*/
cacheability = SOUP_CACHE_UNCACHEABLE;
break;
+ case SOUP_STATUS_NOT_MODIFIED:
+ /* A 304 response validates an existing cache entry */
+ cacheability = SOUP_CACHE_VALIDATES;
+ break;
+
case SOUP_STATUS_MULTIPLE_CHOICES:
case SOUP_STATUS_MOVED_PERMANENTLY:
case SOUP_STATUS_GONE:
@@ -186,6 +191,15 @@ copy_headers (const char *name, const char *value, SoupMessageHeaders *headers)
soup_message_headers_append (headers, name, value);
}
+static void
+update_headers (const char *name, const char *value, SoupMessageHeaders *headers)
+{
+ if (soup_message_headers_get (headers, name))
+ soup_message_headers_replace (headers, name, value);
+ else
+ soup_message_headers_append (headers, name, value);
+}
+
static guint
soup_cache_entry_get_current_age (SoupCacheEntry *entry)
{
@@ -310,6 +324,7 @@ soup_cache_entry_new (SoupCache *cache, SoupMessage *msg)
entry->dirty = TRUE;
entry->writing = FALSE;
entry->got_body = FALSE;
+ entry->being_validated = FALSE;
entry->data = g_string_new (NULL);
entry->pos = 0;
entry->error = NULL;
@@ -611,6 +626,24 @@ msg_got_headers_cb (SoupMessage *msg, SoupCache *cache)
key = soup_message_get_cache_key (msg);
soup_cache_entry_delete_by_key (cache, key);
g_free (key);
+ } else if (cacheable & SOUP_CACHE_VALIDATES) {
+ char *key;
+ SoupCacheEntry *entry;
+
+ key = soup_message_get_cache_key (msg);
+ entry = soup_cache_lookup_uri (cache, key);
+ g_free (key);
+
+ g_return_if_fail (entry);
+
+ entry->being_validated = FALSE;
+
+ /* We update the headers of the existing cache item,
+ plus its age */
+ soup_message_headers_foreach (msg->response_headers,
+ (SoupMessageHeadersForeachFunc)update_headers,
+ entry->headers);
+ soup_cache_entry_set_freshness (entry, msg);
}
}
@@ -649,13 +682,20 @@ soup_cache_send_response (SoupCache *cache, SoupMessage *msg)
SoupCacheEntry *entry;
char *current_age;
+ g_return_if_fail (SOUP_IS_CACHE (cache));
+ g_return_if_fail (SOUP_IS_MESSAGE (msg));
+
key = soup_message_get_cache_key (msg);
entry = soup_cache_lookup_uri (cache, key);
g_return_if_fail (entry);
+ /* If we are told to send a response from cache any validation
+ in course is over by now */
+ entry->being_validated = FALSE;
+
/* Headers */
soup_message_headers_foreach (entry->headers,
- (SoupMessageHeadersForeachFunc)copy_headers,
+ (SoupMessageHeadersForeachFunc)update_headers,
msg->response_headers);
/* Add 'Age' header with the current age */
@@ -843,7 +883,7 @@ soup_cache_new (const char *cache_dir)
*
* Returns: whether or not the @cache has a valid response for @msg
**/
-gboolean
+SoupCacheResponse
soup_cache_has_response (SoupCache *cache, SoupMessage *msg)
{
char *key;
@@ -861,10 +901,10 @@ soup_cache_has_response (SoupCache *cache, SoupMessage *msg)
* match
*/
if (!entry)
- return FALSE;
+ return SOUP_CACHE_RESPONSE_STALE;
- if (entry->dirty)
- return FALSE;
+ if (entry->dirty || entry->being_validated)
+ return SOUP_CACHE_RESPONSE_STALE;
/* 2. The request method associated with the stored response
* allows it to be used for the presented request
@@ -876,7 +916,7 @@ soup_cache_has_response (SoupCache *cache, SoupMessage *msg)
* probably).
*/
if (msg->method != SOUP_METHOD_GET)
- return FALSE;
+ return SOUP_CACHE_RESPONSE_STALE;
/* 3. Selecting request-headers nominated by the stored
* response (if any) match those presented.
@@ -897,7 +937,7 @@ soup_cache_has_response (SoupCache *cache, SoupMessage *msg)
if (g_hash_table_lookup_extended (hash, "no-store", NULL, NULL)) {
g_hash_table_destroy (hash);
- return FALSE;
+ return SOUP_CACHE_RESPONSE_STALE;
}
if (g_hash_table_lookup_extended (hash, "no-cache", NULL, NULL)) {
@@ -926,35 +966,35 @@ soup_cache_has_response (SoupCache *cache, SoupMessage *msg)
if (max_age != -1) {
guint current_age = soup_cache_entry_get_current_age (entry);
- /* If we are over max-age and max-stale is not set, do
- not use the value from the cache */
+ /* If we are over max-age and max-stale is not
+ set, do not use the value from the cache
+ without validation */
if (max_age <= current_age && max_stale == -1)
- return FALSE;
+ return SOUP_CACHE_RESPONSE_NEEDS_VALIDATION;
}
}
/* 5. The stored response is either: fresh, allowed to be
* served stale or succesfully validated
*/
- if (entry->must_revalidate) {
- return FALSE;
- }
+ if (entry->must_revalidate)
+ return SOUP_CACHE_RESPONSE_NEEDS_VALIDATION;
if (!soup_cache_entry_is_fresh_enough (entry, min_fresh)) {
/* Not fresh, can it be served stale? */
if (max_stale != -1) {
/* G_MAXINT32 means we accept any staleness */
if (max_stale == G_MAXINT32)
- return TRUE;
+ return SOUP_CACHE_RESPONSE_FRESH;
if ((soup_cache_entry_get_current_age (entry) - entry->freshness_lifetime) <= max_stale)
- return TRUE;
+ return SOUP_CACHE_RESPONSE_FRESH;
}
- return FALSE;
+ return SOUP_CACHE_RESPONSE_NEEDS_VALIDATION;
}
- return TRUE;
+ return SOUP_CACHE_RESPONSE_FRESH;
}
/**
@@ -1028,3 +1068,46 @@ soup_cache_clear (SoupCache *cache)
g_hash_table_foreach (hash, (GHFunc)remove_cache_item, cache);
}
+
+SoupMessage*
+soup_cache_generate_conditional_request (SoupCache *cache, SoupMessage *original)
+{
+ SoupMessage *msg;
+ SoupURI *uri;
+ SoupCacheEntry *entry;
+ char *key;
+ const char *value;
+
+ g_return_val_if_fail (SOUP_IS_CACHE (cache), NULL);
+ g_return_val_if_fail (SOUP_IS_MESSAGE (original), NULL);
+
+ /* First copy the data we need from the original message */
+ uri = soup_message_get_uri (original);
+ msg = soup_message_new_from_uri (original->method, uri);
+
+ soup_message_headers_foreach (original->request_headers,
+ (SoupMessageHeadersForeachFunc)copy_headers,
+ msg->request_headers);
+
+ /* Now add the validator entries in the header from the cached
+ data */
+ key = soup_message_get_cache_key (original);
+ entry = soup_cache_lookup_uri (cache, key);
+ g_free (key);
+
+ g_return_val_if_fail (entry, NULL);
+
+ entry->being_validated = TRUE;
+
+ value = soup_message_headers_get (entry->headers, "Last-Modified");
+ if (value)
+ soup_message_headers_append (msg->request_headers,
+ "Last-Modified",
+ value);
+ value = soup_message_headers_get (entry->headers, "ETag");
+ if (value)
+ soup_message_headers_append (msg->request_headers,
+ "ETag",
+ value);
+ return msg;
+}