New. An interface for objects that want to act on every message passing
[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-connection.h"
14
15 struct SoupSessionSyncPrivate {
16         GMutex *lock;
17         GCond *cond;
18 };
19
20 void         queue_message  (SoupSession *session, SoupMessage *msg,
21                              SoupMessageCallbackFn callback,
22                              gpointer user_data);
23 static guint send_message   (SoupSession *session, SoupMessage *msg);
24 static void  cancel_message (SoupSession *session, SoupMessage *msg);
25
26 #define PARENT_TYPE SOUP_TYPE_SESSION
27 static SoupSessionClass *parent_class;
28
29 static void
30 init (GObject *object)
31 {
32         SoupSessionSync *ss = SOUP_SESSION_SYNC (object);
33
34         ss->priv = g_new0 (SoupSessionSyncPrivate, 1);
35         ss->priv->lock = g_mutex_new ();
36         ss->priv->cond = g_cond_new ();
37 }
38
39 static void
40 finalize (GObject *object)
41 {
42         SoupSessionSync *ss = SOUP_SESSION_SYNC (object);
43
44         g_mutex_free (ss->priv->lock);
45         g_cond_free (ss->priv->cond);
46         g_free (ss->priv);
47
48         G_OBJECT_CLASS (parent_class)->finalize (object);
49 }
50
51 static void
52 class_init (GObjectClass *object_class)
53 {
54         SoupSessionClass *session_class = SOUP_SESSION_CLASS (object_class);
55
56         parent_class = g_type_class_ref (PARENT_TYPE);
57
58         /* virtual method override */
59         session_class->queue_message = queue_message;
60         session_class->send_message = send_message;
61         session_class->cancel_message = cancel_message;
62         object_class->finalize = finalize;
63 }
64
65 SOUP_MAKE_TYPE (soup_session_sync, SoupSessionSync, class_init, init, PARENT_TYPE)
66
67 SoupSession *
68 soup_session_sync_new (void)
69 {
70         return g_object_new (SOUP_TYPE_SESSION_SYNC, NULL);
71 }
72
73 SoupSession *
74 soup_session_sync_new_with_options (const char *optname1, ...)
75 {
76         SoupSession *session;
77         va_list ap;
78
79         va_start (ap, optname1);
80         session = (SoupSession *)g_object_new_valist (SOUP_TYPE_SESSION_SYNC,
81                                                       optname1, ap);
82         va_end (ap);
83
84         return session;
85 }
86
87
88 void
89 queue_message (SoupSession *session, SoupMessage *msg,
90                SoupMessageCallbackFn callback, gpointer user_data)
91 {
92         /* FIXME */
93         g_warning ("soup_session_queue_message called on synchronous session");
94 }
95
96 static SoupConnection *
97 wait_for_connection (SoupSession *session, SoupMessage *msg)
98 {
99         SoupSessionSync *ss = SOUP_SESSION_SYNC (session);
100         SoupConnection *conn;
101         gboolean try_pruning = FALSE, is_new = FALSE;
102         guint status;
103
104         g_mutex_lock (ss->priv->lock);
105
106  try_again:
107         conn = soup_session_get_connection (session, msg,
108                                             &try_pruning, &is_new);
109         if (conn) {
110                 if (is_new) {
111                         status = soup_connection_connect_sync (conn);
112
113                         /* If the connection attempt fails, SoupSession
114                          * will notice, unref conn, and set an error
115                          * status on msg. So all we need to do is just
116                          * not return the no-longer-valid connection.
117                          */
118
119                         if (!SOUP_STATUS_IS_SUCCESSFUL (status))
120                                 conn = NULL;
121                         else if (msg->status == SOUP_MESSAGE_STATUS_FINISHED) {
122                                 /* Message was cancelled while we were
123                                  * connecting.
124                                  */
125                                 soup_connection_disconnect (conn);
126                                 conn = NULL;
127                         }
128                 }
129
130                 g_mutex_unlock (ss->priv->lock);
131                 return conn;
132         }
133
134         if (try_pruning && soup_session_try_prune_connection (session))
135                 goto try_again;
136
137         /* Wait... */
138         g_cond_wait (ss->priv->cond, ss->priv->lock);
139
140         /* See if something bad happened */
141         if (msg->status == SOUP_MESSAGE_STATUS_FINISHED) {
142                 g_mutex_unlock (ss->priv->lock);
143                 return NULL;
144         }
145
146         goto try_again;
147 }
148
149 static guint
150 send_message (SoupSession *session, SoupMessage *msg)
151 {
152         SoupConnection *conn;
153
154         SOUP_SESSION_CLASS (parent_class)->queue_message (session, msg,
155                                                           NULL, NULL);
156
157         do {
158                 /* Get a connection */
159                 conn = wait_for_connection (session, msg);
160                 if (!conn)
161                         return msg->status_code;
162
163                 /* Set up a weak pointer so that "conn" is zeroed out
164                  * if the connection is destroyed.
165                  */
166                 g_object_add_weak_pointer (G_OBJECT (conn),
167                                            (gpointer *)&conn);
168
169                 /* Now repeatedly send the message across the connection
170                  * until either it's done, or the connection is closed.
171                  */
172                 while (msg->status != SOUP_MESSAGE_STATUS_FINISHED && conn)
173                         soup_connection_send_request (conn, msg);
174
175                 if (conn) {
176                         g_object_remove_weak_pointer (G_OBJECT (conn),
177                                                       (gpointer *)&conn);
178                 }
179
180                 /* If the message isn't finished, that means we need to
181                  * re-send it on a new connection, so loop back to the
182                  * beginning.
183                  */
184         } while (msg->status != SOUP_MESSAGE_STATUS_FINISHED);
185
186         return msg->status_code;
187 }
188
189 static void
190 cancel_message (SoupSession *session, SoupMessage *msg)
191 {
192         SoupSessionSync *ss = SOUP_SESSION_SYNC (session);
193
194         SOUP_SESSION_CLASS (parent_class)->cancel_message (session, msg);
195         g_cond_broadcast (ss->priv->cond);
196 }
197