source: trunk/third/libsoup/libsoup/soup-session-async.c @ 21108

Revision 21108, 5.5 KB checked in by ghudson, 20 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r21107, which included commits to RCS files with non-trunk default branches.
Line 
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
15struct SoupSessionAsyncPrivate {
16        int dummy;
17};
18
19static gboolean run_queue (SoupSessionAsync *sa, gboolean try_pruning);
20
21static void  queue_message   (SoupSession *session, SoupMessage *req,
22                              SoupMessageCallbackFn callback,
23                              gpointer user_data);
24static guint send_message    (SoupSession *session, SoupMessage *req);
25
26#define PARENT_TYPE SOUP_TYPE_SESSION
27static SoupSessionClass *parent_class;
28
29static void
30init (GObject *object)
31{
32        SoupSessionAsync *sa = SOUP_SESSION_ASYNC (object);
33
34        sa->priv = g_new0 (SoupSessionAsyncPrivate, 1);
35}
36
37static void
38finalize (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
47static void
48class_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
60SOUP_MAKE_TYPE (soup_session_async, SoupSessionAsync, class_init, init, PARENT_TYPE)
61
62SoupSession *
63soup_session_async_new (void)
64{
65        return g_object_new (SOUP_TYPE_SESSION_ASYNC, NULL);
66}
67
68SoupSession *
69soup_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
83static void
84connection_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
92static void
93got_connection (SoupConnection *conn, guint status, gpointer user_data)
94{
95        SoupSessionAsync *sa = user_data;
96
97        if (status != SOUP_STATUS_OK) {
98                /* The connection attempt failed, and thus @conn was
99                 * closed and the open connection count for the
100                 * session has been decremented. (If the failure was
101                 * fatal, then SoupSession itself will have dealt
102                 * with cancelling any pending messages for that
103                 * host, so we don't need to worry about that here.)
104                 * However, there may be other messages in the
105                 * queue that were waiting for the connection count
106                 * to go down, so run the queue now.
107                 */
108                run_queue (sa, FALSE);
109                return;
110        }
111
112        g_signal_connect (conn, "disconnected",
113                          G_CALLBACK (connection_closed), sa);
114
115        /* @conn has been marked reserved by SoupSession, but we don't
116         * actually have any specific message in mind for it. (In
117         * particular, the message we were originally planning to
118         * queue on it may have already been queued on some other
119         * connection that became available while we were waiting for
120         * this one to connect.) So we release the connection into the
121         * idle pool and then just run the queue and see what happens.
122         */
123        soup_connection_release (conn);
124        run_queue (sa, FALSE);
125}
126
127static gboolean
128run_queue (SoupSessionAsync *sa, gboolean try_pruning)
129{
130        SoupSession *session = SOUP_SESSION (sa);
131        SoupMessageQueueIter iter;
132        SoupMessage *msg;
133        SoupConnection *conn;
134        gboolean should_prune = FALSE, started_any = FALSE, is_new;
135
136        /* FIXME: prefer CONNECTING messages */
137
138 try_again:
139        for (msg = soup_message_queue_first (session->queue, &iter); msg; msg = soup_message_queue_next (session->queue, &iter)) {
140
141                if (!SOUP_MESSAGE_IS_STARTING (msg))
142                        continue;
143
144                conn = soup_session_get_connection (session, msg,
145                                                    &should_prune, &is_new);
146                if (!conn)
147                        continue;
148
149                if (is_new) {
150                        soup_connection_connect_async (conn, got_connection,
151                                                       session);
152                } else
153                        soup_connection_send_request (conn, msg);
154
155                started_any = TRUE;
156        }
157
158        if (try_pruning && should_prune && !started_any) {
159                /* We didn't manage to start any message, but there is
160                 * at least one message in the queue that could be
161                 * sent if we pruned an idle connection from some
162                 * other server.
163                 */
164                if (soup_session_try_prune_connection (session)) {
165                        try_pruning = FALSE;
166                        goto try_again;
167                }
168        }
169
170        return started_any;
171}
172
173static void
174request_restarted (SoupMessage *req, gpointer sa)
175{
176        run_queue (sa, FALSE);
177}
178
179static void
180final_finished (SoupMessage *req, gpointer user_data)
181{
182        SoupSessionAsync *sa = user_data;
183
184        if (!SOUP_MESSAGE_IS_STARTING (req)) {
185                g_signal_handlers_disconnect_by_func (req, final_finished, sa);
186                g_object_unref (req);
187        }
188
189        run_queue (sa, FALSE);
190}
191
192static void
193queue_message (SoupSession *session, SoupMessage *req,
194               SoupMessageCallbackFn callback, gpointer user_data)
195{
196        SoupSessionAsync *sa = SOUP_SESSION_ASYNC (session);
197
198        g_signal_connect (req, "restarted",
199                          G_CALLBACK (request_restarted), sa);
200
201        if (callback) {
202                g_signal_connect (req, "finished",
203                                  G_CALLBACK (callback), user_data);
204        }
205        g_signal_connect_after (req, "finished",
206                                G_CALLBACK (final_finished), sa);
207
208        SOUP_SESSION_CLASS (parent_class)->queue_message (session, req,
209                                                          callback, user_data);
210
211        run_queue (sa, TRUE);
212}
213
214static guint
215send_message (SoupSession *session, SoupMessage *req)
216{
217        /* Balance out the unref that final_finished will do */
218        g_object_ref (req);
219
220        queue_message (session, req, NULL, NULL);
221
222        while (req->status != SOUP_MESSAGE_STATUS_FINISHED &&
223               !SOUP_STATUS_IS_TRANSPORT_ERROR (req->status_code))
224                g_main_iteration (TRUE);
225
226        return req->status_code;
227}
Note: See TracBrowser for help on using the repository browser.