Change the SoupURI properties to SoupAddress properties.
[platform/upstream/libsoup.git] / libsoup / soup-session-sync.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * soup-session-sync.c
4  *
5  * Copyright (C) 2000-2003, Ximian, Inc.
6  */
7
8 #ifdef HAVE_CONFIG_H
9 #include <config.h>
10 #endif
11
12 #include "soup-session-sync.h"
13 #include "soup-session-private.h"
14 #include "soup-address.h"
15 #include "soup-message-private.h"
16 #include "soup-misc.h"
17
18 /**
19  * SECTION:soup-session-sync
20  * @short_description: Soup session for blocking I/O in multithreaded
21  * programs.
22  *
23  * #SoupSessionSync is an implementation of #SoupSession that uses
24  * synchronous I/O, intended for use in multi-threaded programs.
25  *
26  * You can use #SoupSessionSync from multiple threads concurrently.
27  * Eg, you can send a #SoupMessage in one thread, and then while
28  * waiting for the response, send another #SoupMessage from another
29  * thread. You can also send a message from one thread and then call
30  * soup_session_cancel_message() on it from any other thread (although
31  * you need to be careful to avoid race conditions, where the message
32  * finishes and is then unreffed by the sending thread just before you
33  * cancel it).
34  *
35  * However, the majority of other types and methods in libsoup are not
36  * MT-safe. In particular, you <emphasis>cannot</emphasis> modify or
37  * examine a #SoupMessage while it is being transmitted by
38  * #SoupSessionSync in another thread. Once a message has been handed
39  * off to #SoupSessionSync, it can only be manipulated from its signal
40  * handler callbacks, until I/O is complete.
41  **/
42
43 typedef struct {
44         GMutex *lock;
45         GCond *cond;
46 } SoupSessionSyncPrivate;
47 #define SOUP_SESSION_SYNC_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_SESSION_SYNC, SoupSessionSyncPrivate))
48
49 static void  queue_message  (SoupSession *session, SoupMessage *msg,
50                              SoupSessionCallback callback, gpointer user_data);
51 static guint send_message   (SoupSession *session, SoupMessage *msg);
52 static void  cancel_message (SoupSession *session, SoupMessage *msg,
53                              guint status_code);
54
55 G_DEFINE_TYPE (SoupSessionSync, soup_session_sync, SOUP_TYPE_SESSION)
56
57 static void
58 soup_session_sync_init (SoupSessionSync *ss)
59 {
60         SoupSessionSyncPrivate *priv = SOUP_SESSION_SYNC_GET_PRIVATE (ss);
61
62         priv->lock = g_mutex_new ();
63         priv->cond = g_cond_new ();
64 }
65
66 static void
67 finalize (GObject *object)
68 {
69         SoupSessionSyncPrivate *priv = SOUP_SESSION_SYNC_GET_PRIVATE (object);
70
71         g_mutex_free (priv->lock);
72         g_cond_free (priv->cond);
73
74         G_OBJECT_CLASS (soup_session_sync_parent_class)->finalize (object);
75 }
76
77 static void
78 soup_session_sync_class_init (SoupSessionSyncClass *session_sync_class)
79 {
80         GObjectClass *object_class = G_OBJECT_CLASS (session_sync_class);
81         SoupSessionClass *session_class = SOUP_SESSION_CLASS (session_sync_class);
82
83         g_type_class_add_private (session_sync_class, sizeof (SoupSessionSyncPrivate));
84
85         /* virtual method override */
86         session_class->queue_message = queue_message;
87         session_class->send_message = send_message;
88         session_class->cancel_message = cancel_message;
89         object_class->finalize = finalize;
90 }
91
92
93 /**
94  * soup_session_sync_new:
95  *
96  * Creates an synchronous #SoupSession with the default options.
97  *
98  * Return value: the new session.
99  **/
100 SoupSession *
101 soup_session_sync_new (void)
102 {
103         return g_object_new (SOUP_TYPE_SESSION_SYNC, NULL);
104 }
105
106 /**
107  * soup_session_sync_new_with_options:
108  * @optname1: name of first property to set
109  * @...: value of @optname1, followed by additional property/value pairs
110  *
111  * Creates an synchronous #SoupSession with the specified options.
112  *
113  * Return value: the new session.
114  **/
115 SoupSession *
116 soup_session_sync_new_with_options (const char *optname1, ...)
117 {
118         SoupSession *session;
119         va_list ap;
120
121         va_start (ap, optname1);
122         session = (SoupSession *)g_object_new_valist (SOUP_TYPE_SESSION_SYNC,
123                                                       optname1, ap);
124         va_end (ap);
125
126         return session;
127 }
128
129 static SoupConnection *
130 wait_for_connection (SoupSession *session, SoupMessage *msg)
131 {
132         SoupSessionSyncPrivate *priv = SOUP_SESSION_SYNC_GET_PRIVATE (session);
133         SoupConnection *conn;
134         gboolean try_pruning = FALSE, is_new = FALSE;
135         guint status;
136
137         g_mutex_lock (priv->lock);
138
139  try_again:
140         conn = soup_session_get_connection (session, msg,
141                                             &try_pruning, &is_new);
142         if (conn) {
143                 if (is_new) {
144                         status = soup_connection_connect_sync (conn);
145
146                         /* If the connection attempt fails, SoupSession
147                          * will notice, unref conn, and set an error
148                          * status on msg. So all we need to do is just
149                          * not return the no-longer-valid connection.
150                          */
151
152                         if (status == SOUP_STATUS_TRY_AGAIN)
153                                 goto try_again;
154                         else if (!SOUP_STATUS_IS_SUCCESSFUL (status))
155                                 conn = NULL;
156                         else if (soup_message_get_io_status (msg) == SOUP_MESSAGE_IO_STATUS_FINISHED) {
157                                 /* Message was cancelled while we were
158                                  * connecting.
159                                  */
160                                 soup_connection_disconnect (conn);
161                                 conn = NULL;
162                         }
163                 }
164
165                 g_mutex_unlock (priv->lock);
166                 return conn;
167         }
168
169         if (try_pruning && soup_session_try_prune_connection (session))
170                 goto try_again;
171
172         /* Wait... */
173         g_cond_wait (priv->cond, priv->lock);
174
175         /* See if something bad happened */
176         if (soup_message_get_io_status (msg) == SOUP_MESSAGE_IO_STATUS_FINISHED) {
177                 g_mutex_unlock (priv->lock);
178                 return NULL;
179         }
180
181         goto try_again;
182 }
183
184 static void
185 process_queue_item (SoupMessageQueueItem *item)
186 {
187         SoupSessionSyncPrivate *priv = SOUP_SESSION_SYNC_GET_PRIVATE (item->session);
188         SoupMessage *msg = item->msg;
189         SoupConnection *conn;
190         SoupAddress *addr;
191         guint status;
192
193         do {
194                 /* Resolve address */
195                 addr = soup_message_get_address (msg);
196                 status = soup_address_resolve_sync (addr, item->cancellable);
197                 if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
198                         if (status != SOUP_STATUS_CANCELLED)
199                                 soup_session_cancel_message (item->session, msg, status);
200                         break;
201                 }
202
203                 /* Get a connection */
204                 conn = wait_for_connection (item->session, msg);
205                 if (!conn)
206                         break;
207
208                 soup_connection_send_request (conn, msg);
209                 g_cond_broadcast (priv->cond);
210         } while (soup_message_get_io_status (msg) != SOUP_MESSAGE_IO_STATUS_FINISHED);
211
212         soup_message_queue_remove (item->queue, item);
213 }
214
215 static gboolean
216 queue_message_callback (gpointer data)
217 {
218         SoupMessageQueueItem *item = data;
219
220         item->callback (item->session, item->msg, item->callback_data);
221         g_object_unref (item->session);
222         soup_message_queue_item_unref (item);
223         return FALSE;
224 }
225
226 static gpointer
227 queue_message_thread (gpointer data)
228 {
229         SoupMessageQueueItem *item = data;
230
231         process_queue_item (item);
232         if (item->callback) {
233                 soup_add_completion (soup_session_get_async_context (item->session),
234                                      queue_message_callback, item);
235         } else {
236                 g_object_unref (item->session);
237                 soup_message_queue_item_unref (item);
238         }
239
240         return NULL;
241 }
242
243 static void
244 queue_message (SoupSession *session, SoupMessage *msg,
245                SoupSessionCallback callback, gpointer user_data)
246 {
247         SoupMessageQueueItem *item;
248
249         SOUP_SESSION_CLASS (soup_session_sync_parent_class)->
250                 queue_message (g_object_ref (session), msg, callback, user_data);
251
252         item = soup_message_queue_lookup (soup_session_get_queue (session), msg);
253         g_return_if_fail (item != NULL);
254
255         g_thread_create (queue_message_thread, item, FALSE, NULL);
256 }
257
258 static guint
259 send_message (SoupSession *session, SoupMessage *msg)
260 {
261         SoupMessageQueueItem *item;
262         guint status;
263
264         SOUP_SESSION_CLASS (soup_session_sync_parent_class)->queue_message (session, msg, NULL, NULL);
265
266         item = soup_message_queue_lookup (soup_session_get_queue (session), msg);
267         g_return_val_if_fail (item != NULL, SOUP_STATUS_MALFORMED);
268
269         process_queue_item (item);
270         status = msg->status_code;
271         soup_message_queue_item_unref (item);
272         return status;
273 }
274
275 static void
276 cancel_message (SoupSession *session, SoupMessage *msg, guint status_code)
277 {
278         SoupSessionSyncPrivate *priv = SOUP_SESSION_SYNC_GET_PRIVATE (session);
279
280         SOUP_SESSION_CLASS (soup_session_sync_parent_class)->cancel_message (session, msg, status_code);
281         g_cond_broadcast (priv->cond);
282 }
283