+2003-09-02 Dan Winship <danw@ximian.com>
+
+ * libsoup/soup-session.c: First draft at the new object to
+ maintain formerly-global state. (Not yet complete; still need to
+ get rid of SoupContext).
+
+ * libsoup/soup-message-queue.c: Data structure used by SoupSession
+
+ * libsoup/soup-queue.c: Gone. Mostly moved into soup-session, but
+ some bits went into soup-connection.
+
+ * libsoup/soup-connection.c (soup_connection_send_request): New,
+ to send a request on a connection. The connection updates its
+ internal state and then hands off to soup_message_send_request.
+ (request_done): Callback set up by soup_connection_send_request.
+ Marks the connection as no-longer-in-use, and disconnects it if
+ the message says to.
+ (soup_connection_set_in_use, soup_connection_mark_old): No longer
+ needed; the connection takes care of this itself now.
+ (soup_connection_new_proxy): New, to create a new connection that
+ is explicitly marked as being through an HTTP proxy.
+ (soup_connection_new_tunnel): New, to create a new HTTPS
+ connection through a proxy. (Includes the code to send the
+ CONNECT.)
+
+ * libsoup/soup-context.c (try_existing_connections): Don't need to
+ call soup_connection_set_in_use.
+ (try_create_connection): Use soup_connection_new,
+ soup_connection_new_proxy, or soup_connection_new_tunnel as
+ appropriate.
+
+ * libsoup/soup-message.c (soup_message_prepare): Replaces
+ queue_message.
+ (soup_message_queue, soup_message_requeue, soup_message_prepare):
+ Gone. This must be done via a SoupSession now.
+ (soup_message_set_connection): don't need to mark in_use/not
+ in_use. Also, msg->priv->socket is gone now.
+ (soup_message_get_socket): Gone.
+
+ * libsoup/soup-message-handlers.c (soup_message_run_handlers):
+ Remove references to global handlers.
+ (redirect_handler, authorize_handler): Moved to soup-session.c.
+
+ * libsoup/soup-misc.c (soup_shutdown): Gone; just unref the
+ session to shut down now.
+
+ * libsoup/soup.h: add soup-session.h
+
+ * libsoup/Makefile.am: updates
+
+ * tests/auth-test.c, tests/get.c, tests/simple-proxy.c: Use
+ SoupSession.
+
2003-08-29 Dan Winship <danw@ximian.com>
* libsoup/soup-message-io.c: Major rewrite. There is now only a
soup-server-auth.h \
soup-server-message.h \
soup-server.h \
+ soup-session.h \
soup-socket.h \
soup-types.h \
soup-uri.h
soup-message-handlers.c \
soup-message-io.c \
soup-message-private.h \
+ soup-message-queue.c \
+ soup-message-queue.h \
soup-message-server-io.c \
soup-method.c \
soup-misc.c \
soup-private.h \
- soup-queue.h \
- soup-queue.c \
soup-server.c \
soup-server-auth.c \
soup-server-message.c \
+ soup-session.c \
soup-socket.c \
soup-ssl.h \
soup-ssl.c \
#include "soup-ssl.h"
struct SoupConnectionPrivate {
- SoupSocket *socket;
- gboolean in_use, new;
- time_t last_used;
- guint death_tag;
+ SoupSocket *socket;
+ SoupUri *dest_uri;
+ gboolean is_proxy;
+ time_t last_used;
+
+ SoupMessage *cur_req;
};
#define PARENT_TYPE G_TYPE_OBJECT
static guint signals[LAST_SIGNAL] = { 0 };
+static void request_done (SoupMessage *req, gpointer user_data);
+
static void
init (GObject *object)
{
SoupConnection *conn = SOUP_CONNECTION (object);
conn->priv = g_new0 (SoupConnectionPrivate, 1);
- conn->priv->in_use = FALSE;
- conn->priv->new = TRUE;
}
static void
{
SoupConnection *conn = SOUP_CONNECTION (object);
- soup_connection_disconnect (conn);
+ if (conn->priv->dest_uri)
+ soup_uri_free (conn->priv->dest_uri);
+
g_free (conn->priv);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
+dispose (GObject *object)
+{
+ SoupConnection *conn = SOUP_CONNECTION (object);
+
+ if (conn->priv->cur_req)
+ request_done (conn->priv->cur_req, conn);
+ soup_connection_disconnect (conn);
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
class_init (GObjectClass *object_class)
{
parent_class = g_type_class_ref (PARENT_TYPE);
/* virtual method override */
+ object_class->dispose = dispose;
object_class->finalize = finalize;
/* signals */
soup_connection_disconnect (conn);
}
+static SoupConnection *
+connection_new (const SoupUri *uri, gboolean is_proxy,
+ SoupSocketCallback connect_callback,
+ SoupConnectionCallback user_callback,
+ gpointer user_data)
+{
+ SoupConnection *conn;
+
+ conn = g_object_new (SOUP_TYPE_CONNECTION, NULL);
+ conn->priv->is_proxy = is_proxy;
+
+ soup_signal_connect_once (conn, "connect_result",
+ G_CALLBACK (user_callback), user_data);
+
+ conn->priv->socket = soup_socket_client_new (uri->host, uri->port,
+ uri->protocol == SOUP_PROTOCOL_HTTPS,
+ connect_callback, conn);
+ g_signal_connect (conn->priv->socket, "disconnected",
+ G_CALLBACK (socket_disconnected), conn);
+ return conn;
+}
+
static void
socket_connected (SoupSocket *sock, SoupKnownErrorCode status, gpointer conn)
{
soup_connection_new (const SoupUri *uri,
SoupConnectionCallback callback, gpointer user_data)
{
- SoupConnection *conn;
+ return connection_new (uri, FALSE, socket_connected,
+ callback, user_data);
+}
- conn = g_object_new (SOUP_TYPE_CONNECTION, NULL);
+static void
+proxy_socket_connected (SoupSocket *sock, SoupKnownErrorCode status, gpointer conn)
+{
+ if (status == SOUP_ERROR_CANT_RESOLVE)
+ status = SOUP_ERROR_CANT_RESOLVE_PROXY;
+ else if (status == SOUP_ERROR_CANT_CONNECT)
+ status = SOUP_ERROR_CANT_CONNECT_PROXY;
- soup_signal_connect_once (conn, "connect_result",
- G_CALLBACK (callback), user_data);
- conn->priv->socket = soup_socket_client_new (uri->host, uri->port,
- uri->protocol == SOUP_PROTOCOL_HTTPS,
- socket_connected, conn);
- g_signal_connect (conn->priv->socket, "disconnected",
- G_CALLBACK (socket_disconnected), conn);
+ g_signal_emit (conn, signals[CONNECT_RESULT], 0, status);
+}
+
+/**
+ * soup_connection_new_proxy:
+ * @proxy_uri: proxy to connect to
+ * @callback: callback to call after connecting
+ * @user_data: data for @callback
+ *
+ * Creates a connection to @proxy_uri. @callback will be called when
+ * the connection completes (or fails).
+ *
+ * Return value: the new connection (not yet ready for use).
+ **/
+SoupConnection *
+soup_connection_new_proxy (const SoupUri *proxy_uri,
+ SoupConnectionCallback callback,
+ gpointer user_data)
+{
+ return connection_new (proxy_uri, TRUE, proxy_socket_connected,
+ callback, user_data);
+}
+
+static void
+tunnel_connected (SoupMessage *msg, gpointer user_data)
+{
+ SoupConnection *conn = user_data;
+
+ if (SOUP_ERROR_IS_SUCCESSFUL (msg->errorcode))
+ soup_socket_start_ssl (conn->priv->socket);
+
+ proxy_socket_connected (NULL, msg->errorcode, conn);
+ g_object_unref (msg);
+}
+
+static void
+tunnel_failed (SoupMessage *msg, gpointer conn)
+{
+ g_signal_emit (conn, signals[CONNECT_RESULT], 0,
+ SOUP_ERROR_CANT_CONNECT);
+ g_object_unref (msg);
+}
+
+static void
+tunnel_socket_connected (SoupSocket *sock, SoupKnownErrorCode status,
+ gpointer user_data)
+{
+ SoupConnection *conn = user_data;
+ SoupMessage *connect_msg;
+
+ if (!SOUP_ERROR_IS_SUCCESSFUL (status)) {
+ socket_connected (sock, status, conn);
+ return;
+ }
+
+ connect_msg = soup_message_new_from_uri (SOUP_METHOD_CONNECT,
+ conn->priv->dest_uri);
+ g_signal_connect (connect_msg, "read_body",
+ G_CALLBACK (tunnel_connected), conn);
+ g_signal_connect (connect_msg, "write_error",
+ G_CALLBACK (tunnel_failed), conn);
+ g_signal_connect (connect_msg, "read_error",
+ G_CALLBACK (tunnel_failed), conn);
+
+ soup_connection_send_request (conn, connect_msg);
+}
+
+/**
+ * soup_connection_new_tunnel:
+ * @proxy_uri: proxy to connect to
+ * @dest_uri: remote machine to ask the proxy to connect to
+ * @callback: callback to call after connecting
+ * @user_data: data for @callback
+ *
+ * Creates a connection to @uri via @proxy_uri. @callback will be
+ * called when the connection completes (or fails).
+ *
+ * Return value: the new connection (not yet ready for use).
+ **/
+SoupConnection *
+soup_connection_new_tunnel (const SoupUri *proxy_uri, const SoupUri *dest_uri,
+ SoupConnectionCallback callback,
+ gpointer user_data)
+{
+ SoupConnection *conn;
+
+ conn = connection_new (proxy_uri, TRUE, tunnel_socket_connected,
+ callback, user_data);
+ conn->priv->dest_uri = soup_uri_copy (dest_uri);
return conn;
}
{
g_return_if_fail (SOUP_IS_CONNECTION (conn));
- if (conn->priv->death_tag) {
- g_source_remove (conn->priv->death_tag);
- conn->priv->death_tag = 0;
- }
-
- if (conn->priv->socket) {
- g_signal_handlers_disconnect_by_func (conn->priv->socket,
- socket_disconnected,
- conn);
- soup_socket_disconnect (conn->priv->socket);
- g_object_unref (conn->priv->socket);
- conn->priv->socket = NULL;
+ if (!conn->priv->socket)
+ return;
- g_signal_emit (conn, signals[DISCONNECTED], 0);
- }
+ g_signal_handlers_disconnect_by_func (conn->priv->socket,
+ socket_disconnected, conn);
+ g_object_unref (conn->priv->socket);
+ conn->priv->socket = NULL;
+ g_signal_emit (conn, signals[DISCONNECTED], 0);
}
gboolean
}
-static gboolean
-connection_died (GIOChannel *iochannel,
- GIOCondition condition,
- gpointer conn)
-{
- soup_connection_disconnect (conn);
- return FALSE;
-}
-
-/**
- * soup_connection_set_in_use:
- * @conn: a connection
- * @in_use: whether or not @conn is in_use
- *
- * Marks @conn as being either in use or not. If @in_use is %FALSE,
- * @conn's last-used time is updated.
- **/
-void
-soup_connection_set_in_use (SoupConnection *conn, gboolean in_use)
-{
- g_return_if_fail (SOUP_IS_CONNECTION (conn));
-
- if (!conn->priv->socket) {
- if (in_use)
- g_warning ("Trying to use disconnected socket");
- return;
- }
-
- if (!in_use)
- conn->priv->last_used = time (NULL);
-
- if (in_use == conn->priv->in_use)
- return;
-
- conn->priv->in_use = in_use;
- if (!conn->priv->in_use) {
- conn->priv->death_tag =
- g_io_add_watch (soup_socket_get_iochannel (conn->priv->socket),
- G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
- connection_died,
- conn);
- } else if (conn->priv->death_tag) {
- g_source_remove (conn->priv->death_tag);
- conn->priv->death_tag = 0;
- }
-}
-
/**
* soup_connection_is_in_use:
* @conn: a connection
{
g_return_val_if_fail (SOUP_IS_CONNECTION (conn), FALSE);
- return conn->priv->in_use;
+ return conn->priv->cur_req != NULL;
}
/**
* soup_connection_last_used:
* @conn: a #SoupConnection.
*
- * Return value: the last time soup_connection_mark_used() was called
- * on @conn, or 0 if @conn has not been used yet.
+ * Return value: the last time a response was received on @conn, or 0
+ * if @conn has not been used yet.
*/
time_t
soup_connection_last_used (SoupConnection *conn)
{
g_return_val_if_fail (SOUP_IS_CONNECTION (conn), FALSE);
- return conn->priv->new;
+ return conn->priv->last_used == 0;
+}
+
+
+static void
+request_done (SoupMessage *req, gpointer user_data)
+{
+ SoupConnection *conn = user_data;
+
+ g_object_remove_weak_pointer (G_OBJECT (conn->priv->cur_req),
+ (gpointer *)conn->priv->cur_req);
+ conn->priv->cur_req = NULL;
+ conn->priv->last_used = time (NULL);
+
+ g_signal_handlers_disconnect_by_func (req, request_done, conn);
+
+ if (!soup_message_is_keepalive (req))
+ soup_connection_disconnect (conn);
}
-/**
- * soup_connection_mark_old:
- * @conn: a #SoupConnection.
- *
- * Marks @conn as being no longer "new".
- * FIXME: some day, this should happen automatically.
- */
void
-soup_connection_mark_old (SoupConnection *conn)
+soup_connection_send_request (SoupConnection *conn, SoupMessage *req)
{
g_return_if_fail (SOUP_IS_CONNECTION (conn));
+ g_return_if_fail (SOUP_IS_MESSAGE (req));
+ g_return_if_fail (conn->priv->socket != NULL);
+ g_return_if_fail (conn->priv->cur_req == NULL);
+
+ conn->priv->cur_req = req;
+ g_object_add_weak_pointer (G_OBJECT (req),
+ (gpointer *)conn->priv->cur_req);
+
+ g_signal_connect (req, "finished", G_CALLBACK (request_done), conn);
- conn->priv->new = FALSE;
+ soup_message_send_request (req, conn->priv->socket,
+ conn->priv->is_proxy);
}
SoupConnection *soup_connection_new (const SoupUri *uri,
SoupConnectionCallback,
gpointer data);
+SoupConnection *soup_connection_new_proxy (const SoupUri *proxy_uri,
+ SoupConnectionCallback,
+ gpointer data);
+SoupConnection *soup_connection_new_tunnel (const SoupUri *proxy_uri,
+ const SoupUri *dest_uri,
+ SoupConnectionCallback,
+ gpointer data);
gboolean soup_connection_is_proxy (SoupConnection *conn);
SoupSocket *soup_connection_get_socket (SoupConnection *conn);
-void soup_connection_set_in_use (SoupConnection *conn,
- gboolean in_use);
+gboolean soup_connection_is_new (SoupConnection *conn);
gboolean soup_connection_is_in_use (SoupConnection *conn);
time_t soup_connection_last_used (SoupConnection *conn);
-gboolean soup_connection_is_new (SoupConnection *conn);
-void soup_connection_mark_old (SoupConnection *conn);
+void soup_connection_send_request (SoupConnection *conn,
+ SoupMessage *req);
#endif /* SOUP_CONNECTION_H */
#include "soup-auth.h"
#include "soup-auth-ntlm.h"
-#include "soup-connection.h"
#include "soup-context.h"
+#include "soup-connection.h"
#include "soup-message-private.h"
#include "soup-private.h"
#include "soup-misc.h"
if (!soup_connection_is_in_use (conn) &&
port == ctx->priv->uri->port) {
- /* Set connection to in use */
- soup_connection_set_in_use (conn, TRUE);
-
/* Issue success callback */
(*cb) (ctx, SOUP_ERROR_OK, conn, user_data);
return TRUE;
try_create_connection (struct SoupConnectData *data)
{
int conn_limit = soup_get_connection_limit ();
+ SoupContext *proxy = soup_get_proxy ();
+ SoupUri *uri = data->ctx->priv->uri;
/*
* Check if we are allowed to create a new connection, otherwise wait
connection_count++;
data->timeout_tag = 0;
- data->conn = soup_connection_new (data->ctx->priv->uri,
- soup_context_connect_cb, data);
+
+ if (proxy) {
+ if (uri->protocol == SOUP_PROTOCOL_HTTPS) {
+ data->conn = soup_connection_new_tunnel (
+ proxy->priv->uri, uri,
+ soup_context_connect_cb, data);
+ } else {
+ data->conn = soup_connection_new_proxy (
+ proxy->priv->uri,
+ soup_context_connect_cb, data);
+ }
+ } else
+ data->conn = soup_connection_new (uri, soup_context_connect_cb, data);
+
return TRUE;
}
} data;
} SoupHandlerData;
-static void redirect_handler (SoupMessage *msg, gpointer user_data);
-static void authorize_handler (SoupMessage *msg, gpointer proxy);
-
-static SoupHandlerData global_handlers [] = {
- /* Handle redirect response codes. */
- {
- SOUP_HANDLER_POST_BODY,
- redirect_handler,
- NULL,
- SOUP_HANDLER_ERROR_CLASS,
- { SOUP_ERROR_CLASS_REDIRECT }
- },
-
- /* Handle authorization. */
- {
- SOUP_HANDLER_POST_BODY,
- authorize_handler,
- GINT_TO_POINTER (FALSE),
- SOUP_HANDLER_ERROR_CODE,
- { 401 }
- },
-
- /* Handle proxy authorization. */
- {
- SOUP_HANDLER_POST_BODY,
- authorize_handler,
- GINT_TO_POINTER (TRUE),
- SOUP_HANDLER_ERROR_CODE,
- { 407 }
- },
-
- { 0 }
-};
-
static inline void
run_handler (SoupMessage *msg,
SoupHandlerPhase invoke_phase,
}
/*
- * Run each handler with matching criteria (first per-message then
- * global handlers). If a handler requeues a message, we stop
- * processing and terminate the current request.
+ * Run each handler with matching criteria. If a handler requeues a
+ * message, we stop processing and terminate the current request.
*
* After running all handlers, if there is an error set or the invoke
* phase was post_body, issue the final callback.
soup_message_run_handlers (SoupMessage *msg, SoupHandlerPhase invoke_phase)
{
GSList *list;
- SoupHandlerData *data;
g_return_if_fail (SOUP_IS_MESSAGE (msg));
if (SOUP_MESSAGE_IS_STARTING (msg))
return;
}
-
- for (data = global_handlers; data->phase; data++) {
- run_handler (msg, invoke_phase, data);
-
- if (SOUP_MESSAGE_IS_STARTING (msg))
- return;
- }
}
static void
iter = iter->next;
}
}
-
-
-/* FIXME: these don't belong here */
-
-static void
-authorize_handler (SoupMessage *msg, gpointer proxy)
-{
- SoupContext *ctx;
-
- ctx = proxy ? soup_get_proxy () : msg->priv->context;
- if (soup_context_update_auth (ctx, msg))
- soup_message_requeue (msg);
-}
-
-static void
-redirect_handler (SoupMessage *msg, gpointer user_data)
-{
- const char *new_loc;
- const SoupUri *old_uri;
- SoupUri *new_uri;
- SoupContext *new_ctx;
-
- if (msg->priv->msg_flags & SOUP_MESSAGE_NO_REDIRECT)
- return;
-
- old_uri = soup_message_get_uri (msg);
-
- new_loc = soup_message_get_header (msg->response_headers, "Location");
- if (!new_loc)
- return;
- new_uri = soup_uri_new (new_loc);
- if (!new_uri)
- goto INVALID_REDIRECT;
-
- /* Copy auth info from original URI. */
- if (old_uri->user && !new_uri->user)
- soup_uri_set_auth (new_uri,
- old_uri->user,
- old_uri->passwd,
- old_uri->authmech);
-
- new_ctx = soup_context_from_uri (new_uri);
-
- soup_uri_free (new_uri);
-
- if (!new_ctx)
- goto INVALID_REDIRECT;
-
- soup_message_set_context (msg, new_ctx);
- g_object_unref (new_ctx);
-
- soup_message_requeue (msg);
- return;
-
- INVALID_REDIRECT:
- soup_message_set_error_full (msg,
- SOUP_ERROR_MALFORMED,
- "Invalid Redirect URL");
-}
SoupContext *context;
SoupConnection *connection;
- SoupSocket *socket;
};
void soup_message_run_handlers (SoupMessage *msg,
void soup_message_set_connection (SoupMessage *msg,
SoupConnection *conn);
SoupConnection *soup_message_get_connection (SoupMessage *msg);
-SoupSocket *soup_message_get_socket (SoupMessage *msg);
typedef void (*SoupMessageGetHeadersFn) (SoupMessage *msg,
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-message-queue.c: Message queue
+ *
+ * Copyright (C) 2003, Ximian, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "soup-message-queue.h"
+
+struct SoupMessageQueue {
+ GList *head, *tail;
+ GList *iters;
+};
+
+/**
+ * soup_message_queue_new:
+ *
+ * Return value: a new #SoupMessageQueue object
+ **/
+SoupMessageQueue *
+soup_message_queue_new (void)
+{
+ return g_new0 (SoupMessageQueue, 1);
+}
+
+/**
+ * soup_message_queue_destroy:
+ * @queue: a message queue
+ *
+ * Frees memory associated with @queue, which must be empty.
+ **/
+void
+soup_message_queue_destroy (SoupMessageQueue *queue)
+{
+ g_return_if_fail (queue->head == NULL);
+
+ g_list_free (queue->head);
+ g_list_free (queue->iters);
+ g_free (queue);
+}
+
+/**
+ * soup_message_queue_append:
+ * @queue: a queue
+ * @msg: a message
+ *
+ * Appends @msg to the end of @queue
+ **/
+void
+soup_message_queue_append (SoupMessageQueue *queue, SoupMessage *msg)
+{
+ if (queue->head) {
+ queue->tail = g_list_append (queue->tail, msg);
+ queue->tail = queue->tail->next;
+ } else
+ queue->head = queue->tail = g_list_append (NULL, msg);
+
+ g_object_add_weak_pointer (G_OBJECT (msg), &queue->tail->data);
+}
+
+/**
+ * soup_message_queue_first:
+ * @queue: a queue
+ * @iter: pointer to a #SoupMessageQueueIter
+ *
+ * Initializes @iter and returns the first element of @queue. If you
+ * do not iterate all the way to the end of the list, you must call
+ * soup_message_queue_free_iter() to dispose the iterator when you are
+ * done.
+ *
+ * Return value: the first element of @queue, or %NULL if it is empty.
+ **/
+SoupMessage *
+soup_message_queue_first (SoupMessageQueue *queue, SoupMessageQueueIter *iter)
+{
+ if (!queue->head)
+ return NULL;
+
+ queue->iters = g_list_prepend (queue->iters, iter);
+
+ iter->cur = NULL;
+ iter->next = queue->head;
+ return soup_message_queue_next (queue, iter);
+}
+
+/**
+ * soup_message_queue_next:
+ * @queue: a queue
+ * @iter: pointer to an initialized #SoupMessageQueueIter
+ *
+ * Return value: the next element of @queue, or %NULL if there are no more.
+ **/
+SoupMessage *
+soup_message_queue_next (SoupMessageQueue *queue, SoupMessageQueueIter *iter)
+{
+ while (iter->next) {
+ iter->cur = iter->next;
+ iter->next = iter->cur->next;
+ if (iter->cur->data)
+ return iter->cur->data;
+
+ /* Message was finalized, remove dead queue element */
+ soup_message_queue_remove (queue, iter);
+ }
+
+ /* Nothing left */
+ iter->cur = NULL;
+ soup_message_queue_free_iter (queue, iter);
+ return NULL;
+}
+
+/**
+ * soup_message_queue_remove:
+ * @queue: a queue
+ * @iter: pointer to an initialized #SoupMessageQueueIter
+ *
+ * Removes the queue element pointed to by @iter; that is, the last
+ * message returned by soup_message_queue_first() or
+ * soup_message_queue_next().
+ *
+ * Return value: the removed message, or %NULL if the element pointed
+ * to by @iter was already removed.
+ **/
+SoupMessage *
+soup_message_queue_remove (SoupMessageQueue *queue, SoupMessageQueueIter *iter)
+{
+ GList *i;
+ SoupMessageQueueIter *iter2;
+ SoupMessage *msg;
+
+ if (!iter->cur) {
+ /* We're at end of list or this item was already removed */
+ return NULL;
+ }
+
+ /* Fix any other iters pointing to iter->cur */
+ for (i = queue->iters; i; i = i->next) {
+ iter2 = i->data;
+ if (iter2 != iter) {
+ if (iter2->cur == iter->cur)
+ iter2->cur = NULL;
+ else if (iter2->next == iter->cur)
+ iter2->next = iter->cur->next;
+ }
+ }
+
+ msg = iter->cur->data;
+ if (msg)
+ g_object_remove_weak_pointer (G_OBJECT (msg), &iter->cur->data);
+
+ /* If deleting the last item, fix tail */
+ if (queue->tail == iter->cur)
+ queue->tail = queue->tail->prev;
+
+ /* Remove the item */
+ queue->head = g_list_delete_link (queue->head, iter->cur);
+ iter->cur = NULL;
+
+ return msg;
+}
+
+void
+soup_message_queue_remove_message (SoupMessageQueue *queue, SoupMessage *msg)
+{
+ SoupMessageQueueIter iter;
+ SoupMessage *msg2;
+
+ for (msg2 = soup_message_queue_first (queue, &iter); msg2; msg2 = soup_message_queue_next (queue, &iter)) {
+ if (msg2 == msg) {
+ soup_message_queue_remove (queue, &iter);
+ soup_message_queue_free_iter (queue, &iter);
+ return;
+ }
+ }
+}
+
+
+/**
+ * soup_message_queue_free_iter:
+ * @queue: a queue
+ * @iter: pointer to an initialized #SoupMessageQueueIter
+ *
+ * Removes @iter from the list of active iterators in @queue.
+ **/
+void
+soup_message_queue_free_iter (SoupMessageQueue *queue,
+ SoupMessageQueueIter *iter)
+{
+ queue->iters = g_list_remove (queue->iters, iter);
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2003, Ximian, Inc.
+ */
+
+#ifndef SOUP_MESSAGE_QUEUE_H
+#define SOUP_MESSAGE_QUEUE_H 1
+
+#include <glib.h>
+#include <libsoup/soup-message.h>
+
+typedef struct SoupMessageQueue SoupMessageQueue;
+
+typedef struct {
+ GList *cur, *next;
+} SoupMessageQueueIter;
+
+SoupMessageQueue *soup_message_queue_new (void);
+void soup_message_queue_append (SoupMessageQueue *queue,
+ SoupMessage *msg);
+
+SoupMessage *soup_message_queue_first (SoupMessageQueue *queue,
+ SoupMessageQueueIter *iter);
+SoupMessage *soup_message_queue_next (SoupMessageQueue *queue,
+ SoupMessageQueueIter *iter);
+SoupMessage *soup_message_queue_remove (SoupMessageQueue *queue,
+ SoupMessageQueueIter *iter);
+
+void soup_message_queue_free_iter (SoupMessageQueue *queue,
+ SoupMessageQueueIter *iter);
+
+void soup_message_queue_destroy (SoupMessageQueue *queue);
+
+void soup_message_queue_remove_message (SoupMessageQueue *queue,
+ SoupMessage *msg);
+
+#endif /* SOUP_MESSAGE_QUEUE_H */
#include "soup-misc.h"
#include "soup-context.h"
#include "soup-private.h"
-#include "soup-queue.h"
#define PARENT_TYPE G_TYPE_OBJECT
static GObjectClass *parent_class;
signals[FINISHED] =
g_signal_new ("finished",
G_OBJECT_CLASS_TYPE (object_class),
- G_SIGNAL_RUN_LAST,
+ G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (SoupMessageClass, finished),
NULL, NULL,
soup_marshal_NONE__NONE,
finished (SoupMessage *req)
{
cleanup_message (req);
-
- if (req->priv->callback) {
- (*req->priv->callback) (req, req->priv->user_data);
-
- if (!SOUP_MESSAGE_IS_STARTING (req))
- g_object_unref (req);
- }
}
void
}
soup_message_set_connection (req, NULL);
-
- soup_queue_remove_request (req);
}
/**
g_hash_table_foreach (hash, foreach_value_in_list, &data);
}
-static void
-queue_message (SoupMessage *req)
-{
- if (!req->priv->context) {
- soup_message_set_error_full (req,
- SOUP_ERROR_CANCELLED,
- "Attempted to queue a message "
- "with no destination context");
- soup_message_finished (req);
- return;
- }
-
- if (req->priv->status != SOUP_MESSAGE_STATUS_IDLE)
+/**
+ * soup_message_prepare:
+ * @req: a message
+ *
+ * Prepares @req to be sent, by cleaning up its prior response state
+ **/
+void
+soup_message_prepare (SoupMessage *req)
+{
+ if (req->priv->status != SOUP_MESSAGE_STATUS_IDLE) {
cleanup_message (req);
+ req->priv->status = SOUP_MESSAGE_STATUS_IDLE;
+ }
- switch (req->response.owner) {
- case SOUP_BUFFER_USER_OWNED:
- soup_message_set_error_full (req,
- SOUP_ERROR_CANCELLED,
- "Attempted to queue a message "
- "with a user owned response "
- "buffer.");
- soup_message_finished (req);
- return;
- case SOUP_BUFFER_SYSTEM_OWNED:
+ if (req->response.owner == SOUP_BUFFER_SYSTEM_OWNED)
g_free (req->response.body);
- break;
- case SOUP_BUFFER_STATIC:
- break;
- }
req->response.owner = 0;
req->response.body = NULL;
g_free ((char *) req->errorphrase);
req->errorphrase = NULL;
}
-
- soup_queue_message (req);
-}
-
-/**
- * soup_message_queue:
- * @req: a #SoupMessage.
- * @callback: a #SoupCallbackFn which will be called after the message
- * completes or when an unrecoverable error occurs.
- * @user_data: a pointer passed to @callback.
- *
- * Queues the message @req for sending. All messages are processed
- * while the glib main loop runs. If this #SoupMessage has been
- * processed before, any resources related to the time it was last
- * sent are freed.
- *
- * If the response #SoupDataBuffer has an owner of
- * %SOUP_BUFFER_USER_OWNED, the message will not be queued, and
- * @callback will be called with a #SoupErrorCode of
- * %SOUP_ERROR_CANCELLED.
- *
- * Upon message completetion, the callback specified in @callback will
- * be invoked. If after returning from this callback the message has
- * not been requeued using soup_message_queue(), @req will be unreffed.
- */
-void
-soup_message_queue (SoupMessage *req,
- SoupCallbackFn callback,
- gpointer user_data)
-{
- g_return_if_fail (SOUP_IS_MESSAGE (req));
-
- req->priv->callback = callback;
- req->priv->user_data = user_data;
-
- queue_message (req);
-}
-
-/**
- * soup_message_requeue:
- * @req: a #SoupMessage
- *
- * This causes @req to be placed back on the queue to be attempted
- * again.
- **/
-void
-soup_message_requeue (SoupMessage *req)
-{
- g_return_if_fail (SOUP_IS_MESSAGE (req));
-
- if (req->priv->io_data)
- soup_message_io_cancel (req);
- queue_message (req);
-}
-
-/**
- * soup_message_send:
- * @msg: a #SoupMessage.
- *
- * Synchronously send @msg. This call will not return until the
- * transfer is finished successfully or there is an unrecoverable
- * error.
- *
- * @msg is not freed upon return.
- *
- * Return value: the #SoupErrorClass of the error encountered while
- * sending or reading the response.
- */
-SoupErrorClass
-soup_message_send (SoupMessage *msg)
-{
- soup_message_queue (msg, NULL, NULL);
-
- while (1) {
- g_main_iteration (TRUE);
-
- if (msg->priv->status == SOUP_MESSAGE_STATUS_FINISHED ||
- SOUP_ERROR_IS_TRANSPORT (msg->errorcode))
- break;
-
- /* Quit if soup_shutdown has been called */
- if (!soup_initialized)
- return SOUP_ERROR_CANCELLED;
- }
-
- return msg->errorclass;
}
void
void
soup_message_set_connection (SoupMessage *msg, SoupConnection *conn)
{
- if (conn) {
- soup_connection_set_in_use (conn, TRUE);
+ if (conn)
g_object_ref (conn);
- }
- if (msg->priv->connection) {
- soup_connection_set_in_use (msg->priv->connection, FALSE);
+ if (msg->priv->connection)
g_object_unref (msg->priv->connection);
- }
msg->priv->connection = conn;
-
- if (conn) {
- msg->priv->socket = soup_connection_get_socket (conn);
- g_object_ref (msg->priv->socket);
- } else if (msg->priv->socket) {
- g_object_unref (msg->priv->socket);
- msg->priv->socket = NULL;
- }
}
SoupConnection *
return msg->priv->connection;
}
-SoupSocket *
-soup_message_get_socket (SoupMessage *msg)
-{
- g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
-
- return msg->priv->socket;
-}
-
void
soup_message_set_error (SoupMessage *msg, SoupKnownErrorCode errcode)
{
void soup_message_disconnect (SoupMessage *req);
-SoupErrorClass soup_message_send (SoupMessage *msg);
-
-void soup_message_queue (SoupMessage *req,
- SoupCallbackFn callback,
- gpointer user_data);
-
-void soup_message_requeue (SoupMessage *req);
+void soup_message_prepare (SoupMessage *req);
void soup_message_add_header (GHashTable *hash,
const char *name,
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* soup-misc.c: Miscellaneous settings and configuration file handling.
- *
- * Authors:
- * Alex Graveley (alex@ximian.com)
- *
- * Copyright (C) 2000-2002, Ximian, Inc.
+
+ * Copyright (C) 2000-2003, Ximian, Inc.
*/
#include <ctype.h>
#include "soup-misc.h"
#include "soup-private.h"
-#include "soup-queue.h"
gboolean soup_initialized = FALSE;
soup_initialized = TRUE;
}
-/**
- * soup_shutdown:
- *
- * Shut down the Soup engine.
- *
- * The pending message queue is flushed by calling %soup_message_cancel on all
- * active requests.
- */
-void
-soup_shutdown ()
-{
- soup_queue_shutdown ();
-}
-
static char *ssl_ca_file = NULL;
static char *ssl_ca_dir = NULL;
static char *ssl_cert_file = NULL;
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
- * soup-misc.h: Miscellaneous settings and configuration file handling.
- *
- * Authors:
- * Alex Graveley (alex@ximian.com)
- *
- * Copyright (C) 2000-2002, Ximian, Inc.
+ * Copyright (C) 2000-2003, Ximian, Inc.
*/
#ifndef SOUP_MISC_H
void soup_load_config (gchar *config_file);
-void soup_shutdown (void);
-
void soup_set_proxy (SoupContext *context);
SoupContext *soup_get_proxy (void);
+++ /dev/null
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * soup-queue.c: Asyncronous Callback-based HTTP Request Queue.
- *
- * Authors:
- * Alex Graveley (alex@ximian.com)
- *
- * Copyright (C) 2000-2002, Ximian, Inc.
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <ctype.h>
-#include <glib.h>
-#include <string.h>
-#include <stdlib.h>
-#include <signal.h>
-#include <errno.h>
-
-#include "soup-queue.h"
-#include "soup-auth.h"
-#include "soup-connection.h"
-#include "soup-message.h"
-#include "soup-message-private.h"
-#include "soup-context.h"
-#include "soup-headers.h"
-#include "soup-misc.h"
-#include "soup-private.h"
-#include "soup-socket.h"
-
-static GSList *soup_active_requests = NULL, *soup_active_request_next = NULL;
-
-static guint soup_queue_idle_tag = 0;
-
-static void
-soup_debug_print_a_header (gchar *key, gchar *val, gpointer not_used)
-{
- g_print ("\tKEY: \"%s\", VALUE: \"%s\"\n", key, val);
-}
-
-void
-soup_debug_print_headers (SoupMessage *req)
-{
- g_print ("Request Headers:\n");
- soup_message_foreach_header (req->request_headers,
- (GHFunc) soup_debug_print_a_header,
- NULL);
-
- g_print ("Response Headers:\n");
- soup_message_foreach_header (req->response_headers,
- (GHFunc) soup_debug_print_a_header,
- NULL);
-}
-
-static void
-soup_queue_error (SoupMessage *req)
-{
- SoupConnection *conn = soup_message_get_connection (req);
- const SoupUri *uri;
- gboolean conn_is_new;
-
- conn_is_new = soup_connection_is_new (conn);
- soup_message_disconnect (req);
-
- uri = soup_message_get_uri (req);
-
- if (uri->protocol == SOUP_PROTOCOL_HTTPS) {
- /* FIXME: what does this really do? */
-
- /*
- * This can happen if the SSL handshake fails
- * for some reason (untrustable signatures,
- * etc.)
- */
- if (req->priv->retries >= 3)
- soup_message_set_error (req, SOUP_ERROR_SSL_FAILED);
- else {
- req->priv->retries++;
- soup_message_requeue (req);
- return;
- }
- } else if (conn_is_new) {
- soup_message_set_error (req, SOUP_ERROR_CANT_CONNECT);
- } else {
- /* Must have timed out. Try a new connection */
- soup_message_requeue (req);
- return;
- }
-
- req->priv->status = SOUP_MESSAGE_STATUS_FINISHED;
-}
-
-static void
-soup_queue_request_finished (SoupMessage *req, gpointer user_data)
-{
- SoupConnection *conn = soup_message_get_connection (req);
-
- if (SOUP_ERROR_IS_TRANSPORT (req->errorcode)) {
- soup_queue_error (req);
- return;
- }
-
- if (soup_message_is_keepalive (req) && conn)
- soup_connection_mark_old (conn);
- else
- soup_message_disconnect (req);
-
- req->priv->status = SOUP_MESSAGE_STATUS_FINISHED;
-}
-
-static void
-start_request (SoupContext *ctx, SoupMessage *req)
-{
- req->priv->status = SOUP_MESSAGE_STATUS_RUNNING;
- soup_signal_connect_once (req, "finished",
- G_CALLBACK (soup_queue_request_finished),
- NULL);
- soup_message_send_request (req, soup_message_get_socket (req),
- soup_get_proxy () != NULL);
-}
-
-static void
-proxy_https_connect_cb (SoupMessage *msg, gpointer user_data)
-{
- gboolean *ret = user_data;
-
- if (!SOUP_MESSAGE_IS_ERROR (msg)) {
- soup_socket_start_ssl (soup_message_get_socket (msg));
- *ret = TRUE;
- }
-}
-
-static gboolean
-proxy_https_connect (SoupContext *proxy,
- SoupConnection *conn,
- SoupContext *dest_ctx)
-{
- SoupProtocol proxy_proto;
- SoupMessage *connect_msg;
- gboolean ret = FALSE;
-
- proxy_proto = soup_context_get_uri (proxy)->protocol;
-
- if (proxy_proto != SOUP_PROTOCOL_HTTP &&
- proxy_proto != SOUP_PROTOCOL_HTTPS)
- return FALSE;
-
- connect_msg = soup_message_new_from_uri (SOUP_METHOD_CONNECT,
- soup_context_get_uri (dest_ctx));
- soup_message_set_connection (connect_msg, conn);
- soup_message_add_handler (connect_msg,
- SOUP_HANDLER_POST_BODY,
- proxy_https_connect_cb,
- &ret);
- soup_message_send (connect_msg);
- g_object_unref (connect_msg);
-
- return ret;
-}
-
-static gboolean
-proxy_connect (SoupContext *ctx, SoupMessage *req, SoupConnection *conn)
-{
- SoupProtocol proto, dest_proto;
-
- /*
- * Only attempt proxy connect if the connection's context is different
- * from the requested context, and if the connection is new
- */
- if (ctx == req->priv->context || !soup_connection_is_new (conn))
- return FALSE;
-
- proto = soup_context_get_uri (ctx)->protocol;
- dest_proto = soup_context_get_uri (req->priv->context)->protocol;
-
- /* Handle HTTPS tunnel setup via proxy CONNECT request. */
- if (dest_proto == SOUP_PROTOCOL_HTTPS) {
- /* Syncronously send CONNECT request */
- if (!proxy_https_connect (ctx, conn, req->priv->context)) {
- soup_message_set_error_full (
- req,
- SOUP_ERROR_CANT_CONNECT_PROXY,
- "Unable to create secure data "
- "tunnel through proxy");
- soup_message_finished (req);
- return TRUE;
- }
- }
-
- return FALSE;
-}
-
-void
-soup_queue_connect_cb (SoupContext *ctx,
- SoupKnownErrorCode err,
- SoupConnection *conn,
- gpointer user_data)
-{
- SoupMessage *req = user_data;
-
- req->priv->connect_tag = NULL;
- soup_message_set_connection (req, conn);
-
- switch (err) {
- case SOUP_ERROR_OK:
- /*
- * NOTE: proxy_connect will either set an error or call us
- * again after proxy negotiation.
- */
- if (proxy_connect (ctx, req, conn))
- return;
-
- start_request (ctx, req);
- break;
-
- case SOUP_ERROR_CANT_RESOLVE:
- if (ctx == req->priv->context)
- soup_message_set_error (req, SOUP_ERROR_CANT_RESOLVE);
- else
- soup_message_set_error (req, SOUP_ERROR_CANT_RESOLVE_PROXY);
- soup_message_finished (req);
- break;
-
- default:
- if (ctx == req->priv->context)
- soup_message_set_error (req, SOUP_ERROR_CANT_CONNECT);
- else
- soup_message_set_error (req, SOUP_ERROR_CANT_CONNECT_PROXY);
- soup_message_finished (req);
- break;
- }
-
- return;
-}
-
-void
-soup_queue_add_request (SoupMessage *req)
-{
- soup_active_requests = g_slist_prepend (soup_active_requests, req);
-}
-
-void
-soup_queue_remove_request (SoupMessage *req)
-{
- if (soup_active_request_next && soup_active_request_next->data == req)
- soup_queue_next_request ();
- soup_active_requests = g_slist_remove (soup_active_requests, req);
-}
-
-SoupMessage *
-soup_queue_first_request (void)
-{
- if (!soup_active_requests)
- return NULL;
-
- soup_active_request_next = soup_active_requests->next;
- return soup_active_requests->data;
-}
-
-SoupMessage *
-soup_queue_next_request (void)
-{
- SoupMessage *ret;
-
- if (!soup_active_request_next)
- return NULL;
- ret = soup_active_request_next->data;
- soup_active_request_next = soup_active_request_next->next;
- return ret;
-}
-
-static gboolean
-request_in_progress (SoupMessage *req)
-{
- if (!soup_active_requests)
- return FALSE;
-
- return g_slist_index (soup_active_requests, req) != -1;
-}
-
-static gboolean
-soup_idle_handle_new_requests (gpointer unused)
-{
- SoupMessage *req = soup_queue_first_request ();
- SoupConnection *conn;
-
- for (; req; req = soup_queue_next_request ()) {
- SoupContext *ctx, *proxy;
-
- if (req->priv->status != SOUP_MESSAGE_STATUS_QUEUED)
- continue;
-
- proxy = soup_get_proxy ();
- ctx = proxy ? proxy : req->priv->context;
-
- req->priv->status = SOUP_MESSAGE_STATUS_CONNECTING;
-
- conn = soup_message_get_connection (req);
- if (conn && soup_connection_is_connected (conn))
- start_request (ctx, req);
- else {
- gpointer connect_tag;
-
- connect_tag =
- soup_context_get_connection (
- ctx,
- soup_queue_connect_cb,
- req);
-
- if (connect_tag && request_in_progress (req))
- req->priv->connect_tag = connect_tag;
- }
- }
-
- soup_queue_idle_tag = 0;
- return FALSE;
-}
-
-static void
-soup_queue_initialize (void)
-{
- if (!soup_initialized)
- soup_load_config (NULL);
-
- if (!soup_queue_idle_tag)
- soup_queue_idle_tag =
- g_idle_add (soup_idle_handle_new_requests, NULL);
-}
-
-void
-soup_queue_message (SoupMessage *req)
-{
- g_return_if_fail (SOUP_IS_MESSAGE (req));
-
- req->priv->status = SOUP_MESSAGE_STATUS_QUEUED;
- soup_queue_add_request (req);
- soup_queue_initialize ();
-}
-
-/**
- * soup_queue_shutdown:
- *
- * Shut down the message queue by calling soup_message_cancel() on all
- * active requests and then closing all open connections.
- */
-void
-soup_queue_shutdown (void)
-{
- SoupMessage *req;
-
- soup_initialized = FALSE;
-
- if (soup_queue_idle_tag) {
- g_source_remove (soup_queue_idle_tag);
- soup_queue_idle_tag = 0;
- }
-
- req = soup_queue_first_request ();
- for (; req; req = soup_queue_next_request ())
- soup_message_cancel (req);
-
- soup_connection_purge_idle ();
-}
+++ /dev/null
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * soup-queue.h: Asyncronous Callback-based HTTP Request Queue.
- *
- * Authors:
- * Alex Graveley (alex@ximian.com)
- *
- * Copyright (C) 2001-2002, Ximian, Inc.
- */
-
-#ifndef SOUP_QUEUE_H
-#define SOUP_QUEUE_H 1
-
-#include <glib.h>
-
-#include <libsoup/soup-message.h>
-#include <libsoup/soup-context.h>
-
-void soup_queue_message (SoupMessage *req);
-
-void soup_queue_connect_cb (SoupContext *ctx,
- SoupKnownErrorCode err,
- SoupConnection *conn,
- gpointer user_data);
-
-void soup_queue_add_request (SoupMessage *req);
-void soup_queue_remove_request (SoupMessage *req);
-SoupMessage *soup_queue_first_request (void);
-SoupMessage *soup_queue_next_request (void);
-
-void soup_queue_shutdown (void);
-
-#endif /* SOUP_QUEUE_H */
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-session.c
+ *
+ * Copyright (C) 2000-2003, Ximian, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "soup-session.h"
+#include "soup-connection.h"
+#include "soup-message-private.h"
+#include "soup-message-queue.h"
+#include "soup-private.h"
+
+struct SoupSessionPrivate {
+ SoupMessageQueue *queue;
+ guint queue_idle_tag;
+
+};
+
+#define PARENT_TYPE G_TYPE_OBJECT
+static GObjectClass *parent_class;
+
+static void
+init (GObject *object)
+{
+ SoupSession *session = SOUP_SESSION (object);
+
+ session->priv = g_new0 (SoupSessionPrivate, 1);
+ session->priv->queue = soup_message_queue_new ();
+}
+
+static void
+finalize (GObject *object)
+{
+ SoupSession *session = SOUP_SESSION (object);
+ SoupMessageQueueIter iter;
+ SoupMessage *msg;
+
+ if (session->priv->queue_idle_tag)
+ g_source_remove (session->priv->queue_idle_tag);
+
+ for (msg = soup_message_queue_first (session->priv->queue, &iter); msg;
+ msg = soup_message_queue_next (session->priv->queue, &iter)) {
+ soup_message_queue_remove (session->priv->queue, &iter);
+ soup_message_cancel (msg);
+ }
+ soup_message_queue_destroy (session->priv->queue);
+
+ g_free (session->priv);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+class_init (GObjectClass *object_class)
+{
+ parent_class = g_type_class_ref (PARENT_TYPE);
+
+ /* virtual method override */
+ object_class->finalize = finalize;
+}
+
+SOUP_MAKE_TYPE (soup_session, SoupSession, class_init, init, PARENT_TYPE)
+
+
+SoupSession *
+soup_session_new (void)
+{
+ return g_object_new (SOUP_TYPE_SESSION, NULL);
+}
+
+
+/* Default handlers */
+
+static void
+authorize_handler (SoupMessage *msg, gpointer user_data)
+{
+ SoupSession *session = user_data;
+ SoupContext *ctx;
+
+ if (msg->errorcode == SOUP_ERROR_PROXY_UNAUTHORIZED)
+ ctx = soup_get_proxy ();
+ else
+ ctx = msg->priv->context;
+
+ if (soup_context_update_auth (ctx, msg))
+ soup_session_requeue_message (session, msg);
+}
+
+static void
+redirect_handler (SoupMessage *msg, gpointer user_data)
+{
+ SoupSession *session = user_data;
+ const char *new_loc;
+ const SoupUri *old_uri;
+ SoupUri *new_uri;
+ SoupContext *new_ctx;
+
+ new_loc = soup_message_get_header (msg->response_headers, "Location");
+ if (!new_loc)
+ return;
+ new_uri = soup_uri_new (new_loc);
+ if (!new_uri)
+ goto INVALID_REDIRECT;
+
+ old_uri = soup_message_get_uri (msg);
+
+ /* Copy auth info from original URI. */
+ if (old_uri->user && !new_uri->user)
+ soup_uri_set_auth (new_uri,
+ old_uri->user,
+ old_uri->passwd,
+ old_uri->authmech);
+
+ new_ctx = soup_context_from_uri (new_uri);
+ soup_uri_free (new_uri);
+ if (!new_ctx)
+ goto INVALID_REDIRECT;
+
+ soup_message_set_context (msg, new_ctx);
+ g_object_unref (new_ctx);
+
+ soup_session_requeue_message (session, msg);
+ return;
+
+ INVALID_REDIRECT:
+ soup_message_set_error_full (msg,
+ SOUP_ERROR_MALFORMED,
+ "Invalid Redirect URL");
+}
+
+static void
+request_finished (SoupMessage *req, gpointer user_data)
+{
+ SoupSession *session = user_data;
+
+ soup_message_queue_remove_message (session->priv->queue, req);
+ req->priv->status = SOUP_MESSAGE_STATUS_FINISHED;
+}
+
+static void
+final_finished (SoupMessage *req, gpointer session)
+{
+ if (!SOUP_MESSAGE_IS_STARTING (req)) {
+ g_signal_handlers_disconnect_by_func (req, request_finished, session);
+ g_signal_handlers_disconnect_by_func (req, final_finished, session);
+ g_object_unref (req);
+ }
+}
+
+static void
+start_request (SoupConnection *conn, SoupMessage *req)
+{
+ req->priv->status = SOUP_MESSAGE_STATUS_RUNNING;
+ soup_connection_send_request (conn, req);
+}
+
+static void
+got_connection (SoupContext *ctx, SoupKnownErrorCode err,
+ SoupConnection *conn, gpointer user_data)
+{
+ SoupMessage *req = user_data;
+
+ req->priv->connect_tag = NULL;
+ soup_message_set_connection (req, conn);
+
+ switch (err) {
+ case SOUP_ERROR_OK:
+ start_request (conn, req);
+ break;
+
+ default:
+ soup_message_set_error (req, err);
+ soup_message_finished (req);
+ break;
+ }
+
+ return;
+}
+
+static gboolean
+idle_run_queue (gpointer user_data)
+{
+ SoupSession *session = user_data;
+ SoupMessageQueueIter iter;
+ SoupMessage *req;
+ SoupConnection *conn;
+
+ session->priv->queue_idle_tag = 0;
+
+ for (req = soup_message_queue_first (session->priv->queue, &iter); req;
+ req = soup_message_queue_next (session->priv->queue, &iter)) {
+
+ if (req->priv->status != SOUP_MESSAGE_STATUS_QUEUED)
+ continue;
+
+ conn = soup_message_get_connection (req);
+ if (conn && soup_connection_is_connected (conn)) {
+ start_request (conn, req);
+ } else {
+ gpointer connect_tag;
+
+ req->priv->status = SOUP_MESSAGE_STATUS_CONNECTING;
+ connect_tag =
+ soup_context_get_connection (
+ req->priv->context,
+ got_connection, req);
+
+ if (connect_tag)
+ req->priv->connect_tag = connect_tag;
+ }
+ }
+
+ return FALSE;
+}
+
+static void
+queue_message (SoupSession *session, SoupMessage *req, gboolean requeue)
+{
+ soup_message_prepare (req);
+
+ req->priv->status = SOUP_MESSAGE_STATUS_QUEUED;
+ if (!requeue)
+ soup_message_queue_append (session->priv->queue, req);
+
+ if (!session->priv->queue_idle_tag) {
+ session->priv->queue_idle_tag =
+ g_idle_add (idle_run_queue, session);
+ }
+}
+
+/**
+ * soup_session_queue_message:
+ * @session: a #SoupSession
+ * @req: the message to queue
+ * @callback: a #SoupCallbackFn which will be called after the message
+ * completes or when an unrecoverable error occurs.
+ * @user_data: a pointer passed to @callback.
+ *
+ * Queues the message @req for sending. All messages are processed
+ * while the glib main loop runs. If @req has been processed before,
+ * any resources related to the time it was last sent are freed.
+ *
+ * Upon message completion, the callback specified in @callback will
+ * be invoked. If after returning from this callback the message has
+ * not been requeued, @req will be unreffed.
+ */
+void
+soup_session_queue_message (SoupSession *session, SoupMessage *req,
+ SoupCallbackFn callback, gpointer user_data)
+{
+ g_return_if_fail (SOUP_IS_SESSION (session));
+ g_return_if_fail (SOUP_IS_MESSAGE (req));
+
+ g_signal_connect (req, "finished",
+ G_CALLBACK (request_finished), session);
+ if (callback) {
+ g_signal_connect (req, "finished",
+ G_CALLBACK (callback), user_data);
+ }
+ g_signal_connect_after (req, "finished",
+ G_CALLBACK (final_finished), session);
+
+ soup_message_add_error_code_handler (req, SOUP_ERROR_UNAUTHORIZED,
+ SOUP_HANDLER_POST_BODY,
+ authorize_handler, session);
+ soup_message_add_error_code_handler (req,
+ SOUP_ERROR_PROXY_UNAUTHORIZED,
+ SOUP_HANDLER_POST_BODY,
+ authorize_handler, session);
+
+ if (!(req->priv->msg_flags & SOUP_MESSAGE_NO_REDIRECT)) {
+ soup_message_add_error_class_handler (
+ req, SOUP_ERROR_CLASS_REDIRECT, SOUP_HANDLER_POST_BODY,
+ redirect_handler, session);
+ }
+
+ queue_message (session, req, FALSE);
+}
+
+/**
+ * soup_session_requeue_message:
+ * @session: a #SoupSession
+ * @req: the message to requeue
+ *
+ * This causes @req to be placed back on the queue to be attempted
+ * again.
+ **/
+void
+soup_session_requeue_message (SoupSession *session, SoupMessage *req)
+{
+ g_return_if_fail (SOUP_IS_SESSION (session));
+ g_return_if_fail (SOUP_IS_MESSAGE (req));
+
+ queue_message (session, req, TRUE);
+}
+
+
+/**
+ * soup_session_send_message:
+ * @session: a #SoupSession
+ * @req: the message to send
+ *
+ * Synchronously send @req. This call will not return until the
+ * transfer is finished successfully or there is an unrecoverable
+ * error.
+ *
+ * @req is not freed upon return.
+ *
+ * Return value: the #SoupErrorClass of the error encountered while
+ * sending or reading the response.
+ */
+SoupErrorClass
+soup_session_send_message (SoupSession *session, SoupMessage *req)
+{
+ g_return_val_if_fail (SOUP_IS_SESSION (session), SOUP_ERROR_CLASS_TRANSPORT);
+ g_return_val_if_fail (SOUP_IS_MESSAGE (req), SOUP_ERROR_CLASS_TRANSPORT);
+
+ /* Balance out the unref that final_finished will do */
+ g_object_ref (req);
+
+ soup_session_queue_message (session, req, NULL, NULL);
+
+ while (1) {
+ g_main_iteration (TRUE);
+
+ if (req->priv->status == SOUP_MESSAGE_STATUS_FINISHED ||
+ SOUP_ERROR_IS_TRANSPORT (req->errorcode))
+ break;
+ }
+
+ return req->errorclass;
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2000-2003, Ximian, Inc.
+ */
+
+#ifndef SOUP_SESSION_H
+#define SOUP_SESSION_H 1
+
+#include <libsoup/soup-message.h>
+
+#define SOUP_TYPE_SESSION (soup_session_get_type ())
+#define SOUP_SESSION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_SESSION, SoupSession))
+#define SOUP_SESSION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_SESSION, SoupSessionClass))
+#define SOUP_IS_SESSION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_SESSION))
+#define SOUP_IS_SESSION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SOUP_TYPE_SESSION))
+#define SOUP_SESSION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_SESSION, SoupSessionClass))
+
+typedef struct SoupSessionPrivate SoupSessionPrivate;
+
+typedef struct {
+ GObject parent;
+
+ SoupSessionPrivate *priv;
+} SoupSession;
+
+typedef struct {
+ GObjectClass parent_class;
+
+} SoupSessionClass;
+
+GType soup_session_get_type (void);
+
+SoupSession *soup_session_new (void);
+
+void soup_session_queue_message (SoupSession *session,
+ SoupMessage *req,
+ SoupMessageCallbackFn callback,
+ gpointer user_data);
+void soup_session_requeue_message (SoupSession *session,
+ SoupMessage *req);
+
+SoupErrorClass soup_session_send_message (SoupSession *session,
+ SoupMessage *req);
+
+
+#endif /* SOUP_SESSION_H */
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
- * soup.h: Asyncronous Callback-based HTTP Request Queue.
- *
- * Authors:
- * Alex Graveley (alex@ximian.com)
- *
- * Copyright (C) 2000-2002, Ximian, Inc.
+ * Copyright (C) 2000-2003, Ximian, Inc.
*/
#ifndef SOUP_H
#include <libsoup/soup-context.h>
#include <libsoup/soup-message.h>
#include <libsoup/soup-misc.h>
+#include <libsoup/soup-session.h>
#include <libsoup/soup-socket.h>
#include <libsoup/soup-uri.h>
}
#endif
-#endif /*SOUP_H*/
+#endif /* SOUP_H */
#include "libsoup/soup-auth.h"
#include "libsoup/soup-private.h"
#include "libsoup/soup-message-private.h"
+#include "libsoup/soup-session.h"
int errors = 0;
int
main (int argc, char **argv)
{
- SoupMessage *msg;
+ SoupSession *session;
+ SoupMessage *msg;
char *expected;
int i;
g_type_init ();
+ session = soup_session_new ();
for (i = 0; i < ntests; i++) {
printf ("Test %d: %s\n", i + 1, tests[i].explanation);
soup_message_add_error_code_handler (
msg, SOUP_ERROR_OK, SOUP_HANDLER_PRE_BODY,
handler, expected);
- soup_message_send (msg);
+ soup_session_send_message (session, msg);
if (msg->errorcode != SOUP_ERROR_UNAUTHORIZED &&
msg->errorcode != SOUP_ERROR_OK) {
printf (" %d %s !\n", msg->errorcode,
printf ("\n");
- /* We don't free the message, because if we did, we'd
- * end up freeing the context and discarding the
- * cached auth info at the end of each test.
- */
+ g_object_unref (msg);
}
+ g_object_unref (session);
+
printf ("\nauth-test: %d errors\n", errors);
return errors;
}
#include <libsoup/soup.h>
gboolean recurse = FALSE;
+SoupSession *session;
GMainLoop *loop;
char *base;
SoupUri *base_uri;
soup_message_set_flags (msg, SOUP_MESSAGE_NO_REDIRECT);
pending++;
- soup_message_queue (msg, got_url, soup_uri_new (url));
+ soup_session_queue_message (session, msg, got_url, soup_uri_new (url));
g_free (url_to_get);
}
int opt;
g_type_init ();
+ session = soup_session_new ();
+
while ((opt = getopt (argc, argv, "r")) != -1) {
switch (opt) {
case 'r':
#include <libsoup/soup-message.h>
#include <libsoup/soup-server.h>
#include <libsoup/soup-server-message.h>
+#include <libsoup/soup-session.h>
/* WARNING: this is really really really not especially compliant with
* RFC 2616. But it does work for basic stuff.
*/
+SoupSession *session;
+
static void
copy_header (gpointer name, gpointer value, gpointer dest_headers)
{
g_signal_connect (msg, "finished", G_CALLBACK (client_msg_failed), msg2);
- soup_message_queue (msg2, finish_msg, msg);
+ soup_session_queue_message (session, msg2, finish_msg, msg);
g_object_ref (msg);
soup_message_io_pause (msg);
soup_server_get_port (server));
soup_server_run_async (server);
+ session = soup_session_new ();
+
printf ("\nWaiting for requests...\n");
loop = g_main_loop_new (NULL, TRUE);