From 52b4df4b2b6d2c70fa333ce712041fd85a65d7c9 Mon Sep 17 00:00:00 2001 From: Sergio Villar Senin Date: Wed, 30 Jan 2013 20:05:29 +0100 Subject: [PATCH] soup-session: handle cancellation of SoupCache resources SoupSession will now properly handle cancellations (either with soup_session_cancel_message or g_cancellable_cancel) of resources returned by the cache whether they're fresh or need a revalidation. http://bugzilla.gnome.org/show_bug.cgi?id=692310 --- libsoup/soup-cache-private.h | 2 + libsoup/soup-cache.c | 13 ++++++ libsoup/soup-session.c | 94 +++++++++++++++++++++++++++++++++----------- 3 files changed, 85 insertions(+), 24 deletions(-) diff --git a/libsoup/soup-cache-private.h b/libsoup/soup-cache-private.h index 3843e8e..65ad868 100644 --- a/libsoup/soup-cache-private.h +++ b/libsoup/soup-cache-private.h @@ -36,6 +36,8 @@ SoupCacheability soup_cache_get_cacheability (SoupCache *cache, SoupMessage *msg); SoupMessage *soup_cache_generate_conditional_request (SoupCache *cache, SoupMessage *original); +void soup_cache_cancel_conditional_request (SoupCache *cache, + SoupMessage *msg); G_END_DECLS diff --git a/libsoup/soup-cache.c b/libsoup/soup-cache.c index 5569d6b..839cdc1 100644 --- a/libsoup/soup-cache.c +++ b/libsoup/soup-cache.c @@ -1377,6 +1377,19 @@ soup_cache_generate_conditional_request (SoupCache *cache, SoupMessage *original return msg; } +void +soup_cache_cancel_conditional_request (SoupCache *cache, + SoupMessage *msg) +{ + SoupCacheEntry *entry; + + entry = soup_cache_entry_lookup (cache, msg); + if (entry) + entry->being_validated = FALSE; + + soup_session_cancel_message (cache->priv->session, msg, SOUP_STATUS_CANCELLED); +} + static void pack_entry (gpointer data, gpointer user_data) diff --git a/libsoup/soup-session.c b/libsoup/soup-session.c index c50faa2..ea0a16b 100644 --- a/libsoup/soup-session.c +++ b/libsoup/soup-session.c @@ -3781,12 +3781,49 @@ async_return_from_cache (SoupMessageQueueItem *item, async_send_request_return_result (item, g_object_ref (stream), NULL); } +typedef struct { + SoupCache *cache; + SoupMessage *conditional_msg; +} AsyncCacheCancelData; + + +static void +free_async_cache_cancel_data (AsyncCacheCancelData *data) +{ + g_object_unref (data->conditional_msg); + g_object_unref (data->cache); + g_slice_free (AsyncCacheCancelData, data); +} + +static void +cancel_cache_response (SoupMessageQueueItem *item) +{ + item->paused = FALSE; + item->state = SOUP_MESSAGE_FINISHING; + soup_message_set_status (item->msg, SOUP_STATUS_CANCELLED); + soup_session_kick_queue (item->session); +} + +static void +conditional_request_cancelled_cb (GCancellable *cancellable, AsyncCacheCancelData *data) +{ + soup_cache_cancel_conditional_request (data->cache, data->conditional_msg); +} + static void conditional_get_ready_cb (SoupSession *session, SoupMessage *msg, gpointer user_data) { SoupMessageQueueItem *item = user_data; GInputStream *stream; + if (g_cancellable_is_cancelled (item->cancellable)) { + cancel_cache_response (item); + return; + } else { + gulong handler_id = GPOINTER_TO_SIZE (g_object_get_data (G_OBJECT (msg), "SoupSession:handler-id")); + g_cancellable_disconnect (item->cancellable, handler_id); + } + if (msg->status_code == SOUP_STATUS_NOT_MODIFIED) { SoupCache *cache = (SoupCache *)soup_session_get_feature (session, SOUP_TYPE_CACHE); @@ -3805,29 +3842,32 @@ conditional_get_ready_cb (SoupSession *session, SoupMessage *msg, gpointer user_ soup_session_kick_queue (session); } -typedef struct { - SoupMessageQueueItem *item; - GInputStream *stream; -} SendAsyncCacheData; - -static void -free_send_async_cache_data (SendAsyncCacheData *sacd) -{ - soup_message_queue_item_unref (sacd->item); - g_object_unref (sacd->stream); - g_slice_free (SendAsyncCacheData, sacd); -} - static gboolean idle_return_from_cache_cb (gpointer data) { GTask *task = data; - SendAsyncCacheData *sacd = g_task_get_task_data (task); + SoupMessageQueueItem *item = g_task_get_task_data (task); + GInputStream *istream; + + if (item->state == SOUP_MESSAGE_FINISHED) { + /* The original request was cancelled using + * soup_session_cancel_message () so it has been + * already handled by the cancellation code path. + */ + return FALSE; + } else if (g_cancellable_is_cancelled (item->cancellable)) { + /* Cancel original msg after g_cancellable_cancel(). */ + cancel_cache_response (item); + return FALSE; + } + + istream = g_object_steal_data (G_OBJECT (task), "SoupSession:istream"); + async_return_from_cache (item, istream); - async_return_from_cache (sacd->item, sacd->stream); return FALSE; } + static gboolean async_respond_from_cache (SoupSession *session, SoupMessageQueueItem *item) @@ -3842,7 +3882,6 @@ async_respond_from_cache (SoupSession *session, response = soup_cache_has_response (cache, item->msg); if (response == SOUP_CACHE_RESPONSE_FRESH) { GInputStream *stream; - SendAsyncCacheData *sacd; GSource *source; stream = soup_cache_send_response (cache, item->msg); @@ -3850,14 +3889,8 @@ async_respond_from_cache (SoupSession *session, /* Cached file was deleted? */ return FALSE; } - - sacd = g_slice_new (SendAsyncCacheData); - sacd->item = item; - soup_message_queue_item_ref (item); - sacd->stream = stream; - - g_task_set_task_data (item->task, sacd, - (GDestroyNotify) free_send_async_cache_data); + g_object_set_data_full (G_OBJECT (item->task), "SoupSession:istream", + stream, g_object_unref); source = g_timeout_source_new (0); g_task_attach_source (item->task, source, @@ -3866,14 +3899,27 @@ async_respond_from_cache (SoupSession *session, return TRUE; } else if (response == SOUP_CACHE_RESPONSE_NEEDS_VALIDATION) { SoupMessage *conditional_msg; + AsyncCacheCancelData *data; + gulong handler_id; conditional_msg = soup_cache_generate_conditional_request (cache, item->msg); if (!conditional_msg) return FALSE; + /* Detect any quick cancellation before the cache is able to return data. */ + data = g_slice_new0 (AsyncCacheCancelData); + data->cache = g_object_ref (cache); + data->conditional_msg = g_object_ref (conditional_msg); + handler_id = g_cancellable_connect (item->cancellable, G_CALLBACK (conditional_request_cancelled_cb), + data, (GDestroyNotify) free_async_cache_cancel_data); + + g_object_set_data (G_OBJECT (conditional_msg), "SoupSession:handler-id", + GSIZE_TO_POINTER (handler_id)); soup_session_queue_message (session, conditional_msg, conditional_get_ready_cb, item); + + return TRUE; } else return FALSE; -- 2.7.4