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->status_code == SOUP_STATUS_PROXY_AUTHENTICATION_REQUIRED)
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_status_full (msg,
136 SOUP_STATUS_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, guint status,
168 SoupConnection *conn, gpointer user_data)
170 SoupMessage *req = user_data;
172 req->priv->connect_tag = NULL;
173 soup_message_set_connection (req, conn);
175 if (status != SOUP_STATUS_OK) {
176 soup_message_set_status (req, status);
177 soup_message_finished (req);
181 start_request (conn, req);
185 idle_run_queue (gpointer user_data)
187 SoupSession *session = user_data;
188 SoupMessageQueueIter iter;
190 SoupConnection *conn;
192 session->priv->queue_idle_tag = 0;
194 for (req = soup_message_queue_first (session->priv->queue, &iter); req;
195 req = soup_message_queue_next (session->priv->queue, &iter)) {
197 if (req->priv->status != SOUP_MESSAGE_STATUS_QUEUED)
200 conn = soup_message_get_connection (req);
201 if (conn && soup_connection_is_connected (conn)) {
202 start_request (conn, req);
204 gpointer connect_tag;
206 req->priv->status = SOUP_MESSAGE_STATUS_CONNECTING;
208 soup_context_get_connection (
210 got_connection, req);
213 req->priv->connect_tag = connect_tag;
221 queue_message (SoupSession *session, SoupMessage *req, gboolean requeue)
223 soup_message_prepare (req);
225 req->priv->status = SOUP_MESSAGE_STATUS_QUEUED;
227 soup_message_queue_append (session->priv->queue, req);
229 if (!session->priv->queue_idle_tag) {
230 session->priv->queue_idle_tag =
231 g_idle_add (idle_run_queue, session);
236 * soup_session_queue_message:
237 * @session: a #SoupSession
238 * @req: the message to queue
239 * @callback: a #SoupCallbackFn which will be called after the message
240 * completes or when an unrecoverable error occurs.
241 * @user_data: a pointer passed to @callback.
243 * Queues the message @req for sending. All messages are processed
244 * while the glib main loop runs. If @req has been processed before,
245 * any resources related to the time it was last sent are freed.
247 * Upon message completion, the callback specified in @callback will
248 * be invoked. If after returning from this callback the message has
249 * not been requeued, @req will be unreffed.
252 soup_session_queue_message (SoupSession *session, SoupMessage *req,
253 SoupCallbackFn callback, gpointer user_data)
255 g_return_if_fail (SOUP_IS_SESSION (session));
256 g_return_if_fail (SOUP_IS_MESSAGE (req));
258 g_signal_connect (req, "finished",
259 G_CALLBACK (request_finished), session);
261 g_signal_connect (req, "finished",
262 G_CALLBACK (callback), user_data);
264 g_signal_connect_after (req, "finished",
265 G_CALLBACK (final_finished), session);
267 soup_message_add_status_code_handler (req, SOUP_STATUS_UNAUTHORIZED,
268 SOUP_HANDLER_POST_BODY,
269 authorize_handler, session);
270 soup_message_add_status_code_handler (req,
271 SOUP_STATUS_PROXY_UNAUTHORIZED,
272 SOUP_HANDLER_POST_BODY,
273 authorize_handler, session);
275 if (!(req->priv->msg_flags & SOUP_MESSAGE_NO_REDIRECT)) {
276 soup_message_add_status_class_handler (
277 req, SOUP_STATUS_CLASS_REDIRECT,
278 SOUP_HANDLER_POST_BODY,
279 redirect_handler, session);
282 queue_message (session, req, FALSE);
286 * soup_session_requeue_message:
287 * @session: a #SoupSession
288 * @req: the message to requeue
290 * This causes @req to be placed back on the queue to be attempted
294 soup_session_requeue_message (SoupSession *session, SoupMessage *req)
296 g_return_if_fail (SOUP_IS_SESSION (session));
297 g_return_if_fail (SOUP_IS_MESSAGE (req));
299 queue_message (session, req, TRUE);
304 * soup_session_send_message:
305 * @session: a #SoupSession
306 * @req: the message to send
308 * Synchronously send @req. This call will not return until the
309 * transfer is finished successfully or there is an unrecoverable
312 * @req is not freed upon return.
314 * Return value: the HTTP status code of the response
317 soup_session_send_message (SoupSession *session, SoupMessage *req)
319 g_return_val_if_fail (SOUP_IS_SESSION (session), SOUP_STATUS_MALFORMED);
320 g_return_val_if_fail (SOUP_IS_MESSAGE (req), SOUP_STATUS_MALFORMED);
322 /* Balance out the unref that final_finished will do */
325 soup_session_queue_message (session, req, NULL, NULL);
328 g_main_iteration (TRUE);
330 if (req->priv->status == SOUP_MESSAGE_STATUS_FINISHED ||
331 SOUP_STATUS_IS_TRANSPORT (req->status_code))
335 return req->status_code;