1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
5 * Copyright (C) 2000-2003, Ximian, Inc.
16 #include "soup-session.h"
17 #include "soup-connection.h"
18 #include "soup-message-private.h"
19 #include "soup-message-queue.h"
20 #include "soup-private.h"
22 struct SoupSessionPrivate {
23 SoupMessageQueue *queue;
28 #define PARENT_TYPE G_TYPE_OBJECT
29 static GObjectClass *parent_class;
32 init (GObject *object)
34 SoupSession *session = SOUP_SESSION (object);
36 session->priv = g_new0 (SoupSessionPrivate, 1);
37 session->priv->queue = soup_message_queue_new ();
41 finalize (GObject *object)
43 SoupSession *session = SOUP_SESSION (object);
44 SoupMessageQueueIter iter;
47 if (session->priv->queue_idle_tag)
48 g_source_remove (session->priv->queue_idle_tag);
50 for (msg = soup_message_queue_first (session->priv->queue, &iter); msg;
51 msg = soup_message_queue_next (session->priv->queue, &iter)) {
52 soup_message_queue_remove (session->priv->queue, &iter);
53 soup_message_cancel (msg);
55 soup_message_queue_destroy (session->priv->queue);
57 g_free (session->priv);
59 G_OBJECT_CLASS (parent_class)->finalize (object);
63 class_init (GObjectClass *object_class)
65 parent_class = g_type_class_ref (PARENT_TYPE);
67 /* virtual method override */
68 object_class->finalize = finalize;
71 SOUP_MAKE_TYPE (soup_session, SoupSession, class_init, init, PARENT_TYPE)
75 soup_session_new (void)
77 return g_object_new (SOUP_TYPE_SESSION, NULL);
81 /* Default handlers */
84 authorize_handler (SoupMessage *msg, gpointer user_data)
86 SoupSession *session = user_data;
89 if (msg->errorcode == SOUP_ERROR_PROXY_UNAUTHORIZED)
90 ctx = soup_get_proxy ();
92 ctx = msg->priv->context;
94 if (soup_context_update_auth (ctx, msg))
95 soup_session_requeue_message (session, msg);
99 redirect_handler (SoupMessage *msg, gpointer user_data)
101 SoupSession *session = user_data;
103 const SoupUri *old_uri;
105 SoupContext *new_ctx;
107 new_loc = soup_message_get_header (msg->response_headers, "Location");
110 new_uri = soup_uri_new (new_loc);
112 goto INVALID_REDIRECT;
114 old_uri = soup_message_get_uri (msg);
116 /* Copy auth info from original URI. */
117 if (old_uri->user && !new_uri->user)
118 soup_uri_set_auth (new_uri,
123 new_ctx = soup_context_from_uri (new_uri);
124 soup_uri_free (new_uri);
126 goto INVALID_REDIRECT;
128 soup_message_set_context (msg, new_ctx);
129 g_object_unref (new_ctx);
131 soup_session_requeue_message (session, msg);
135 soup_message_set_error_full (msg,
136 SOUP_ERROR_MALFORMED,
137 "Invalid Redirect URL");
141 request_finished (SoupMessage *req, gpointer user_data)
143 SoupSession *session = user_data;
145 soup_message_queue_remove_message (session->priv->queue, req);
146 req->priv->status = SOUP_MESSAGE_STATUS_FINISHED;
150 final_finished (SoupMessage *req, gpointer session)
152 if (!SOUP_MESSAGE_IS_STARTING (req)) {
153 g_signal_handlers_disconnect_by_func (req, request_finished, session);
154 g_signal_handlers_disconnect_by_func (req, final_finished, session);
155 g_object_unref (req);
160 start_request (SoupConnection *conn, SoupMessage *req)
162 req->priv->status = SOUP_MESSAGE_STATUS_RUNNING;
163 soup_connection_send_request (conn, req);
167 got_connection (SoupContext *ctx, SoupKnownErrorCode err,
168 SoupConnection *conn, gpointer user_data)
170 SoupMessage *req = user_data;
172 req->priv->connect_tag = NULL;
173 soup_message_set_connection (req, conn);
177 start_request (conn, req);
181 soup_message_set_error (req, err);
182 soup_message_finished (req);
190 idle_run_queue (gpointer user_data)
192 SoupSession *session = user_data;
193 SoupMessageQueueIter iter;
195 SoupConnection *conn;
197 session->priv->queue_idle_tag = 0;
199 for (req = soup_message_queue_first (session->priv->queue, &iter); req;
200 req = soup_message_queue_next (session->priv->queue, &iter)) {
202 if (req->priv->status != SOUP_MESSAGE_STATUS_QUEUED)
205 conn = soup_message_get_connection (req);
206 if (conn && soup_connection_is_connected (conn)) {
207 start_request (conn, req);
209 gpointer connect_tag;
211 req->priv->status = SOUP_MESSAGE_STATUS_CONNECTING;
213 soup_context_get_connection (
215 got_connection, req);
218 req->priv->connect_tag = connect_tag;
226 queue_message (SoupSession *session, SoupMessage *req, gboolean requeue)
228 soup_message_prepare (req);
230 req->priv->status = SOUP_MESSAGE_STATUS_QUEUED;
232 soup_message_queue_append (session->priv->queue, req);
234 if (!session->priv->queue_idle_tag) {
235 session->priv->queue_idle_tag =
236 g_idle_add (idle_run_queue, session);
241 * soup_session_queue_message:
242 * @session: a #SoupSession
243 * @req: the message to queue
244 * @callback: a #SoupCallbackFn which will be called after the message
245 * completes or when an unrecoverable error occurs.
246 * @user_data: a pointer passed to @callback.
248 * Queues the message @req for sending. All messages are processed
249 * while the glib main loop runs. If @req has been processed before,
250 * any resources related to the time it was last sent are freed.
252 * Upon message completion, the callback specified in @callback will
253 * be invoked. If after returning from this callback the message has
254 * not been requeued, @req will be unreffed.
257 soup_session_queue_message (SoupSession *session, SoupMessage *req,
258 SoupCallbackFn callback, gpointer user_data)
260 g_return_if_fail (SOUP_IS_SESSION (session));
261 g_return_if_fail (SOUP_IS_MESSAGE (req));
263 g_signal_connect (req, "finished",
264 G_CALLBACK (request_finished), session);
266 g_signal_connect (req, "finished",
267 G_CALLBACK (callback), user_data);
269 g_signal_connect_after (req, "finished",
270 G_CALLBACK (final_finished), session);
272 soup_message_add_error_code_handler (req, SOUP_ERROR_UNAUTHORIZED,
273 SOUP_HANDLER_POST_BODY,
274 authorize_handler, session);
275 soup_message_add_error_code_handler (req,
276 SOUP_ERROR_PROXY_UNAUTHORIZED,
277 SOUP_HANDLER_POST_BODY,
278 authorize_handler, session);
280 if (!(req->priv->msg_flags & SOUP_MESSAGE_NO_REDIRECT)) {
281 soup_message_add_error_class_handler (
282 req, SOUP_ERROR_CLASS_REDIRECT, SOUP_HANDLER_POST_BODY,
283 redirect_handler, session);
286 queue_message (session, req, FALSE);
290 * soup_session_requeue_message:
291 * @session: a #SoupSession
292 * @req: the message to requeue
294 * This causes @req to be placed back on the queue to be attempted
298 soup_session_requeue_message (SoupSession *session, SoupMessage *req)
300 g_return_if_fail (SOUP_IS_SESSION (session));
301 g_return_if_fail (SOUP_IS_MESSAGE (req));
303 queue_message (session, req, TRUE);
308 * soup_session_send_message:
309 * @session: a #SoupSession
310 * @req: the message to send
312 * Synchronously send @req. This call will not return until the
313 * transfer is finished successfully or there is an unrecoverable
316 * @req is not freed upon return.
318 * Return value: the #SoupErrorClass of the error encountered while
319 * sending or reading the response.
322 soup_session_send_message (SoupSession *session, SoupMessage *req)
324 g_return_val_if_fail (SOUP_IS_SESSION (session), SOUP_ERROR_CLASS_TRANSPORT);
325 g_return_val_if_fail (SOUP_IS_MESSAGE (req), SOUP_ERROR_CLASS_TRANSPORT);
327 /* Balance out the unref that final_finished will do */
330 soup_session_queue_message (session, req, NULL, NULL);
333 g_main_iteration (TRUE);
335 if (req->priv->status == SOUP_MESSAGE_STATUS_FINISHED ||
336 SOUP_ERROR_IS_TRANSPORT (req->errorcode))
340 return req->errorclass;