New. An interface for objects that want to act on every message passing
[platform/upstream/libsoup.git] / libsoup / soup-session-async.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * soup-session-async.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-async.h"
13 #include "soup-connection.h"
14
15 struct SoupSessionAsyncPrivate {
16         int dummy;
17 };
18
19 static gboolean run_queue (SoupSessionAsync *sa, gboolean try_pruning);
20
21 static void  queue_message   (SoupSession *session, SoupMessage *req,
22                               SoupMessageCallbackFn callback,
23                               gpointer user_data);
24 static guint send_message    (SoupSession *session, SoupMessage *req);
25
26 #define PARENT_TYPE SOUP_TYPE_SESSION
27 static SoupSessionClass *parent_class;
28
29 static void
30 init (GObject *object)
31 {
32         SoupSessionAsync *sa = SOUP_SESSION_ASYNC (object);
33
34         sa->priv = g_new0 (SoupSessionAsyncPrivate, 1);
35 }
36
37 static void
38 finalize (GObject *object)
39 {
40         SoupSessionAsync *sa = SOUP_SESSION_ASYNC (object);
41
42         g_free (sa->priv);
43
44         G_OBJECT_CLASS (parent_class)->finalize (object);
45 }
46
47 static void
48 class_init (GObjectClass *object_class)
49 {
50         SoupSessionClass *session_class = SOUP_SESSION_CLASS (object_class);
51
52         parent_class = g_type_class_ref (PARENT_TYPE);
53
54         /* virtual method override */
55         session_class->queue_message = queue_message;
56         session_class->send_message = send_message;
57         object_class->finalize = finalize;
58 }
59
60 SOUP_MAKE_TYPE (soup_session_async, SoupSessionAsync, class_init, init, PARENT_TYPE)
61
62 SoupSession *
63 soup_session_async_new (void)
64 {
65         return g_object_new (SOUP_TYPE_SESSION_ASYNC, NULL);
66 }
67
68 SoupSession *
69 soup_session_async_new_with_options (const char *optname1, ...)
70 {
71         SoupSession *session;
72         va_list ap;
73
74         va_start (ap, optname1);
75         session = (SoupSession *)g_object_new_valist (SOUP_TYPE_SESSION_ASYNC,
76                                                       optname1, ap);
77         va_end (ap);
78
79         return session;
80 }
81
82
83 static void
84 connection_closed (SoupConnection *conn, SoupSessionAsync *sa)
85 {
86         /* Run the queue in case anyone was waiting for a connection
87          * to be closed.
88          */
89         run_queue (sa, FALSE);
90 }
91
92 static void
93 got_connection (SoupConnection *conn, guint status, gpointer user_data)
94 {
95         SoupSessionAsync *sa = user_data;
96
97         if (status == SOUP_STATUS_OK) {
98                 g_signal_connect (conn, "disconnected",
99                                   G_CALLBACK (connection_closed),
100                                   sa);
101         }
102
103         /* Either we just got a connection, or we just failed to
104          * open a connection and so decremented the open connection
105          * count by one. Either way, we need to run the queue now.
106          */
107         run_queue (sa, FALSE);
108 }
109
110 static gboolean
111 run_queue (SoupSessionAsync *sa, gboolean try_pruning)
112 {
113         SoupSession *session = SOUP_SESSION (sa);
114         SoupMessageQueueIter iter;
115         SoupMessage *msg;
116         SoupConnection *conn;
117         gboolean should_prune = FALSE, started_any = FALSE, is_new;
118
119         /* FIXME: prefer CONNECTING messages */
120
121  try_again:
122         for (msg = soup_message_queue_first (session->queue, &iter); msg; msg = soup_message_queue_next (session->queue, &iter)) {
123
124                 if (!SOUP_MESSAGE_IS_STARTING (msg))
125                         continue;
126
127                 conn = soup_session_get_connection (session, msg,
128                                                     &should_prune, &is_new);
129                 if (!conn)
130                         continue;
131
132                 if (is_new) {
133                         soup_connection_connect_async (conn, got_connection,
134                                                        session);
135                 } else
136                         soup_connection_send_request (conn, msg);
137
138                 started_any = TRUE;
139         }
140
141         if (try_pruning && should_prune && !started_any) {
142                 /* We didn't manage to start any message, but there is
143                  * at least one message in the queue that could be
144                  * sent if we pruned an idle connection from some
145                  * other server.
146                  */
147                 if (soup_session_try_prune_connection (session)) {
148                         try_pruning = FALSE;
149                         goto try_again;
150                 }
151         }
152
153         return started_any;
154 }
155
156 static void
157 request_restarted (SoupMessage *req, gpointer sa)
158 {
159         run_queue (sa, FALSE);
160 }
161
162 static void
163 final_finished (SoupMessage *req, gpointer user_data)
164 {
165         SoupSessionAsync *sa = user_data;
166
167         if (!SOUP_MESSAGE_IS_STARTING (req)) {
168                 g_signal_handlers_disconnect_by_func (req, final_finished, sa);
169                 g_object_unref (req);
170         }
171
172         run_queue (sa, FALSE);
173 }
174
175 static void
176 queue_message (SoupSession *session, SoupMessage *req,
177                SoupMessageCallbackFn callback, gpointer user_data)
178 {
179         SoupSessionAsync *sa = SOUP_SESSION_ASYNC (session);
180
181         g_signal_connect (req, "restarted",
182                           G_CALLBACK (request_restarted), sa);
183
184         if (callback) {
185                 g_signal_connect (req, "finished",
186                                   G_CALLBACK (callback), user_data);
187         }
188         g_signal_connect_after (req, "finished",
189                                 G_CALLBACK (final_finished), sa);
190
191         SOUP_SESSION_CLASS (parent_class)->queue_message (session, req,
192                                                           callback, user_data);
193
194         run_queue (sa, TRUE);
195 }
196
197 static guint
198 send_message (SoupSession *session, SoupMessage *req)
199 {
200         /* Balance out the unref that final_finished will do */
201         g_object_ref (req);
202
203         queue_message (session, req, NULL, NULL);
204
205         while (req->status != SOUP_MESSAGE_STATUS_FINISHED &&
206                !SOUP_STATUS_IS_TRANSPORT_ERROR (req->status_code))
207                 g_main_iteration (TRUE);
208
209         return req->status_code;
210 }