1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
5 * Copyright (C) 2000-2003, Ximian, Inc.
12 #define LIBSOUP_I_HAVE_READ_BUG_594377_AND_KNOW_SOUP_PASSWORD_MANAGER_MIGHT_GO_AWAY
14 #include "soup-address.h"
15 #include "soup-session-sync.h"
16 #include "soup-session-private.h"
17 #include "soup-address.h"
18 #include "soup-message-private.h"
19 #include "soup-misc.h"
20 #include "soup-password-manager.h"
21 #include "soup-proxy-uri-resolver.h"
25 * SECTION:soup-session-sync
26 * @short_description: Soup session for blocking I/O in multithreaded
29 * #SoupSessionSync is an implementation of #SoupSession that uses
30 * synchronous I/O, intended for use in multi-threaded programs.
32 * You can use #SoupSessionSync from multiple threads concurrently.
33 * Eg, you can send a #SoupMessage in one thread, and then while
34 * waiting for the response, send another #SoupMessage from another
35 * thread. You can also send a message from one thread and then call
36 * soup_session_cancel_message() on it from any other thread (although
37 * you need to be careful to avoid race conditions, where the message
38 * finishes and is then unreffed by the sending thread just before you
41 * However, the majority of other types and methods in libsoup are not
42 * MT-safe. In particular, you <emphasis>cannot</emphasis> modify or
43 * examine a #SoupMessage while it is being transmitted by
44 * #SoupSessionSync in another thread. Once a message has been handed
45 * off to #SoupSessionSync, it can only be manipulated from its signal
46 * handler callbacks, until I/O is complete.
52 } SoupSessionSyncPrivate;
53 #define SOUP_SESSION_SYNC_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_SESSION_SYNC, SoupSessionSyncPrivate))
55 static void queue_message (SoupSession *session, SoupMessage *msg,
56 SoupSessionCallback callback, gpointer user_data);
57 static guint send_message (SoupSession *session, SoupMessage *msg);
58 static void cancel_message (SoupSession *session, SoupMessage *msg,
60 static void auth_required (SoupSession *session, SoupMessage *msg,
61 SoupAuth *auth, gboolean retrying);
63 G_DEFINE_TYPE (SoupSessionSync, soup_session_sync, SOUP_TYPE_SESSION)
66 soup_session_sync_init (SoupSessionSync *ss)
68 SoupSessionSyncPrivate *priv = SOUP_SESSION_SYNC_GET_PRIVATE (ss);
70 priv->lock = g_mutex_new ();
71 priv->cond = g_cond_new ();
75 finalize (GObject *object)
77 SoupSessionSyncPrivate *priv = SOUP_SESSION_SYNC_GET_PRIVATE (object);
79 g_mutex_free (priv->lock);
80 g_cond_free (priv->cond);
82 G_OBJECT_CLASS (soup_session_sync_parent_class)->finalize (object);
86 soup_session_sync_class_init (SoupSessionSyncClass *session_sync_class)
88 GObjectClass *object_class = G_OBJECT_CLASS (session_sync_class);
89 SoupSessionClass *session_class = SOUP_SESSION_CLASS (session_sync_class);
91 g_type_class_add_private (session_sync_class, sizeof (SoupSessionSyncPrivate));
93 /* virtual method override */
94 session_class->queue_message = queue_message;
95 session_class->send_message = send_message;
96 session_class->cancel_message = cancel_message;
97 session_class->auth_required = auth_required;
98 object_class->finalize = finalize;
103 * soup_session_sync_new:
105 * Creates an synchronous #SoupSession with the default options.
107 * Return value: the new session.
110 soup_session_sync_new (void)
112 return g_object_new (SOUP_TYPE_SESSION_SYNC, NULL);
116 * soup_session_sync_new_with_options:
117 * @optname1: name of first property to set
118 * @...: value of @optname1, followed by additional property/value pairs
120 * Creates an synchronous #SoupSession with the specified options.
122 * Return value: the new session.
125 soup_session_sync_new_with_options (const char *optname1, ...)
127 SoupSession *session;
130 va_start (ap, optname1);
131 session = (SoupSession *)g_object_new_valist (SOUP_TYPE_SESSION_SYNC,
139 tunnel_connect (SoupSession *session, SoupConnection *conn,
140 SoupAddress *tunnel_addr)
142 SoupMessageQueueItem *item;
147 g_signal_emit_by_name (session, "tunneling", conn);
148 item = soup_session_make_connect_message (session, tunnel_addr);
150 soup_session_send_queue_item (session, item, conn);
151 while (SOUP_MESSAGE_IS_STARTING (item->msg));
153 status = item->msg->status_code;
154 soup_message_queue_item_unref (item);
156 if (SOUP_STATUS_IS_SUCCESSFUL (status)) {
157 if (!soup_connection_start_ssl (conn))
158 status = SOUP_STATUS_SSL_FAILED;
161 if (!SOUP_STATUS_IS_SUCCESSFUL (status))
162 soup_session_connection_failed (session, conn, status);
164 g_object_unref (conn);
168 static SoupConnection *
169 wait_for_connection (SoupMessageQueueItem *item)
171 SoupSession *session = item->session;
172 SoupMessage *msg = item->msg;
173 SoupSessionSyncPrivate *priv = SOUP_SESSION_SYNC_GET_PRIVATE (session);
174 gboolean try_pruning = FALSE;
175 SoupProxyURIResolver *proxy_resolver;
176 SoupAddress *tunnel_addr;
177 SoupConnection *conn;
180 proxy_resolver = (SoupProxyURIResolver *)soup_session_get_feature_for_message (session, SOUP_TYPE_PROXY_URI_RESOLVER, msg);
181 if (proxy_resolver && !item->resolved_proxy_addr) {
182 status = soup_proxy_uri_resolver_get_proxy_uri_sync (
183 proxy_resolver, soup_message_get_uri (msg),
184 item->cancellable, &item->proxy_uri);
185 if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
186 if (status != SOUP_STATUS_CANCELLED)
187 soup_session_cancel_message (session, msg, status);
191 if (item->proxy_uri) {
192 item->proxy_addr = soup_address_new (
193 item->proxy_uri->host, item->proxy_uri->port);
194 status = soup_address_resolve_sync (item->proxy_addr,
196 if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
197 if (status != SOUP_STATUS_CANCELLED)
198 soup_session_cancel_message (session, msg, status);
203 item->resolved_proxy_addr = TRUE;
206 g_mutex_lock (priv->lock);
208 soup_session_cleanup_connections (session, FALSE);
211 conn = soup_session_get_connection (session, item, &try_pruning);
213 if (soup_connection_get_state (conn) == SOUP_CONNECTION_NEW) {
214 status = soup_connection_connect_sync (conn);
216 if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
217 soup_session_connection_failed (session, conn, status);
219 } else if (soup_message_get_io_status (msg) == SOUP_MESSAGE_IO_STATUS_FINISHED) {
220 /* Message was cancelled while we were
223 soup_connection_disconnect (conn);
225 } else if ((tunnel_addr = soup_connection_get_tunnel_addr (conn))) {
226 status = tunnel_connect (session, conn, tunnel_addr);
227 if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
234 g_mutex_unlock (priv->lock);
240 if (soup_session_cleanup_connections (session, TRUE))
245 g_cond_wait (priv->cond, priv->lock);
247 /* See if something bad happened */
248 if (soup_message_get_io_status (msg) == SOUP_MESSAGE_IO_STATUS_FINISHED) {
249 g_mutex_unlock (priv->lock);
257 process_queue_item (SoupMessageQueueItem *item)
259 SoupSessionSyncPrivate *priv = SOUP_SESSION_SYNC_GET_PRIVATE (item->session);
260 SoupConnection *conn;
263 conn = wait_for_connection (item);
267 soup_session_send_queue_item (item->session, item, conn);
268 g_cond_broadcast (priv->cond);
269 } while (soup_message_get_io_status (item->msg) !=
270 SOUP_MESSAGE_IO_STATUS_FINISHED);
274 queue_message_callback (gpointer data)
276 SoupMessageQueueItem *item = data;
278 item->callback (item->session, item->msg, item->callback_data);
279 g_object_unref (item->session);
280 soup_message_queue_item_unref (item);
285 queue_message_thread (gpointer data)
287 SoupMessageQueueItem *item = data;
289 process_queue_item (item);
290 if (item->callback) {
291 soup_add_completion (soup_session_get_async_context (item->session),
292 queue_message_callback, item);
294 g_object_unref (item->session);
295 soup_message_queue_item_unref (item);
302 queue_message (SoupSession *session, SoupMessage *msg,
303 SoupSessionCallback callback, gpointer user_data)
305 SoupMessageQueueItem *item;
307 SOUP_SESSION_CLASS (soup_session_sync_parent_class)->
308 queue_message (g_object_ref (session), msg, callback, user_data);
310 item = soup_message_queue_lookup (soup_session_get_queue (session), msg);
311 g_return_if_fail (item != NULL);
313 g_thread_create (queue_message_thread, item, FALSE, NULL);
317 send_message (SoupSession *session, SoupMessage *msg)
319 SoupMessageQueueItem *item;
322 SOUP_SESSION_CLASS (soup_session_sync_parent_class)->queue_message (session, msg, NULL, NULL);
324 item = soup_message_queue_lookup (soup_session_get_queue (session), msg);
325 g_return_val_if_fail (item != NULL, SOUP_STATUS_MALFORMED);
327 process_queue_item (item);
328 status = msg->status_code;
329 soup_message_queue_item_unref (item);
334 cancel_message (SoupSession *session, SoupMessage *msg, guint status_code)
336 SoupSessionSyncPrivate *priv = SOUP_SESSION_SYNC_GET_PRIVATE (session);
338 SOUP_SESSION_CLASS (soup_session_sync_parent_class)->cancel_message (session, msg, status_code);
339 g_cond_broadcast (priv->cond);
343 auth_required (SoupSession *session, SoupMessage *msg,
344 SoupAuth *auth, gboolean retrying)
346 SoupSessionFeature *password_manager;
348 password_manager = soup_session_get_feature_for_message (
349 session, SOUP_TYPE_PASSWORD_MANAGER, msg);
350 if (password_manager) {
351 soup_password_manager_get_passwords_sync (
352 SOUP_PASSWORD_MANAGER (password_manager),
353 msg, auth, NULL); /* FIXME cancellable */
356 SOUP_SESSION_CLASS (soup_session_sync_parent_class)->
357 auth_required (session, msg, auth, retrying);