summaryrefslogtreecommitdiff
path: root/libsoup/soup-session-async.c
diff options
context:
space:
mode:
Diffstat (limited to 'libsoup/soup-session-async.c')
-rw-r--r--libsoup/soup-session-async.c658
1 files changed, 23 insertions, 635 deletions
diff --git a/libsoup/soup-session-async.c b/libsoup/soup-session-async.c
index 46900552..b9353480 100644
--- a/libsoup/soup-session-async.c
+++ b/libsoup/soup-session-async.c
@@ -9,64 +9,44 @@
#include <config.h>
#endif
-#define LIBSOUP_I_HAVE_READ_BUG_594377_AND_KNOW_SOUP_PASSWORD_MANAGER_MIGHT_GO_AWAY
-
#include "soup-session-async.h"
#include "soup.h"
#include "soup-session-private.h"
#include "soup-message-private.h"
#include "soup-message-queue.h"
+#include "soup-misc-private.h"
/**
* SECTION:soup-session-async
- * @short_description: Soup session for asynchronous (main-loop-based) I/O.
+ * @short_description: (Deprecated) SoupSession for asynchronous
+ * (main-loop-based) I/O.
*
* #SoupSessionAsync is an implementation of #SoupSession that uses
- * non-blocking I/O via the glib main loop. It is intended for use in
- * single-threaded programs.
+ * non-blocking I/O via the glib main loop for all I/O.
+ *
+ * As of libsoup 2.42, this is deprecated in favor of the plain
+ * #SoupSession class (which uses both asynchronous and synchronous
+ * I/O, depending on the API used). See the <link
+ * linkend="libsoup-session-porting">porting guide</link>.
**/
-static void run_queue (SoupSessionAsync *sa);
-static void do_idle_run_queue (SoupSession *session);
-
-static void send_request_running (SoupSession *session, SoupMessageQueueItem *item);
-static void send_request_restarted (SoupSession *session, SoupMessageQueueItem *item);
-static void send_request_finished (SoupSession *session, SoupMessageQueueItem *item);
-
G_DEFINE_TYPE (SoupSessionAsync, soup_session_async, SOUP_TYPE_SESSION)
-typedef struct {
- SoupSessionAsync *sa;
- gboolean disposed;
-
-} SoupSessionAsyncPrivate;
-#define SOUP_SESSION_ASYNC_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_SESSION_ASYNC, SoupSessionAsyncPrivate))
-
static void
soup_session_async_init (SoupSessionAsync *sa)
{
- SoupSessionAsyncPrivate *priv = SOUP_SESSION_ASYNC_GET_PRIVATE (sa);
-
- priv->sa = sa;
-}
-
-static void
-soup_session_async_dispose (GObject *object)
-{
- SoupSessionAsyncPrivate *priv = SOUP_SESSION_ASYNC_GET_PRIVATE (object);
-
- priv->disposed = TRUE;
-
- G_OBJECT_CLASS (soup_session_async_parent_class)->dispose (object);
}
-
/**
* soup_session_async_new:
*
* Creates an asynchronous #SoupSession with the default options.
*
* Return value: the new session.
+ *
+ * Deprecated: #SoupSessionAsync is deprecated; use a plain
+ * #SoupSession, created with soup_session_new(). See the <link
+ * linkend="libsoup-session-porting">porting guide</link>.
**/
SoupSession *
soup_session_async_new (void)
@@ -82,6 +62,10 @@ soup_session_async_new (void)
* Creates an asynchronous #SoupSession with the specified options.
*
* Return value: the new session.
+ *
+ * Deprecated: #SoupSessionAsync is deprecated; use a plain
+ * #SoupSession, created with soup_session_new_with_options(). See the
+ * <link linkend="libsoup-session-porting">porting guide</link>.
**/
SoupSession *
soup_session_async_new_with_options (const char *optname1, ...)
@@ -97,331 +81,23 @@ soup_session_async_new_with_options (const char *optname1, ...)
return session;
}
-static void
-connection_closed (SoupConnection *conn, gpointer session)
-{
- /* Run the queue in case anyone was waiting for a connection
- * to be closed.
- */
- do_idle_run_queue (session);
-}
-
-static void
-message_completed (SoupMessage *msg, gpointer user_data)
-{
- SoupMessageQueueItem *item = user_data;
-
- do_idle_run_queue (item->session);
-
- if (item->state != SOUP_MESSAGE_RESTARTING)
- item->state = SOUP_MESSAGE_FINISHING;
-}
-
-static void
-tunnel_complete (SoupMessageQueueItem *item)
-{
- SoupSession *session = item->session;
-
- soup_message_finished (item->msg);
- if (item->related->msg->status_code)
- item->related->state = SOUP_MESSAGE_FINISHING;
- else
- soup_message_set_https_status (item->related->msg, item->conn);
-
- do_idle_run_queue (session);
- soup_message_queue_item_unref (item->related);
- soup_session_unqueue_item (session, item);
- soup_message_queue_item_unref (item);
- g_object_unref (session);
-}
-
-static void
-ssl_tunnel_completed (SoupConnection *conn, guint status, gpointer user_data)
-{
- SoupMessageQueueItem *item = user_data;
-
- if (SOUP_STATUS_IS_SUCCESSFUL (status)) {
- g_signal_connect (item->conn, "disconnected",
- G_CALLBACK (connection_closed), item->session);
- soup_connection_set_state (item->conn, SOUP_CONNECTION_IDLE);
- soup_connection_set_state (item->conn, SOUP_CONNECTION_IN_USE);
-
- item->related->state = SOUP_MESSAGE_READY;
- } else {
- if (item->conn)
- soup_connection_disconnect (item->conn);
- soup_message_set_status (item->related->msg, SOUP_STATUS_SSL_FAILED);
- }
-
- tunnel_complete (item);
-}
-
-static void
-tunnel_message_completed (SoupMessage *msg, gpointer user_data)
-{
- SoupMessageQueueItem *item = user_data;
- SoupSession *session = item->session;
-
- if (item->state == SOUP_MESSAGE_RESTARTING) {
- soup_message_restarted (msg);
- if (item->conn) {
- item->state = SOUP_MESSAGE_RUNNING;
- soup_session_send_queue_item (session, item, tunnel_message_completed);
- return;
- }
-
- soup_message_set_status (msg, SOUP_STATUS_TRY_AGAIN);
- }
-
- item->state = SOUP_MESSAGE_FINISHED;
-
- if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
- if (item->conn)
- soup_connection_disconnect (item->conn);
- if (msg->status_code == SOUP_STATUS_TRY_AGAIN) {
- item->related->state = SOUP_MESSAGE_AWAITING_CONNECTION;
- soup_message_queue_item_set_connection (item->related, NULL);
- } else
- soup_message_set_status (item->related->msg, msg->status_code);
-
- tunnel_complete (item);
- return;
- }
-
- soup_connection_start_ssl_async (item->conn, item->cancellable,
- ssl_tunnel_completed, item);
-}
-
-static void
-got_connection (SoupConnection *conn, guint status, gpointer user_data)
-{
- SoupMessageQueueItem *item = user_data;
- SoupSession *session = item->session;
-
- if (item->state != SOUP_MESSAGE_CONNECTING) {
- soup_connection_disconnect (conn);
- do_idle_run_queue (session);
- soup_message_queue_item_unref (item);
- g_object_unref (session);
- return;
- }
-
- soup_message_set_https_status (item->msg, conn);
-
- if (status != SOUP_STATUS_OK) {
- soup_connection_disconnect (conn);
-
- if (status == SOUP_STATUS_TRY_AGAIN) {
- soup_message_queue_item_set_connection (item, NULL);
- item->state = SOUP_MESSAGE_AWAITING_CONNECTION;
- } else {
- soup_session_set_item_status (session, item, status);
- item->state = SOUP_MESSAGE_FINISHING;
- }
-
- do_idle_run_queue (session);
- soup_message_queue_item_unref (item);
- g_object_unref (session);
- return;
- }
-
- if (soup_connection_is_tunnelled (conn)) {
- SoupMessageQueueItem *tunnel_item;
-
- item->state = SOUP_MESSAGE_TUNNELING;
-
- tunnel_item = soup_session_make_connect_message (session, conn);
- tunnel_item->related = item;
- soup_session_send_queue_item (session, tunnel_item, tunnel_message_completed);
- return;
- }
-
- item->state = SOUP_MESSAGE_READY;
- g_signal_connect (conn, "disconnected",
- G_CALLBACK (connection_closed), session);
- run_queue ((SoupSessionAsync *)session);
- soup_message_queue_item_unref (item);
- g_object_unref (session);
-}
-
-static void
-process_queue_item (SoupMessageQueueItem *item,
- gboolean *should_prune,
- gboolean loop)
-{
- SoupSession *session = item->session;
-
- if (item->async_context != soup_session_get_async_context (session))
- return;
-
- do {
- if (item->paused)
- return;
-
- switch (item->state) {
- case SOUP_MESSAGE_STARTING:
- item->state = SOUP_MESSAGE_AWAITING_CONNECTION;
- break;
-
- case SOUP_MESSAGE_AWAITING_CONNECTION:
- if (!soup_session_get_connection (session, item, should_prune))
- return;
-
- if (soup_connection_get_state (item->conn) != SOUP_CONNECTION_NEW) {
- item->state = SOUP_MESSAGE_READY;
- break;
- }
-
- item->state = SOUP_MESSAGE_CONNECTING;
- soup_message_queue_item_ref (item);
- g_object_ref (session);
- soup_connection_connect_async (item->conn, item->cancellable,
- got_connection, item);
- return;
-
- case SOUP_MESSAGE_READY:
- item->state = SOUP_MESSAGE_RUNNING;
- soup_session_send_queue_item (session, item, message_completed);
- if (item->new_api)
- send_request_running (session, item);
- break;
-
- case SOUP_MESSAGE_RESTARTING:
- item->state = SOUP_MESSAGE_STARTING;
- soup_message_restarted (item->msg);
- if (item->new_api)
- send_request_restarted (session, item);
- break;
-
- case SOUP_MESSAGE_FINISHING:
- item->state = SOUP_MESSAGE_FINISHED;
- soup_message_finished (item->msg);
- if (item->state != SOUP_MESSAGE_FINISHED)
- break;
-
- g_object_ref (session);
- soup_session_unqueue_item (session, item);
- if (item->callback)
- item->callback (session, item->msg, item->callback_data);
- else if (item->new_api)
- send_request_finished (session, item);
- do_idle_run_queue (session);
- g_object_unref (session);
- return;
-
- default:
- /* Nothing to do with this message in any
- * other state.
- */
- return;
- }
- } while (loop && item->state != SOUP_MESSAGE_FINISHED);
-}
-
-static void
-run_queue (SoupSessionAsync *sa)
-{
- SoupSession *session = SOUP_SESSION (sa);
- SoupMessageQueue *queue = soup_session_get_queue (session);
- SoupMessageQueueItem *item;
- SoupMessage *msg;
- gboolean try_pruning = TRUE, should_prune = FALSE;
-
- g_object_ref (session);
- soup_session_cleanup_connections (session, FALSE);
-
- try_again:
- for (item = soup_message_queue_first (queue);
- item;
- item = soup_message_queue_next (queue, item)) {
- msg = item->msg;
-
- /* CONNECT messages are handled specially */
- if (msg->method != SOUP_METHOD_CONNECT)
- process_queue_item (item, &should_prune, TRUE);
- }
-
- if (try_pruning && should_prune) {
- /* There is at least one message in the queue that
- * could be sent if we pruned an idle connection from
- * some other server.
- */
- if (soup_session_cleanup_connections (session, TRUE)) {
- try_pruning = should_prune = FALSE;
- goto try_again;
- }
- }
-
- g_object_unref (session);
-}
-
-static gboolean
-idle_run_queue (gpointer user_data)
-{
- SoupSessionAsyncPrivate *priv = user_data;
-
- if (priv->disposed)
- return FALSE;
-
- /* Ensure that the source is destroyed before running the queue */
- g_source_destroy (g_main_current_source ());
-
- run_queue (priv->sa);
- return FALSE;
-}
-
-static void
-do_idle_run_queue (SoupSession *session)
-{
- SoupSessionAsyncPrivate *priv = SOUP_SESSION_ASYNC_GET_PRIVATE (session);
- GMainContext *async_context = soup_session_get_async_context (session);
- GSource *source;
-
- if (priv->disposed)
- return;
-
- /* We use priv rather than session as the source data, because
- * other parts of libsoup (or the calling app) may have sources
- * using the session as the source data.
- */
-
- source = g_main_context_find_source_by_user_data (async_context, priv);
- if (source)
- return;
-
- source = soup_add_completion (async_context, idle_run_queue, priv);
-}
-
-static void
-soup_session_async_queue_message (SoupSession *session, SoupMessage *req,
- SoupSessionCallback callback, gpointer user_data)
-{
- SoupMessageQueueItem *item;
-
- item = soup_session_append_queue_item (session, req, callback, user_data);
- soup_message_queue_item_unref (item);
-
- do_idle_run_queue (session);
-}
-
static guint
-soup_session_async_send_message (SoupSession *session, SoupMessage *req)
+soup_session_async_send_message (SoupSession *session, SoupMessage *msg)
{
SoupMessageQueueItem *item;
GMainContext *async_context =
soup_session_get_async_context (session);
- soup_session_async_queue_message (session, req, NULL, NULL);
-
- item = soup_message_queue_lookup (soup_session_get_queue (session), req);
- g_return_val_if_fail (item != NULL, SOUP_STATUS_MALFORMED);
+ item = soup_session_append_queue_item (session, msg, TRUE, FALSE,
+ NULL, NULL);
+ soup_session_kick_queue (session);
while (item->state != SOUP_MESSAGE_FINISHED)
g_main_context_iteration (async_context, TRUE);
soup_message_queue_item_unref (item);
- return req->status_code;
+ return msg->status_code;
}
static void
@@ -430,7 +106,6 @@ soup_session_async_cancel_message (SoupSession *session, SoupMessage *msg,
{
SoupMessageQueue *queue;
SoupMessageQueueItem *item;
- gboolean dummy;
SOUP_SESSION_CLASS (soup_session_async_parent_class)->
cancel_message (session, msg, status_code);
@@ -452,304 +127,17 @@ soup_session_async_cancel_message (SoupSession *session, SoupMessage *msg,
item->state = SOUP_MESSAGE_FINISHING;
if (item->state != SOUP_MESSAGE_FINISHED)
- process_queue_item (item, &dummy, FALSE);
-
- soup_message_queue_item_unref (item);
-}
-
-static void
-got_passwords (SoupPasswordManager *password_manager, SoupMessage *msg,
- SoupAuth *auth, gboolean retrying, gpointer session)
-{
- soup_session_unpause_message (session, msg);
- SOUP_SESSION_CLASS (soup_session_async_parent_class)->
- auth_required (session, msg, auth, retrying);
- g_object_unref (auth);
-}
-
-static void
-soup_session_async_auth_required (SoupSession *session, SoupMessage *msg,
- SoupAuth *auth, gboolean retrying)
-{
- SoupSessionFeature *password_manager;
-
- password_manager = soup_session_get_feature_for_message (
- session, SOUP_TYPE_PASSWORD_MANAGER, msg);
- if (password_manager) {
- soup_session_pause_message (session, msg);
- g_object_ref (auth);
- soup_password_manager_get_passwords_async (
- SOUP_PASSWORD_MANAGER (password_manager),
- msg, auth, retrying,
- soup_session_get_async_context (session),
- NULL, /* FIXME cancellable */
- got_passwords, session);
- } else {
- SOUP_SESSION_CLASS (soup_session_async_parent_class)->
- auth_required (session, msg, auth, retrying);
- }
-}
-
-static void
-soup_session_async_kick (SoupSession *session)
-{
- do_idle_run_queue (session);
-}
-
-
-static void
-send_request_return_result (SoupMessageQueueItem *item,
- gpointer stream, GError *error)
-{
- GSimpleAsyncResult *simple;
-
- simple = item->result;
- item->result = NULL;
-
- if (error)
- g_simple_async_result_take_error (simple, error);
- else if (SOUP_STATUS_IS_TRANSPORT_ERROR (item->msg->status_code)) {
- if (stream)
- g_object_unref (stream);
- g_simple_async_result_set_error (simple,
- SOUP_HTTP_ERROR,
- item->msg->status_code,
- "%s",
- item->msg->reason_phrase);
- } else
- g_simple_async_result_set_op_res_gpointer (simple, stream, g_object_unref);
-
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
-}
-
-static void
-send_request_restarted (SoupSession *session, SoupMessageQueueItem *item)
-{
- /* We won't be needing this, then. */
- g_object_set_data (G_OBJECT (item->msg), "SoupSessionAsync:ostream", NULL);
- item->io_started = FALSE;
-}
-
-static void
-send_request_finished (SoupSession *session, SoupMessageQueueItem *item)
-{
- GMemoryOutputStream *mostream;
- GInputStream *istream = NULL;
- GError *error = NULL;
-
- if (!item->result) {
- /* Something else already took care of it. */
- return;
- }
-
- mostream = g_object_get_data (G_OBJECT (item->msg), "SoupSessionAsync:ostream");
- if (mostream) {
- gpointer data;
- gssize size;
-
- /* We thought it would be requeued, but it wasn't, so
- * return the original body.
- */
- size = g_memory_output_stream_get_data_size (mostream);
- data = size ? g_memory_output_stream_steal_data (mostream) : g_strdup ("");
- istream = g_memory_input_stream_new_from_data (data, size, g_free);
- } else if (item->io_started) {
- /* The message finished before becoming readable. This
- * will happen, eg, if it's cancelled from got-headers.
- * Do nothing; the op will complete via read_ready_cb()
- * after we return;
- */
- return;
- } else {
- /* The message finished before even being started;
- * probably a tunnel connect failure.
- */
- istream = g_memory_input_stream_new ();
- }
-
- send_request_return_result (item, istream, error);
-}
-
-static void
-send_async_spliced (GObject *source, GAsyncResult *result, gpointer user_data)
-{
- SoupMessageQueueItem *item = user_data;
- GInputStream *istream = g_object_get_data (source, "istream");
-
- GError *error = NULL;
-
- /* If the message was cancelled, it will be completed via other means */
- if (g_cancellable_is_cancelled (item->cancellable) ||
- !item->result) {
- soup_message_queue_item_unref (item);
- return;
- }
-
- if (g_output_stream_splice_finish (G_OUTPUT_STREAM (source),
- result, &error) == -1) {
- send_request_return_result (item, NULL, error);
- return;
- }
+ soup_session_process_queue_item (session, item, NULL, FALSE);
- /* Otherwise either restarted or finished will eventually be called.
- * It should be safe to call the sync close() method here since
- * the message body has already been written.
- */
- g_input_stream_close (istream, NULL, NULL);
- do_idle_run_queue (item->session);
soup_message_queue_item_unref (item);
}
static void
-send_async_maybe_complete (SoupMessageQueueItem *item,
- GInputStream *stream)
-{
- if (item->msg->status_code == SOUP_STATUS_UNAUTHORIZED ||
- item->msg->status_code == SOUP_STATUS_PROXY_UNAUTHORIZED ||
- soup_session_would_redirect (item->session, item->msg)) {
- GOutputStream *ostream;
-
- /* Message may be requeued, so gather the current message body... */
- ostream = g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
- g_object_set_data_full (G_OBJECT (item->msg), "SoupSessionAsync:ostream",
- ostream, g_object_unref);
-
- g_object_set_data_full (G_OBJECT (ostream), "istream",
- stream, g_object_unref);
-
- /* Give the splice op its own ref on item */
- soup_message_queue_item_ref (item);
- g_output_stream_splice_async (ostream, stream,
- /* We can't use CLOSE_SOURCE because it
- * might get closed in the wrong thread.
- */
- G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
- G_PRIORITY_DEFAULT,
- item->cancellable,
- send_async_spliced, item);
- return;
- }
-
- send_request_return_result (item, stream, NULL);
-}
-
-static void try_run_until_read (SoupMessageQueueItem *item);
-
-static gboolean
-read_ready_cb (SoupMessage *msg, gpointer user_data)
-{
- SoupMessageQueueItem *item = user_data;
-
- try_run_until_read (item);
- return FALSE;
-}
-
-static void
-try_run_until_read (SoupMessageQueueItem *item)
-{
- GError *error = NULL;
- GInputStream *stream = NULL;
- GSource *source;
-
- if (soup_message_io_run_until_read (item->msg, item->cancellable, &error))
- stream = soup_message_io_get_response_istream (item->msg, &error);
- if (stream) {
- send_async_maybe_complete (item, stream);
- return;
- }
-
- if (g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_TRY_AGAIN)) {
- item->state = SOUP_MESSAGE_RESTARTING;
- soup_message_io_finished (item->msg);
- g_error_free (error);
- return;
- }
-
- if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
- send_request_return_result (item, NULL, error);
- return;
- }
-
- g_clear_error (&error);
- source = soup_message_io_get_source (item->msg, item->cancellable,
- read_ready_cb, item);
- g_source_attach (source, soup_session_get_async_context (item->session));
- g_source_unref (source);
-}
-
-static void
-send_request_running (SoupSession *session, SoupMessageQueueItem *item)
-{
- item->io_started = TRUE;
- try_run_until_read (item);
-}
-
-void
-soup_session_send_request_async (SoupSession *session,
- SoupMessage *msg,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- SoupMessageQueueItem *item;
- gboolean use_thread_context;
-
- g_return_if_fail (SOUP_IS_SESSION_ASYNC (session));
-
- g_object_get (G_OBJECT (session),
- SOUP_SESSION_USE_THREAD_CONTEXT, &use_thread_context,
- NULL);
- g_return_if_fail (use_thread_context);
-
- soup_session_async_queue_message (session, msg, NULL, NULL);
-
- item = soup_message_queue_lookup (soup_session_get_queue (session), msg);
- g_return_if_fail (item != NULL);
-
- item->new_api = TRUE;
- item->result = g_simple_async_result_new (G_OBJECT (session),
- callback, user_data,
- soup_session_send_request_async);
- g_simple_async_result_set_op_res_gpointer (item->result, item, (GDestroyNotify) soup_message_queue_item_unref);
-
- if (cancellable) {
- g_object_unref (item->cancellable);
- item->cancellable = g_object_ref (cancellable);
- }
-}
-
-GInputStream *
-soup_session_send_request_finish (SoupSession *session,
- GAsyncResult *result,
- GError **error)
-{
- GSimpleAsyncResult *simple;
-
- g_return_val_if_fail (SOUP_IS_SESSION_ASYNC (session), NULL);
- g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (session), soup_session_send_request_async), NULL);
-
- simple = G_SIMPLE_ASYNC_RESULT (result);
- if (g_simple_async_result_propagate_error (simple, error))
- return NULL;
- return g_object_ref (g_simple_async_result_get_op_res_gpointer (simple));
-}
-
-static void
soup_session_async_class_init (SoupSessionAsyncClass *soup_session_async_class)
{
SoupSessionClass *session_class = SOUP_SESSION_CLASS (soup_session_async_class);
- GObjectClass *object_class = G_OBJECT_CLASS (session_class);
-
- g_type_class_add_private (soup_session_async_class,
- sizeof (SoupSessionAsyncPrivate));
/* virtual method override */
- session_class->queue_message = soup_session_async_queue_message;
session_class->send_message = soup_session_async_send_message;
session_class->cancel_message = soup_session_async_cancel_message;
- session_class->auth_required = soup_session_async_auth_required;
- session_class->kick = soup_session_async_kick;
-
- object_class->dispose = soup_session_async_dispose;
}