From 11bdb5d83a6e1174310ad4cabd7b6db5ef54263d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Vr=C3=A1til?= Date: Fri, 14 Sep 2012 17:49:56 +0200 Subject: Bug #682295 - Fix deadlock in EHttpRequest Fix deadlock in EHttpRequest by running each libsoup operation in it's own GMainContext. The patch also replaces use of features from libsoup>=2.39, because we can't change dependency. The code is based on code from CalDAV, which uses synchronous libsoup API as well and works with libsoup<2.39 --- mail/e-http-request.c | 166 +++++++++++++++++++++++++++----------------------- 1 file changed, 89 insertions(+), 77 deletions(-) (limited to 'mail') diff --git a/mail/e-http-request.c b/mail/e-http-request.c index ffefdaefd1..eef570ac3f 100644 --- a/mail/e-http-request.c +++ b/mail/e-http-request.c @@ -16,6 +16,10 @@ * */ +#ifdef HAVE_CONFIG_H +#include +#endif + #include "e-http-request.h" #define LIBSOUP_USE_UNSTABLE_REQUEST_API @@ -75,12 +79,65 @@ copy_stream_to_stream (CamelStream *input, return total_len; } -static gboolean -unref_soup_session (SoupSession *session) +static void +redirect_handler (SoupMessage *msg, + gpointer user_data) +{ + if (SOUP_STATUS_IS_REDIRECTION (msg->status_code)) { + SoupSession *soup_session = user_data; + SoupURI *new_uri; + const gchar *new_loc; + + new_loc = soup_message_headers_get (msg->response_headers, "Location"); + if (!new_loc) + return; + + new_uri = soup_uri_new_with_base (soup_message_get_uri (msg), new_loc); + if (!new_uri) { + soup_message_set_status_full (msg, + SOUP_STATUS_MALFORMED, + "Invalid Redirect URL"); + return; + } + + soup_message_set_uri (msg, new_uri); + soup_session_requeue_message (soup_session, msg); + + soup_uri_free (new_uri); + } +} + +static void +send_and_handle_redirection (SoupSession *session, + SoupMessage *message, + gchar **new_location) { - g_object_unref (session); + gchar *old_uri = NULL; - return FALSE; + g_return_if_fail (message != NULL); + + if (new_location) { + old_uri = soup_uri_to_string (soup_message_get_uri (message), FALSE); + } + + soup_message_set_flags (message, SOUP_MESSAGE_NO_REDIRECT); + soup_message_add_header_handler ( + message, "got_body", "Location", + G_CALLBACK (redirect_handler), session); + soup_message_headers_append (message->request_headers, "Connection", "close"); + soup_session_send_message (session, message); + + if (new_location) { + gchar *new_loc = soup_uri_to_string (soup_message_get_uri (message), FALSE); + + if (new_loc && old_uri && !g_str_equal (new_loc, old_uri)) { + *new_location = new_loc; + } else { + g_free (new_loc); + } + } + + g_free (old_uri); } static void @@ -247,47 +304,31 @@ handle_http_request (GSimpleAsyncResult *res, if ((image_policy == E_MAIL_IMAGE_LOADING_POLICY_ALWAYS) || force_load_images) { - SoupRequester *requester; - SoupRequest *http_request; SoupSession *session; - SoupMessage *msg; + SoupMessage *message; CamelStream *cache_stream; GError *error; - gssize nread; - SoupURI *soup_uri; - gsize real_content_length; - GInputStream *http_stream; + GMainContext *context; + + context = g_main_context_new (); + g_main_context_push_thread_default (context); session = soup_session_sync_new_with_options ( SOUP_SESSION_TIMEOUT, 90, NULL); - requester = soup_requester_new (); - soup_session_add_feature (session, SOUP_SESSION_FEATURE (requester)); - g_object_unref (requester); + message = soup_message_new (SOUP_METHOD_GET, uri); + soup_message_headers_append ( + message->request_headers, "User-Agent", "Evolution/" VERSION); - error = NULL; - soup_uri = soup_uri_new (uri); - http_request = soup_requester_request_uri (requester, soup_uri, &error); - soup_uri_free (soup_uri); - if (error) { - g_warning ("Failed to request %s: %s", uri, error->message); - g_clear_error (&error); - goto cleanup; - } + send_and_handle_redirection (session, message, NULL); - error = NULL; - http_stream = soup_request_send (http_request, cancellable, &error); - msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (http_request)); - if (!msg || msg->status_code != SOUP_STATUS_OK) { - g_warning ( - "Failed to retrieve data from %s: %s (code %d)", - uri, error ? error->message : "Unknown error", - msg->status_code); - g_clear_error (&error); + if (!SOUP_STATUS_IS_SUCCESSFUL (message->status_code)) { + g_warning ("Failed to request %s (code %d)", uri, message->status_code); goto cleanup; } + /* Write the response body to cache */ error = NULL; cache_stream = camel_data_cache_add (cache, "http", uri_md5, &error); if (!cache_stream) { @@ -295,31 +336,11 @@ handle_http_request (GSimpleAsyncResult *res, "Failed to create cache file for '%s': %s", uri, error ? error->message : "Unknown error"); g_clear_error (&error); - - /* We rely on existence of the stream. If CamelDataCache - * failed to create a cache file, then store it at least - * temporarily. */ - cache_stream = camel_stream_mem_new (); - } - - real_content_length = 0; - do { - gchar buf[512]; - - error = NULL; - nread = g_input_stream_read ( - http_stream, buf, 512, cancellable, &error); - if (nread == -1) { - g_warning ( - "Failed to read data from input stream: %s", - error ? error->message : "Unknown error"); - g_clear_error (&error); - goto cleanup; - } - + } else { error = NULL; camel_stream_write ( - cache_stream, buf, nread, cancellable, &error); + cache_stream, message->response_body->data, + message->response_body->length, cancellable, &error); if (error) { g_warning ( "Failed to write data to cache stream: %s", @@ -328,34 +349,25 @@ handle_http_request (GSimpleAsyncResult *res, goto cleanup; } - real_content_length += nread; - - } while (nread > 0); - - /* XXX For some reason, WebKit refuses to display content of - * GInputStream we get from soup_request_send(), so create a - * new input stream here and fill it with what's in the cache - * stream. */ - stream = g_memory_input_stream_new (); - copy_stream_to_stream ( - cache_stream, G_MEMORY_INPUT_STREAM (stream), cancellable); + camel_stream_close (cache_stream, cancellable, NULL); + g_object_unref (cache_stream); + } - g_input_stream_close (http_stream, cancellable, NULL); - g_object_unref (http_stream); + /* Send the response body to WebKit */ + stream = g_memory_input_stream_new_from_data ( + g_memdup (message->response_body->data, message->response_body->length), + message->response_body->length, + (GDestroyNotify) g_free); - /* Don't use the content-length header as it is sometimes missing - * or invalid */ - request->priv->content_length = real_content_length; + request->priv->content_length = message->response_body->length; request->priv->content_type = g_strdup ( soup_message_headers_get_content_type ( - msg->response_headers, NULL)); + message->response_headers, NULL)); - camel_stream_close (cache_stream, cancellable, NULL); - g_object_unref (cache_stream); - g_object_unref (http_request); - g_object_unref (msg); - g_idle_add ((GSourceFunc) unref_soup_session, session); + g_object_unref (message); + g_object_unref (session); + g_main_context_unref (context); d (printf ("Received image from %s\n" "Content-Type: %s\n" -- cgit v1.2.3