soup-session: move cache handling here
authorDan Winship <danw@gnome.org>
Wed, 26 Dec 2012 19:50:11 +0000 (14:50 -0500)
committerDan Winship <danw@gnome.org>
Wed, 26 Dec 2012 20:21:27 +0000 (15:21 -0500)
Move the SoupCache special handling from SoupRequestHTTP to
SoupSession (as another step towards making SoupCache not such a
magical special case hack that only works in certain situations).

libsoup/soup-message-queue.h
libsoup/soup-request-http.c
libsoup/soup-session.c

index dd61924..490f9a2 100644 (file)
@@ -22,6 +22,7 @@ typedef enum {
        SOUP_MESSAGE_TUNNELED,
        SOUP_MESSAGE_READY,
        SOUP_MESSAGE_RUNNING,
+       SOUP_MESSAGE_CACHED,
        SOUP_MESSAGE_RESTARTING,
        SOUP_MESSAGE_FINISHING,
        SOUP_MESSAGE_FINISHED
index fe1a974..15e087a 100644 (file)
@@ -29,7 +29,6 @@
 
 #include "soup-request-http.h"
 #include "soup.h"
-#include "soup-cache-private.h"
 #include "soup-message-private.h"
 #include "soup-session-private.h"
 
@@ -256,20 +255,6 @@ soup_request_http_send (SoupRequest          *request,
 }
 
 
-typedef struct {
-       SoupMessage *original;
-       GInputStream *stream;
-} SendAsyncData;
-
-static void
-free_send_async_data (SendAsyncData *sadata)
-{
-       g_clear_object (&sadata->stream);
-       g_clear_object (&sadata->original);
-
-       g_slice_free (SendAsyncData, sadata);
-}
-
 static void
 http_input_stream_ready_cb (GObject *source, GAsyncResult *result, gpointer user_data)
 {
@@ -286,59 +271,6 @@ http_input_stream_ready_cb (GObject *source, GAsyncResult *result, gpointer user
 }
 
 static void
-conditional_get_ready_cb (SoupSession *session, SoupMessage *msg, gpointer user_data)
-{
-       GTask *task = user_data;
-       SoupRequestHTTP *http = g_task_get_source_object (task);
-       SendAsyncData *sadata = g_task_get_task_data (task);
-       GInputStream *stream;
-
-       if (msg->status_code == SOUP_STATUS_NOT_MODIFIED) {
-               SoupCache *cache = (SoupCache *)soup_session_get_feature (session, SOUP_TYPE_CACHE);
-
-               stream = soup_cache_send_response (cache, sadata->original);
-               if (stream) {
-                       soup_message_got_headers (sadata->original);
-                       soup_message_finished (sadata->original);
-
-                       http->priv->content_type = g_strdup (soup_message_headers_get_content_type (msg->response_headers, NULL));
-
-                       g_task_return_pointer (task, stream, g_object_unref);
-                       g_object_unref (task);
-                       return;
-               }
-       }
-
-       /* The resource was modified or the server returned a 200
-        * OK. Either way we reload it. This is far from optimal as
-        * we're donwloading the resource twice, but we will change it
-        * once the cache is integrated in the streams stack.
-        */
-       soup_session_send_request_async (session, sadata->original,
-                                        g_task_get_cancellable (task),
-                                        http_input_stream_ready_cb, task);
-}
-
-static gboolean
-idle_return_from_cache_cb (gpointer data)
-{
-       GTask *task = data;
-       SoupRequestHTTP *http = g_task_get_source_object (task);
-       SendAsyncData *sadata = g_task_get_task_data (task);
-
-       /* Issue signals  */
-       soup_message_got_headers (http->priv->msg);
-       soup_message_finished (http->priv->msg);
-
-       http->priv->content_type = g_strdup (soup_message_headers_get_content_type (http->priv->msg->response_headers, NULL));
-
-       g_task_return_pointer (task, g_object_ref (sadata->stream), g_object_unref);
-       g_object_unref (task);
-
-       return FALSE;
-}
-
-static void
 soup_request_http_send_async (SoupRequest          *request,
                              GCancellable         *cancellable,
                              GAsyncReadyCallback   callback,
@@ -347,55 +279,12 @@ soup_request_http_send_async (SoupRequest          *request,
        SoupRequestHTTP *http = SOUP_REQUEST_HTTP (request);
        SoupSession *session = soup_request_get_session (request);
        GTask *task;
-       SendAsyncData *sadata;
-       GInputStream *stream;
-       SoupCache *cache;
 
        g_return_if_fail (!SOUP_IS_SESSION_SYNC (session));
 
        http->priv->sent = TRUE;
 
        task = g_task_new (request, cancellable, callback, user_data);
-       sadata = g_slice_new0 (SendAsyncData);
-       g_task_set_task_data (task, sadata, (GDestroyNotify)free_send_async_data);
-
-       cache = (SoupCache *)soup_session_get_feature (session, SOUP_TYPE_CACHE);
-
-       if (cache) {
-               SoupCacheResponse response;
-
-               response = soup_cache_has_response (cache, http->priv->msg);
-               if (response == SOUP_CACHE_RESPONSE_FRESH) {
-                       stream = soup_cache_send_response (cache, http->priv->msg);
-
-                       /* Cached resource file could have been deleted outside */
-                       if (stream) {
-                               /* Do return the stream asynchronously as in
-                                * the other cases. It's not enough to let
-                                * GTask do the asynchrony for us, because
-                                * the signals must be also emitted
-                                * asynchronously
-                                */
-                               sadata->stream = stream;
-                               soup_add_completion (soup_session_get_async_context (session),
-                                                    idle_return_from_cache_cb, task);
-                               return;
-                       }
-               } else if (response == SOUP_CACHE_RESPONSE_NEEDS_VALIDATION) {
-                       SoupMessage *conditional_msg;
-
-                       conditional_msg = soup_cache_generate_conditional_request (cache, http->priv->msg);
-
-                       if (conditional_msg) {
-                               sadata->original = g_object_ref (http->priv->msg);
-                               soup_session_queue_message (session, conditional_msg,
-                                                           conditional_get_ready_cb,
-                                                           task);
-                               return;
-                       }
-               }
-       }
-
        soup_session_send_request_async (session, http->priv->msg, cancellable,
                                         http_input_stream_ready_cb, task);
 }
index f44bf8e..cfcadb2 100644 (file)
@@ -14,6 +14,7 @@
 #include "soup-session.h"
 #include "soup.h"
 #include "soup-auth-manager-ntlm.h"
+#include "soup-cache-private.h"
 #include "soup-connection.h"
 #include "soup-marshal.h"
 #include "soup-message-private.h"
@@ -1780,6 +1781,10 @@ soup_session_process_queue_item (SoupSession          *session,
                        item->state = SOUP_MESSAGE_FINISHING;
                        break;
 
+               case SOUP_MESSAGE_CACHED:
+                       /* Will be handled elsewhere */
+                       return;
+
                case SOUP_MESSAGE_RESTARTING:
                        item->state = SOUP_MESSAGE_STARTING;
                        soup_message_restarted (item->msg);
@@ -3551,6 +3556,121 @@ async_send_request_running (SoupSession *session, SoupMessageQueueItem *item)
        try_run_until_read (item);
 }
 
+static void
+async_return_from_cache (SoupMessageQueueItem *item,
+                        GInputStream         *stream)
+{
+       const char *content_type;
+       GHashTable *params;
+
+       soup_message_got_headers (item->msg);
+
+       content_type = soup_message_headers_get_content_type (item->msg->response_headers, &params);
+       soup_message_content_sniffed (item->msg, content_type, params);
+       g_hash_table_unref (params);
+
+       item->state = SOUP_MESSAGE_FINISHING;
+       async_send_request_return_result (item, g_object_ref (stream), NULL);
+}
+
+static void
+conditional_get_ready_cb (SoupSession *session, SoupMessage *msg, gpointer user_data)
+{
+       SoupMessageQueueItem *item = user_data;
+       GInputStream *stream;
+
+       if (msg->status_code == SOUP_STATUS_NOT_MODIFIED) {
+               SoupCache *cache = (SoupCache *)soup_session_get_feature (session, SOUP_TYPE_CACHE);
+
+               stream = soup_cache_send_response (cache, item->msg);
+               if (stream) {
+                       async_return_from_cache (item, stream);
+                       g_object_unref (stream);
+                       return;
+               }
+       }
+
+       /* The resource was modified or the server returned a 200
+        * OK. Either way we reload it. FIXME.
+        */
+       item->state = SOUP_MESSAGE_STARTING;
+       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);
+
+       async_return_from_cache (sacd->item, sacd->stream);
+       return FALSE;
+}
+
+static gboolean
+async_respond_from_cache (SoupSession          *session,
+                         SoupMessageQueueItem *item)
+{
+       SoupCache *cache;
+       SoupCacheResponse response;
+
+       cache = (SoupCache *)soup_session_get_feature (session, SOUP_TYPE_CACHE);
+       if (!cache)
+               return FALSE;
+
+       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);
+               if (!stream) {
+                       /* 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);
+
+               source = g_timeout_source_new (0);
+               g_task_attach_source (item->task, source,
+                                     (GSourceFunc) idle_return_from_cache_cb);
+               g_source_unref (source);
+               return TRUE;
+       } else if (response == SOUP_CACHE_RESPONSE_NEEDS_VALIDATION) {
+               SoupMessage *conditional_msg;
+
+               conditional_msg = soup_cache_generate_conditional_request (cache, item->msg);
+               if (!conditional_msg)
+                       return FALSE;
+
+               soup_session_queue_message (session, conditional_msg,
+                                           conditional_get_ready_cb,
+                                           item);
+               return TRUE;
+       } else
+               return FALSE;
+}
+
 void
 soup_session_send_request_async (SoupSession         *session,
                                 SoupMessage         *msg,
@@ -3585,7 +3705,10 @@ soup_session_send_request_async (SoupSession         *session,
                item->cancellable = g_object_ref (cancellable);
        }
 
-       soup_session_kick_queue (session);
+       if (async_respond_from_cache (session, item))
+               item->state = SOUP_MESSAGE_CACHED;
+       else
+               soup_session_kick_queue (session);
 }
 
 GInputStream *