First draft at the new object to maintain formerly-global state. (Not yet
authorDan Winship <danw@src.gnome.org>
Tue, 2 Sep 2003 16:15:19 +0000 (16:15 +0000)
committerDan Winship <danw@src.gnome.org>
Tue, 2 Sep 2003 16:15:19 +0000 (16:15 +0000)
* 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.

21 files changed:
ChangeLog
libsoup/Makefile.am
libsoup/soup-connection.c
libsoup/soup-connection.h
libsoup/soup-context.c
libsoup/soup-message-handlers.c
libsoup/soup-message-private.h
libsoup/soup-message-queue.c [new file with mode: 0644]
libsoup/soup-message-queue.h [new file with mode: 0644]
libsoup/soup-message.c
libsoup/soup-message.h
libsoup/soup-misc.c
libsoup/soup-misc.h
libsoup/soup-queue.c [deleted file]
libsoup/soup-queue.h [deleted file]
libsoup/soup-session.c [new file with mode: 0644]
libsoup/soup-session.h [new file with mode: 0644]
libsoup/soup.h
tests/auth-test.c
tests/get.c
tests/simple-proxy.c

index 3f3ca32..f45e60e 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,56 @@
+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
index dffd716..9d1c6c5 100644 (file)
@@ -40,6 +40,7 @@ libsoupinclude_HEADERS =      \
        soup-server-auth.h      \
        soup-server-message.h   \
        soup-server.h           \
+       soup-session.h          \
        soup-socket.h           \
        soup-types.h            \
        soup-uri.h
@@ -79,15 +80,16 @@ libsoup_2_2_la_SOURCES =            \
        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                      \
index f90ee1e..a376435 100644 (file)
 #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
@@ -46,14 +48,14 @@ enum {
 
 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
@@ -61,18 +63,33 @@ finalize (GObject *object)
 {
        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 */
@@ -104,6 +121,28 @@ socket_disconnected (SoupSocket *sock, gpointer conn)
        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)
 {
@@ -125,17 +164,107 @@ SoupConnection *
 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;
 }
 
@@ -151,21 +280,14 @@ soup_connection_disconnect (SoupConnection *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
@@ -191,53 +313,6 @@ soup_connection_get_socket (SoupConnection *conn)
 }
 
 
-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
@@ -249,15 +324,15 @@ soup_connection_is_in_use (SoupConnection *conn)
 {
        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)
@@ -279,20 +354,40 @@ soup_connection_is_new (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);
 }
index e9b17f8..979903b 100644 (file)
@@ -43,6 +43,13 @@ typedef void  (*SoupConnectionCallback)        (SoupConnection     *sock,
 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);
 
@@ -51,12 +58,11 @@ gboolean        soup_connection_is_connected   (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 */
index 49cc62c..6c4fec8 100644 (file)
@@ -23,8 +23,8 @@
 
 #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"
@@ -396,9 +396,6 @@ try_existing_connections (SoupContext           *ctx,
 
                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;
@@ -414,6 +411,8 @@ static gboolean
 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
@@ -429,8 +428,20 @@ try_create_connection (struct SoupConnectData *data)
        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;
 }
 
index dfa1193..3991a34 100644 (file)
@@ -33,40 +33,6 @@ typedef struct {
        } 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,
@@ -97,9 +63,8 @@ run_handler (SoupMessage     *msg,
 }
 
 /*
- * 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.
@@ -111,7 +76,6 @@ void
 soup_message_run_handlers (SoupMessage *msg, SoupHandlerPhase invoke_phase)
 {
        GSList *list;
-       SoupHandlerData *data;
 
        g_return_if_fail (SOUP_IS_MESSAGE (msg));
 
@@ -121,13 +85,6 @@ soup_message_run_handlers (SoupMessage *msg, SoupHandlerPhase invoke_phase)
                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
@@ -250,62 +207,3 @@ soup_message_remove_handler (SoupMessage     *msg,
                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");
-}
index 03fa989..cb8260f 100644 (file)
@@ -40,7 +40,6 @@ struct SoupMessagePrivate {
 
        SoupContext       *context;
        SoupConnection    *connection;
-       SoupSocket        *socket;
 };
 
 void             soup_message_run_handlers   (SoupMessage      *msg,
@@ -55,7 +54,6 @@ void             soup_message_set_context    (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,
diff --git a/libsoup/soup-message-queue.c b/libsoup/soup-message-queue.c
new file mode 100644 (file)
index 0000000..0bf1f7d
--- /dev/null
@@ -0,0 +1,194 @@
+/* -*- 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);
+}
diff --git a/libsoup/soup-message-queue.h b/libsoup/soup-message-queue.h
new file mode 100644 (file)
index 0000000..fcd8248
--- /dev/null
@@ -0,0 +1,37 @@
+/* -*- 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 */
index b8e40cc..d808ab0 100644 (file)
@@ -16,7 +16,6 @@
 #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;
@@ -164,7 +163,7 @@ class_init (GObjectClass *object_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,
@@ -353,13 +352,6 @@ static void
 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
@@ -381,8 +373,6 @@ cleanup_message (SoupMessage *req)
        }
 
        soup_message_set_connection (req, NULL);
-
-       soup_queue_remove_request (req);
 }
 
 /**
@@ -543,36 +533,22 @@ soup_message_foreach_header (GHashTable *hash, GHFunc func, gpointer user_data)
        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;
@@ -589,92 +565,6 @@ queue_message (SoupMessage *req)
                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
@@ -775,24 +665,12 @@ soup_message_get_uri (SoupMessage *msg)
 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 *
@@ -803,14 +681,6 @@ soup_message_get_connection (SoupMessage *msg)
        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)
 {
index fd830e4..affcb98 100644 (file)
@@ -100,13 +100,7 @@ void           soup_message_cancel              (SoupMessage       *req);
 
 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,
index 59a275f..d24d2d1 100644 (file)
@@ -1,11 +1,8 @@
 /* -*- 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>
@@ -15,7 +12,6 @@
 
 #include "soup-misc.h"
 #include "soup-private.h"
-#include "soup-queue.h"
 
 gboolean soup_initialized = FALSE;
 
@@ -672,20 +668,6 @@ soup_load_config (gchar *config_file)
        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;
index 3df12ce..4688428 100644 (file)
@@ -1,11 +1,6 @@
 /* -*- 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
@@ -20,8 +15,6 @@
 
 void               soup_load_config          (gchar       *config_file);
 
-void               soup_shutdown             (void);
-
 void               soup_set_proxy            (SoupContext *context);
 
 SoupContext       *soup_get_proxy            (void);
diff --git a/libsoup/soup-queue.c b/libsoup/soup-queue.c
deleted file mode 100644 (file)
index 0ac89b5..0000000
+++ /dev/null
@@ -1,365 +0,0 @@
-/* -*- 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 ();
-}
diff --git a/libsoup/soup-queue.h b/libsoup/soup-queue.h
deleted file mode 100644 (file)
index 35f8d11..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-/* -*- 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 */
diff --git a/libsoup/soup-session.c b/libsoup/soup-session.c
new file mode 100644 (file)
index 0000000..5917a6a
--- /dev/null
@@ -0,0 +1,341 @@
+/* -*- 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;
+}
diff --git a/libsoup/soup-session.h b/libsoup/soup-session.h
new file mode 100644 (file)
index 0000000..c146ebb
--- /dev/null
@@ -0,0 +1,46 @@
+/* -*- 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 */
index 992d9c6..08d9c4f 100644 (file)
@@ -1,11 +1,6 @@
 /* -*- 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
@@ -18,6 +13,7 @@ extern "C" {
 #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>
 
@@ -25,4 +21,4 @@ extern "C" {
 }
 #endif
 
-#endif /*SOUP_H*/
+#endif /* SOUP_H */
index 6628af7..b056227 100644 (file)
@@ -6,6 +6,7 @@
 #include "libsoup/soup-auth.h"
 #include "libsoup/soup-private.h"
 #include "libsoup/soup-message-private.h"
+#include "libsoup/soup-session.h"
 
 int errors = 0;
 
@@ -216,11 +217,13 @@ handler (SoupMessage *msg, gpointer data)
 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);
@@ -240,7 +243,7 @@ main (int argc, char **argv)
                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,
@@ -262,12 +265,11 @@ main (int argc, char **argv)
 
                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;
 }
index 67479bc..90ddb6f 100644 (file)
@@ -15,6 +15,7 @@
 #include <libsoup/soup.h>
 
 gboolean recurse = FALSE;
+SoupSession *session;
 GMainLoop *loop;
 char *base;
 SoupUri *base_uri;
@@ -189,7 +190,7 @@ get_url (const char *url)
        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);
 }
 
@@ -206,6 +207,8 @@ main (int argc, char **argv)
        int opt;
 
        g_type_init ();
+       session = soup_session_new ();
+
        while ((opt = getopt (argc, argv, "r")) != -1) {
                switch (opt) {
                case 'r':
index 3af7dc8..f0926a0 100644 (file)
 #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)
 {
@@ -107,7 +110,7 @@ server_callback (SoupServerContext *context, SoupMessage *msg, gpointer data)
 
        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);
@@ -155,6 +158,8 @@ main (int argc, char **argv)
                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);