Add gthread to glib check
authorDan Winship <danw@src.gnome.org>
Wed, 17 Dec 2003 19:49:14 +0000 (19:49 +0000)
committerDan Winship <danw@src.gnome.org>
Wed, 17 Dec 2003 19:49:14 +0000 (19:49 +0000)
* configure.in: Add gthread to glib check

* libsoup/soup-session.c: Make this an abstract class.

* libsoup/soup-session-async.c: A SoupSession class for
asynchronous gmain-based operation; replaces the old SoupSession.

* libsoup/soup-session-sync.c: A SoupSession class for synchronous
blocking operation for use with threaded apps.

* libsoup/soup-types.h, libsoup/soup.h: add the new session
subclasses

* libsoup/soup-connection.c (soup_connection_connect_sync): Don't
try to unref the socket if the socket creation fails.
(soup_connection_reserve): New, to explicitly mark a connection as
being in use without queueing a message on it.

* libsoup/soup-dns.c (check_hostent): Oof. Fix the logic of the
"block" flag to not be reversed.

* libsoup/soup-message.c (finished): set status to FINISHED here.
(soup_message_cancel): Gone; needs to be done at the session
level.

* libsoup/soup-message-queue.c: Add a mutex and make all of the
operations thread-safe.

* libsoup/soup-socket.c (disconnect_internal): Make this
thread-safe.
(soup_socket_connect): Make the sync case work correctly.

* libsoup/Makefile.am: add the SoupSession subclasses

* tests/Makefile.am: libsoup depends on libgthread now, so
revserver doesn't need to explicitly.

* tests/get.c, tests/auth-test.c, tests/simple-proxy.c: Use
soup_session_async_new().

22 files changed:
ChangeLog
configure.in
libsoup/Makefile.am
libsoup/soup-connection.c
libsoup/soup-connection.h
libsoup/soup-dns.c
libsoup/soup-message-queue.c
libsoup/soup-message.c
libsoup/soup-message.h
libsoup/soup-session-async.c [new file with mode: 0644]
libsoup/soup-session-async.h [new file with mode: 0644]
libsoup/soup-session-sync.c [new file with mode: 0644]
libsoup/soup-session-sync.h [new file with mode: 0644]
libsoup/soup-session.c
libsoup/soup-session.h
libsoup/soup-socket.c
libsoup/soup-types.h
libsoup/soup.h
tests/Makefile.am
tests/auth-test.c
tests/get.c
tests/simple-proxy.c

index 29e9e67..9eefbf8 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,45 @@
+2003-12-17  Dan Winship  <danw@ximian.com>
+
+       * configure.in: Add gthread to glib check
+
+       * libsoup/soup-session.c: Make this an abstract class.
+
+       * libsoup/soup-session-async.c: A SoupSession class for
+       asynchronous gmain-based operation; replaces the old SoupSession.
+
+       * libsoup/soup-session-sync.c: A SoupSession class for synchronous
+       blocking operation for use with threaded apps.
+
+       * libsoup/soup-types.h, libsoup/soup.h: add the new session
+       subclasses
+
+       * libsoup/soup-connection.c (soup_connection_connect_sync): Don't
+       try to unref the socket if the socket creation fails.
+       (soup_connection_reserve): New, to explicitly mark a connection as
+       being in use without queueing a message on it.
+
+       * libsoup/soup-dns.c (check_hostent): Oof. Fix the logic of the
+       "block" flag to not be reversed.
+
+       * libsoup/soup-message.c (finished): set status to FINISHED here.
+       (soup_message_cancel): Gone; needs to be done at the session
+       level.
+
+       * libsoup/soup-message-queue.c: Add a mutex and make all of the
+       operations thread-safe.
+
+       * libsoup/soup-socket.c (disconnect_internal): Make this
+       thread-safe.
+       (soup_socket_connect): Make the sync case work correctly.
+
+       * libsoup/Makefile.am: add the SoupSession subclasses
+
+       * tests/Makefile.am: libsoup depends on libgthread now, so
+       revserver doesn't need to explicitly.
+
+       * tests/get.c, tests/auth-test.c, tests/simple-proxy.c: Use
+       soup_session_async_new().
+
 2003-12-16  Rodrigo Moya <rodrigo@ximian.com>
 
        * libsoup/soup-soap-response.[ch] (soup_soap_parameter_get_int_value):
index d045489..8215a25 100644 (file)
@@ -72,7 +72,7 @@ dnl ***********************
 dnl *** Checks for glib ***
 dnl ***********************
 
-AM_PATH_GLIB_2_0(2.0.0,,,gobject)
+AM_PATH_GLIB_2_0(2.0.0,,,gobject gthread)
 
 PKG_CHECK_MODULES(XML, libxml-2.0)
 AC_SUBST(XML_CFLAGS)
index 2781ff8..e49a43e 100644 (file)
@@ -38,6 +38,8 @@ libsoupinclude_HEADERS =      \
        soup-server-message.h   \
        soup-server.h           \
        soup-session.h          \
+       soup-session-async.h    \
+       soup-session-sync.h     \
        soup-soap-message.h     \
        soup-soap-response.h    \
        soup-socket.h           \
@@ -88,6 +90,8 @@ libsoup_2_2_la_SOURCES =              \
        soup-server-auth.c              \
        soup-server-message.c           \
        soup-session.c                  \
+       soup-session-async.c            \
+       soup-session-sync.c             \
        soup-soap-message.c             \
        soup-soap-response.c            \
        soup-socket.c                   \
index c37f813..67bbfca 100644 (file)
@@ -44,6 +44,7 @@ struct SoupConnectionPrivate {
 
        SoupMessage *cur_req;
        time_t       last_used;
+       gboolean     in_use;
 };
 
 #define PARENT_TYPE G_TYPE_OBJECT
@@ -432,10 +433,14 @@ soup_connection_connect_sync (SoupConnection *conn)
 
        if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
        fail:
-               g_object_unref (conn->priv->socket);
-               conn->priv->socket = NULL;
+               if (conn->priv->socket) {
+                       g_object_unref (conn->priv->socket);
+                       conn->priv->socket = NULL;
+               }
        }
 
+       g_signal_emit (conn, signals[CONNECT_RESULT], 0,
+                      proxified_status (conn, status));
        return proxified_status (conn, status);
 }
 
@@ -473,7 +478,7 @@ soup_connection_is_in_use (SoupConnection *conn)
 {
        g_return_val_if_fail (SOUP_IS_CONNECTION (conn), FALSE);
 
-       return conn->priv->cur_req != NULL;
+       return conn->priv->in_use;
 }
 
 /**
@@ -500,6 +505,7 @@ request_done (SoupMessage *req, gpointer user_data)
                                      (gpointer *)conn->priv->cur_req);
        conn->priv->cur_req = NULL;
        conn->priv->last_used = time (NULL);
+       conn->priv->in_use = FALSE;
 
        g_signal_handlers_disconnect_by_func (req, request_done, conn);
 
@@ -513,6 +519,7 @@ send_request (SoupConnection *conn, SoupMessage *req)
        if (req != conn->priv->cur_req) {
                g_return_if_fail (conn->priv->cur_req == NULL);
                conn->priv->cur_req = req;
+               conn->priv->in_use = TRUE;
                g_object_add_weak_pointer (G_OBJECT (req),
                                           (gpointer *)conn->priv->cur_req);
 
@@ -536,6 +543,14 @@ soup_connection_send_request (SoupConnection *conn, SoupMessage *req)
 }
 
 void
+soup_connection_reserve (SoupConnection *conn)
+{
+       g_return_if_fail (SOUP_IS_CONNECTION (conn));
+
+       conn->priv->in_use = TRUE;
+}
+
+void
 soup_connection_authenticate (SoupConnection *conn, SoupMessage *msg,
                              const char *auth_type, const char *auth_realm,
                              char **username, char **password)
index 2d88b5a..d25b6a9 100644 (file)
@@ -69,6 +69,7 @@ time_t          soup_connection_last_used      (SoupConnection   *conn);
 
 void            soup_connection_send_request   (SoupConnection   *conn,
                                                SoupMessage      *req);
+void            soup_connection_reserve        (SoupConnection   *conn);
 
 /* protected */
 void            soup_connection_authenticate   (SoupConnection   *conn,
index a3fed01..1789abb 100644 (file)
@@ -613,9 +613,9 @@ check_hostent (SoupDNSEntry *entry, gboolean block)
        }
 
        if (block)
-               tvp = &tv;
-       else
                tvp = NULL;
+       else
+               tvp = &tv;
 
        do {
                FD_ZERO (&readfds);
index 0bf1f7d..9b89381 100644 (file)
@@ -14,6 +14,8 @@
 struct SoupMessageQueue {
        GList *head, *tail;
        GList *iters;
+
+       GMutex *mutex;
 };
 
 /**
@@ -24,7 +26,11 @@ struct SoupMessageQueue {
 SoupMessageQueue *
 soup_message_queue_new (void)
 {
-       return g_new0 (SoupMessageQueue, 1);
+       SoupMessageQueue *queue;
+
+       queue = g_new0 (SoupMessageQueue, 1);
+       queue->mutex = g_mutex_new ();
+       return queue;
 }
 
 /**
@@ -40,6 +46,7 @@ soup_message_queue_destroy (SoupMessageQueue *queue)
 
        g_list_free (queue->head);
        g_list_free (queue->iters);
+       g_mutex_free (queue->mutex);
        g_free (queue);
 }
 
@@ -53,6 +60,7 @@ soup_message_queue_destroy (SoupMessageQueue *queue)
 void
 soup_message_queue_append (SoupMessageQueue *queue, SoupMessage *msg)
 {
+       g_mutex_lock (queue->mutex);
        if (queue->head) {
                queue->tail = g_list_append (queue->tail, msg);
                queue->tail = queue->tail->next;
@@ -60,6 +68,7 @@ soup_message_queue_append (SoupMessageQueue *queue, SoupMessage *msg)
                queue->head = queue->tail = g_list_append (NULL, msg);
 
        g_object_add_weak_pointer (G_OBJECT (msg), &queue->tail->data);
+       g_mutex_unlock (queue->mutex);
 }
 
 /**
@@ -77,16 +86,60 @@ soup_message_queue_append (SoupMessageQueue *queue, SoupMessage *msg)
 SoupMessage *
 soup_message_queue_first (SoupMessageQueue *queue, SoupMessageQueueIter *iter)
 {
-       if (!queue->head)
+       g_mutex_lock (queue->mutex);
+
+       if (!queue->head) {
+               g_mutex_unlock (queue->mutex);
                return NULL;
+       }
 
        queue->iters = g_list_prepend (queue->iters, iter);
 
        iter->cur = NULL;
        iter->next = queue->head;
+       g_mutex_unlock (queue->mutex);
+
        return soup_message_queue_next (queue, iter);
 }
 
+static SoupMessage *
+queue_remove_internal (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;
+}
+
 /**
  * soup_message_queue_next:
  * @queue: a queue
@@ -97,19 +150,25 @@ soup_message_queue_first (SoupMessageQueue *queue, SoupMessageQueueIter *iter)
 SoupMessage *
 soup_message_queue_next (SoupMessageQueue *queue, SoupMessageQueueIter *iter)
 {
+       g_mutex_lock (queue->mutex);
+
        while (iter->next) {
                iter->cur = iter->next;
                iter->next = iter->cur->next;
-               if (iter->cur->data)
+               if (iter->cur->data) {
+                       g_mutex_unlock (queue->mutex);
                        return iter->cur->data;
+               }
 
                /* Message was finalized, remove dead queue element */
-               soup_message_queue_remove (queue, iter);
+               queue_remove_internal (queue, iter);
        }
 
        /* Nothing left */
        iter->cur = NULL;
-       soup_message_queue_free_iter (queue, iter);
+       queue->iters = g_list_remove (queue->iters, iter);
+
+       g_mutex_unlock (queue->mutex);
        return NULL;
 }
 
@@ -128,37 +187,11 @@ soup_message_queue_next (SoupMessageQueue *queue, SoupMessageQueueIter *iter)
 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;
+       g_mutex_lock (queue->mutex);
+       msg = queue_remove_internal (queue, iter);
+       g_mutex_unlock (queue->mutex);
 
        return msg;
 }
@@ -190,5 +223,7 @@ void
 soup_message_queue_free_iter (SoupMessageQueue *queue,
                              SoupMessageQueueIter *iter)
 {
+       g_mutex_lock (queue->mutex);
        queue->iters = g_list_remove (queue->iters, iter);
+       g_mutex_unlock (queue->mutex);
 }
index fe6edf8..6d85825 100644 (file)
@@ -40,7 +40,8 @@ static void wrote_body (SoupMessage *req);
 static void got_headers (SoupMessage *req);
 static void got_chunk (SoupMessage *req);
 static void got_body (SoupMessage *req);
-static void stop_io (SoupMessage *req);
+static void restarted (SoupMessage *req);
+static void finished (SoupMessage *req);
 static void free_chunks (SoupMessage *msg);
 
 static void
@@ -105,8 +106,8 @@ class_init (GObjectClass *object_class)
        message_class->got_headers  = got_headers;
        message_class->got_chunk    = got_chunk;
        message_class->got_body     = got_body;
-       message_class->restarted    = stop_io;
-       message_class->finished     = stop_io;
+       message_class->restarted    = restarted;
+       message_class->finished     = finished;
 
        /* virtual method override */
        object_class->finalize = finalize;
@@ -390,7 +391,7 @@ soup_message_got_body (SoupMessage *msg)
 }
 
 static void
-stop_io (SoupMessage *req)
+restarted (SoupMessage *req)
 {
        soup_message_io_cancel (req);
 }
@@ -401,27 +402,17 @@ soup_message_restarted (SoupMessage *msg)
        g_signal_emit (msg, signals[RESTARTED], 0);
 }
 
-void
-soup_message_finished (SoupMessage *msg)
+static void
+finished (SoupMessage *req)
 {
-       g_signal_emit (msg, signals[FINISHED], 0);
+       soup_message_io_cancel (req);
+       req->status = SOUP_MESSAGE_STATUS_FINISHED;
 }
 
-/**
- * soup_message_cancel:
- * @msg: a #SoupMessage currently being processed.
- * 
- * Cancel a running message, and issue completion callback with an
- * status code of %SOUP_STATUS_CANCELLED. If not requeued by the
- * completion callback, the @msg will be destroyed.
- */
 void
-soup_message_cancel (SoupMessage *msg)
+soup_message_finished (SoupMessage *msg)
 {
-       if (msg->status != SOUP_MESSAGE_STATUS_FINISHED) {
-               soup_message_set_status (msg, SOUP_STATUS_CANCELLED);
-               soup_message_finished (msg);
-       }
+       g_signal_emit (msg, signals[FINISHED], 0);
 }
 
 static gboolean
index 89a1753..f72fb7a 100644 (file)
@@ -102,8 +102,6 @@ void           soup_message_set_response        (SoupMessage       *msg,
                                                 char              *resp_body,
                                                 gulong             resp_length);
 
-void           soup_message_cancel              (SoupMessage       *msg);
-
 void           soup_message_add_header          (GHashTable        *hash,
                                                 const char        *name,
                                                 const char        *value);
diff --git a/libsoup/soup-session-async.c b/libsoup/soup-session-async.c
new file mode 100644 (file)
index 0000000..2d073a1
--- /dev/null
@@ -0,0 +1,213 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-session-async.c
+ *
+ * Copyright (C) 2000-2003, Ximian, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "soup-session-async.h"
+#include "soup-connection.h"
+
+struct SoupSessionAsyncPrivate {
+       int dummy;
+};
+
+static gboolean run_queue (SoupSessionAsync *sa, gboolean try_pruning);
+
+static void  queue_message   (SoupSession *session, SoupMessage *req,
+                             SoupMessageCallbackFn callback,
+                             gpointer user_data);
+static guint send_message    (SoupSession *session, SoupMessage *req);
+
+#define PARENT_TYPE SOUP_TYPE_SESSION
+static SoupSessionClass *parent_class;
+
+static void
+init (GObject *object)
+{
+       SoupSessionAsync *sa = SOUP_SESSION_ASYNC (object);
+
+       sa->priv = g_new0 (SoupSessionAsyncPrivate, 1);
+}
+
+static void
+finalize (GObject *object)
+{
+       SoupSessionAsync *sa = SOUP_SESSION_ASYNC (object);
+
+       g_free (sa->priv);
+
+       G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+class_init (GObjectClass *object_class)
+{
+       SoupSessionClass *session_class = SOUP_SESSION_CLASS (object_class);
+
+       parent_class = g_type_class_ref (PARENT_TYPE);
+
+       /* virtual method override */
+       session_class->queue_message = queue_message;
+       session_class->send_message = send_message;
+       object_class->finalize = finalize;
+}
+
+SOUP_MAKE_TYPE (soup_session_async, SoupSessionAsync, class_init, init, PARENT_TYPE)
+
+SoupSession *
+soup_session_async_new (void)
+{
+       return g_object_new (SOUP_TYPE_SESSION_ASYNC, NULL);
+}
+
+SoupSession *
+soup_session_async_new_with_options (const char *optname1, ...)
+{
+       SoupSession *session;
+       va_list ap;
+
+       va_start (ap, optname1);
+       session = (SoupSession *)g_object_new_valist (SOUP_TYPE_SESSION_ASYNC,
+                                                     optname1, ap);
+       va_end (ap);
+
+       return session;
+}
+
+
+static void
+connection_closed (SoupConnection *conn, SoupSessionAsync *sa)
+{
+       /* Run the queue in case anyone was waiting for a connection
+        * to be closed.
+        */
+       run_queue (sa, FALSE);
+}
+
+static void
+got_connection (SoupConnection *conn, guint status, gpointer user_data)
+{
+       SoupSessionAsync *sa = user_data;
+
+       if (status == SOUP_STATUS_OK) {
+               g_signal_connect (conn, "disconnected",
+                                 G_CALLBACK (connection_closed),
+                                 sa);
+       }
+
+       /* Either we just got a connection, or we just failed to
+        * open a connection and so decremented the open connection
+        * count by one. Either way, we need to run the queue now.
+        */
+       run_queue (sa, FALSE);
+}
+
+static gboolean
+run_queue (SoupSessionAsync *sa, gboolean try_pruning)
+{
+       SoupSession *session = SOUP_SESSION (sa);
+       SoupMessageQueueIter iter;
+       SoupMessage *msg;
+       SoupConnection *conn;
+       gboolean should_prune = FALSE, started_any = FALSE, is_new;
+
+       /* FIXME: prefer CONNECTING messages */
+
+ try_again:
+       for (msg = soup_message_queue_first (session->queue, &iter); msg; msg = soup_message_queue_next (session->queue, &iter)) {
+
+               if (!SOUP_MESSAGE_IS_STARTING (msg))
+                       continue;
+
+               conn = soup_session_get_connection (session, msg,
+                                                   &should_prune, &is_new);
+               if (!conn)
+                       continue;
+
+               if (is_new) {
+                       soup_connection_connect_async (conn, got_connection,
+                                                      session);
+               } else
+                       soup_session_send_message_via (session, msg, conn);
+
+               started_any = TRUE;
+       }
+
+       if (try_pruning && should_prune && !started_any) {
+               /* We didn't manage to start any message, but there is
+                * at least one message in the queue that could be
+                * sent if we pruned an idle connection from some
+                * other server.
+                */
+               if (soup_session_try_prune_connection (session)) {
+                       try_pruning = FALSE;
+                       goto try_again;
+               }
+       }
+
+       return started_any;
+}
+
+static void
+request_restarted (SoupMessage *req, gpointer sa)
+{
+       run_queue (sa, FALSE);
+}
+
+static void
+final_finished (SoupMessage *req, gpointer user_data)
+{
+       SoupSessionAsync *sa = user_data;
+
+       if (!SOUP_MESSAGE_IS_STARTING (req)) {
+               g_signal_handlers_disconnect_by_func (req, request_finished, sa);
+               g_signal_handlers_disconnect_by_func (req, final_finished, sa);
+               g_object_unref (req);
+       }
+
+       run_queue (sa, FALSE);
+}
+
+static void
+queue_message (SoupSession *session, SoupMessage *req,
+              SoupMessageCallbackFn callback, gpointer user_data)
+{
+       SoupSessionAsync *sa = SOUP_SESSION_ASYNC (session);
+
+       g_signal_connect (req, "restarted",
+                         G_CALLBACK (request_restarted), sa);
+
+       g_signal_connect (req, "finished",
+                         G_CALLBACK (request_finished), sa);
+       if (callback) {
+               g_signal_connect (req, "finished",
+                                 G_CALLBACK (callback), user_data);
+       }
+       g_signal_connect_after (req, "finished",
+                               G_CALLBACK (final_finished), sa);
+
+       SOUP_SESSION_CLASS (parent_class)->queue_message (session, req,
+                                                         callback, user_data);
+
+       run_queue (sa, TRUE);
+}
+
+static guint
+send_message (SoupSession *session, SoupMessage *req)
+{
+       /* Balance out the unref that final_finished will do */
+       g_object_ref (req);
+
+       queue_message (session, req, NULL, NULL);
+
+       while (req->status != SOUP_MESSAGE_STATUS_FINISHED &&
+              !SOUP_STATUS_IS_TRANSPORT_ERROR (req->status_code))
+               g_main_iteration (TRUE);
+
+       return req->status_code;
+}
diff --git a/libsoup/soup-session-async.h b/libsoup/soup-session-async.h
new file mode 100644 (file)
index 0000000..26f5fe7
--- /dev/null
@@ -0,0 +1,39 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2000-2003, Ximian, Inc.
+ */
+
+#ifndef SOUP_SESSION_ASYNC_H
+#define SOUP_SESSION_ASYNC_H 1
+
+#include <libsoup/soup-types.h>
+#include <libsoup/soup-session.h>
+
+#define SOUP_TYPE_SESSION_ASYNC            (soup_session_async_get_type ())
+#define SOUP_SESSION_ASYNC(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_SESSION_ASYNC, SoupSessionAsync))
+#define SOUP_SESSION_ASYNC_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_SESSION_ASYNC, SoupSessionAsyncClass))
+#define SOUP_IS_SESSION_ASYNC(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_SESSION_ASYNC))
+#define SOUP_IS_SESSION_ASYNC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SOUP_TYPE_SESSION_ASYNC))
+#define SOUP_SESSION_ASYNC_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_SESSION_ASYNC, SoupSessionAsyncClass))
+
+typedef struct SoupSessionAsyncPrivate SoupSessionAsyncPrivate;
+
+struct SoupSessionAsync {
+       SoupSession parent;
+
+       SoupSessionAsyncPrivate *priv;
+};
+
+typedef struct {
+       SoupSessionClass parent_class;
+
+} SoupSessionAsyncClass;
+
+GType soup_session_async_get_type (void);
+
+SoupSession *soup_session_async_new              (void);
+SoupSession *soup_session_async_new_with_options (const char *optname1,
+                                                 ...);
+
+
+#endif /* SOUP_SESSION_ASYNC_H */
diff --git a/libsoup/soup-session-sync.c b/libsoup/soup-session-sync.c
new file mode 100644 (file)
index 0000000..5c07804
--- /dev/null
@@ -0,0 +1,197 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-session-sync.c
+ *
+ * Copyright (C) 2000-2003, Ximian, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "soup-session-sync.h"
+#include "soup-connection.h"
+
+struct SoupSessionSyncPrivate {
+       GMutex *lock;
+       GCond *cond;
+};
+
+void         queue_message  (SoupSession *session, SoupMessage *msg,
+                            SoupMessageCallbackFn callback,
+                            gpointer user_data);
+static guint send_message   (SoupSession *session, SoupMessage *msg);
+static void  cancel_message (SoupSession *session, SoupMessage *msg);
+
+#define PARENT_TYPE SOUP_TYPE_SESSION
+static SoupSessionClass *parent_class;
+
+static void
+init (GObject *object)
+{
+       SoupSessionSync *ss = SOUP_SESSION_SYNC (object);
+
+       ss->priv = g_new0 (SoupSessionSyncPrivate, 1);
+       ss->priv->lock = g_mutex_new ();
+       ss->priv->cond = g_cond_new ();
+}
+
+static void
+finalize (GObject *object)
+{
+       SoupSessionSync *ss = SOUP_SESSION_SYNC (object);
+
+       g_mutex_free (ss->priv->lock);
+       g_cond_free (ss->priv->cond);
+       g_free (ss->priv);
+
+       G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+class_init (GObjectClass *object_class)
+{
+       SoupSessionClass *session_class = SOUP_SESSION_CLASS (object_class);
+
+       parent_class = g_type_class_ref (PARENT_TYPE);
+
+       /* virtual method override */
+       session_class->queue_message = queue_message;
+       session_class->send_message = send_message;
+       session_class->cancel_message = cancel_message;
+       object_class->finalize = finalize;
+}
+
+SOUP_MAKE_TYPE (soup_session_sync, SoupSessionSync, class_init, init, PARENT_TYPE)
+
+SoupSession *
+soup_session_sync_new (void)
+{
+       return g_object_new (SOUP_TYPE_SESSION_SYNC, NULL);
+}
+
+SoupSession *
+soup_session_sync_new_with_options (const char *optname1, ...)
+{
+       SoupSession *session;
+       va_list ap;
+
+       va_start (ap, optname1);
+       session = (SoupSession *)g_object_new_valist (SOUP_TYPE_SESSION_SYNC,
+                                                     optname1, ap);
+       va_end (ap);
+
+       return session;
+}
+
+
+void
+queue_message (SoupSession *session, SoupMessage *msg,
+              SoupMessageCallbackFn callback, gpointer user_data)
+{
+       /* FIXME */
+       g_warning ("soup_session_queue_message called on synchronous session");
+}
+
+static SoupConnection *
+wait_for_connection (SoupSession *session, SoupMessage *msg)
+{
+       SoupSessionSync *ss = SOUP_SESSION_SYNC (session);
+       SoupConnection *conn;
+       gboolean try_pruning = FALSE, is_new = FALSE;
+       guint status;
+
+       g_mutex_lock (ss->priv->lock);
+
+ try_again:
+       conn = soup_session_get_connection (session, msg,
+                                           &try_pruning, &is_new);
+       if (conn) {
+               if (is_new) {
+                       status = soup_connection_connect_sync (conn);
+
+                       /* If the connection attempt fails, SoupSession
+                        * will notice, unref conn, and set an error
+                        * status on msg. So all we need to do is just
+                        * not return the no-longer-valid connection.
+                        */
+
+                       if (!SOUP_STATUS_IS_SUCCESSFUL (status))
+                               conn = NULL;
+                       else if (msg->status == SOUP_MESSAGE_STATUS_FINISHED) {
+                               /* Message was cancelled while we were
+                                * connecting.
+                                */
+                               soup_connection_disconnect (conn);
+                               conn = NULL;
+                       }
+               }
+
+               g_mutex_unlock (ss->priv->lock);
+               return conn;
+       }
+
+       if (try_pruning && soup_session_try_prune_connection (session))
+               goto try_again;
+
+       /* Wait... */
+       g_cond_wait (ss->priv->cond, ss->priv->lock);
+
+       /* See if something bad happened */
+       if (msg->status == SOUP_MESSAGE_STATUS_FINISHED) {
+               g_mutex_unlock (ss->priv->lock);
+               return NULL;
+       }
+
+       goto try_again;
+}
+
+static guint
+send_message (SoupSession *session, SoupMessage *msg)
+{
+       SoupConnection *conn;
+
+       SOUP_SESSION_CLASS (parent_class)->queue_message (session, msg,
+                                                         NULL, NULL);
+
+       do {
+               /* Get a connection */
+               conn = wait_for_connection (session, msg);
+               if (!conn)
+                       return msg->status_code;
+
+               /* Set up a weak pointer so that "conn" is zeroed out
+                * if the connection is destroyed.
+                */
+               g_object_add_weak_pointer (G_OBJECT (conn),
+                                          (gpointer *)&conn);
+
+               /* Now repeatedly send the message across the connection
+                * until either it's done, or the connection is closed.
+                */
+               while (msg->status != SOUP_MESSAGE_STATUS_FINISHED && conn)
+                       soup_session_send_message_via (session, msg, conn);
+
+               if (conn) {
+                       g_object_remove_weak_pointer (G_OBJECT (conn),
+                                                     (gpointer *)&conn);
+               }
+
+               /* If the message isn't finished, that means we need to
+                * re-send it on a new connection, so loop back to the
+                * beginning.
+                */
+       } while (msg->status != SOUP_MESSAGE_STATUS_FINISHED);
+
+       return msg->status_code;
+}
+
+static void
+cancel_message (SoupSession *session, SoupMessage *msg)
+{
+       SoupSessionSync *ss = SOUP_SESSION_SYNC (session);
+
+       SOUP_SESSION_CLASS (parent_class)->cancel_message (session, msg);
+       g_cond_broadcast (ss->priv->cond);
+}
+
diff --git a/libsoup/soup-session-sync.h b/libsoup/soup-session-sync.h
new file mode 100644 (file)
index 0000000..8765279
--- /dev/null
@@ -0,0 +1,39 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2000-2003, Ximian, Inc.
+ */
+
+#ifndef SOUP_SESSION_SYNC_H
+#define SOUP_SESSION_SYNC_H 1
+
+#include <libsoup/soup-types.h>
+#include <libsoup/soup-session.h>
+
+#define SOUP_TYPE_SESSION_SYNC            (soup_session_sync_get_type ())
+#define SOUP_SESSION_SYNC(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_SESSION_SYNC, SoupSessionSync))
+#define SOUP_SESSION_SYNC_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_SESSION_SYNC, SoupSessionSyncClass))
+#define SOUP_IS_SESSION_SYNC(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_SESSION_SYNC))
+#define SOUP_IS_SESSION_SYNC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SOUP_TYPE_SESSION_SYNC))
+#define SOUP_SESSION_SYNC_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_SESSION_SYNC, SoupSessionSyncClass))
+
+typedef struct SoupSessionSyncPrivate SoupSessionSyncPrivate;
+
+struct SoupSessionSync {
+       SoupSession parent;
+
+       SoupSessionSyncPrivate *priv;
+};
+
+typedef struct {
+       SoupSessionClass parent_class;
+
+} SoupSessionSyncClass;
+
+GType soup_session_sync_get_type (void);
+
+SoupSession *soup_session_sync_new              (void);
+SoupSession *soup_session_sync_new_with_options (const char *optname1,
+                                                ...);
+
+
+#endif /* SOUP_SESSION_SYNC_H */
index bc4d79c..65da464 100644 (file)
@@ -24,7 +24,6 @@
 
 typedef struct {
        SoupUri    *root_uri;
-       guint       error;
 
        GSList     *connections;      /* CONTAINS: SoupConnection */
        guint       num_conns;
@@ -41,20 +40,28 @@ struct SoupSessionPrivate {
        char *ssl_ca_file;
        gpointer ssl_creds;
 
-       SoupMessageQueue *queue;
-
        GHashTable *hosts; /* SoupUri -> SoupSessionHost */
        GHashTable *conns; /* SoupConnection -> SoupSessionHost */
        guint num_conns;
 
        SoupSessionHost *proxy_host;
+
+       /* Must hold the host_lock before potentially creating a
+        * new SoupSessionHost, or adding/removing a connection.
+        * Must not emit signals or destroy objects while holding it.
+        */
+       GMutex *host_lock;
 };
 
 static guint    host_uri_hash  (gconstpointer key);
 static gboolean host_uri_equal (gconstpointer v1, gconstpointer v2);
 static void     free_host      (SoupSessionHost *host, SoupSession *session);
 
-static gboolean run_queue (SoupSession *session, gboolean try_pruning);
+static void queue_message   (SoupSession *session, SoupMessage *msg,
+                            SoupMessageCallbackFn callback,
+                            gpointer user_data);
+static void requeue_message (SoupSession *session, SoupMessage *msg);
+static void cancel_message  (SoupSession *session, SoupMessage *msg);
 
 #define SOUP_SESSION_MAX_CONNS_DEFAULT 10
 #define SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT 4
@@ -93,7 +100,8 @@ init (GObject *object)
        SoupSession *session = SOUP_SESSION (object);
 
        session->priv = g_new0 (SoupSessionPrivate, 1);
-       session->priv->queue = soup_message_queue_new ();
+       session->priv->host_lock = g_mutex_new ();
+       session->queue = soup_message_queue_new ();
        session->priv->hosts = g_hash_table_new (host_uri_hash,
                                                 host_uri_equal);
        session->priv->conns = g_hash_table_new (NULL, NULL);
@@ -132,7 +140,8 @@ finalize (GObject *object)
 {
        SoupSession *session = SOUP_SESSION (object);
 
-       soup_message_queue_destroy (session->priv->queue);
+       g_mutex_free (session->priv->host_lock);
+       soup_message_queue_destroy (session->queue);
        g_hash_table_destroy (session->priv->hosts);
        g_hash_table_destroy (session->priv->conns);
        g_free (session->priv);
@@ -143,8 +152,15 @@ finalize (GObject *object)
 static void
 class_init (GObjectClass *object_class)
 {
+       SoupSessionClass *session_class = SOUP_SESSION_CLASS (object_class);
+
        parent_class = g_type_class_ref (PARENT_TYPE);
 
+       /* virtual method definition */
+       session_class->queue_message = queue_message;
+       session_class->requeue_message = requeue_message;
+       session_class->cancel_message = cancel_message;
+
        /* virtual method override */
        object_class->dispose = dispose;
        object_class->finalize = finalize;
@@ -222,32 +238,13 @@ class_init (GObjectClass *object_class)
 
 SOUP_MAKE_TYPE (soup_session, SoupSession, class_init, init, PARENT_TYPE)
 
-SoupSession *
-soup_session_new (void)
-{
-       return g_object_new (SOUP_TYPE_SESSION, NULL);
-}
-
-SoupSession *
-soup_session_new_with_options (const char *optname1, ...)
-{
-       SoupSession *session;
-       va_list ap;
-
-       va_start (ap, optname1);
-       session = (SoupSession *)g_object_new_valist (SOUP_TYPE_SESSION, optname1, ap);
-       va_end (ap);
-
-       return session;
-}
-
 static gboolean
 safe_uri_equal (const SoupUri *a, const SoupUri *b)
 {
        if (!a && !b)
                return TRUE;
 
-       if (a && !b || b && !a)
+       if ((a && !b) || (b && !a))
                return FALSE;
 
        return soup_uri_equal (a, b);
@@ -367,6 +364,10 @@ soup_session_host_new (SoupSession *session, const SoupUri *source_uri)
        return host;
 }
 
+/* Note: get_host_for_message doesn't lock the host_lock. The caller
+ * must do it itself if there's a chance the host doesn't already
+ * exist.
+ */
 static SoupSessionHost *
 get_host_for_message (SoupSession *session, SoupMessage *msg)
 {
@@ -378,21 +379,22 @@ get_host_for_message (SoupSession *session, SoupMessage *msg)
                return host;
 
        host = soup_session_host_new (session, source);
-
        g_hash_table_insert (session->priv->hosts, host->root_uri, host);
 
        return host;
 }
 
+/* Note: get_proxy_host doesn't lock the host_lock. The caller must do
+ * it itself if there's a chance the host doesn't already exist.
+ */
 static SoupSessionHost *
 get_proxy_host (SoupSession *session)
 {
-       if (session->priv->proxy_host)
+       if (session->priv->proxy_host || !session->priv->proxy_uri)
                return session->priv->proxy_host;
 
        session->priv->proxy_host =
                soup_session_host_new (session, session->priv->proxy_uri);
-
        return session->priv->proxy_host;
 }
 
@@ -430,6 +432,7 @@ free_host (SoupSessionHost *host, SoupSession *session)
        }
 
        soup_uri_free (host->root_uri);
+       g_free (host);
 }      
 
 /* Authentication */
@@ -627,8 +630,8 @@ update_auth_internal (SoupSession *session, SoupMessage *msg,
 
        /* If we need to authenticate, try to do it. */
        if (!soup_auth_is_authenticated (new_auth)) {
-               return authenticate_auth (session, new_auth, msg,
-                                         prior_auth_failed, proxy);
+               return authenticate_auth (session, new_auth,
+                                         msg, prior_auth_failed, proxy);
        }
 
        /* Otherwise we're good. */
@@ -689,47 +692,17 @@ redirect_handler (SoupMessage *msg, gpointer user_data)
        if (!new_loc)
                return;
        new_uri = soup_uri_new (new_loc);
-       if (!new_uri)
-               goto INVALID_REDIRECT;
+       if (!new_uri) {
+               soup_message_set_status_full (msg,
+                                             SOUP_STATUS_MALFORMED,
+                                             "Invalid Redirect URL");
+               return;
+       }
 
        soup_message_set_uri (msg, new_uri);
        soup_uri_free (new_uri);
 
        soup_session_requeue_message (session, msg);
-       return;
-
- INVALID_REDIRECT:
-       soup_message_set_status_full (msg,
-                                     SOUP_STATUS_MALFORMED,
-                                     "Invalid Redirect URL");
-}
-
-static void
-request_restarted (SoupMessage *req, gpointer session)
-{
-       run_queue (session, FALSE);
-}
-
-static void
-request_finished (SoupMessage *req, gpointer user_data)
-{
-       req->status = SOUP_MESSAGE_STATUS_FINISHED;
-}
-
-static void
-final_finished (SoupMessage *req, gpointer user_data)
-{
-       SoupSession *session = user_data;
-
-       if (!SOUP_MESSAGE_IS_STARTING (req)) {
-               soup_message_queue_remove_message (session->priv->queue, 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);
-       }
-
-       run_queue (session, FALSE);
 }
 
 static void
@@ -755,15 +728,16 @@ add_auth (SoupSession *session, SoupMessage *msg, gboolean proxy)
        }
 }
 
-static void
-send_request (SoupSession *session, SoupMessage *req, SoupConnection *conn)
+void
+soup_session_send_message_via (SoupSession *session, SoupMessage *msg,
+                              SoupConnection *conn)
 {
-       req->status = SOUP_MESSAGE_STATUS_RUNNING;
+       msg->status = SOUP_MESSAGE_STATUS_RUNNING;
 
-       add_auth (session, req, FALSE);
+       add_auth (session, msg, FALSE);
        if (session->priv->proxy_uri)
-               add_auth (session, req, TRUE);
-       soup_connection_send_request (conn, req);
+               add_auth (session, msg, TRUE);
+       soup_connection_send_request (conn, msg);
 }
 
 static void
@@ -771,8 +745,11 @@ find_oldest_connection (gpointer key, gpointer host, gpointer data)
 {
        SoupConnection *conn = key, **oldest = data;
 
-       /* Don't prune a connection that hasn't even been used yet. */
-       if (soup_connection_last_used (conn) == 0)
+       /* Don't prune a connection that is currently in use, or
+        * hasn't been used yet.
+        */
+       if (soup_connection_is_in_use (conn) ||
+           soup_connection_last_used (conn) == 0)
                return;
 
        if (!*oldest || (soup_connection_last_used (conn) <
@@ -780,67 +757,86 @@ find_oldest_connection (gpointer key, gpointer host, gpointer data)
                *oldest = conn;
 }
 
-static gboolean
-try_prune_connection (SoupSession *session)
+/**
+ * soup_session_try_prune_connection:
+ * @session: a #SoupSession
+ *
+ * Finds the least-recently-used idle connection in @session and closes
+ * it.
+ *
+ * Return value: %TRUE if a connection was closed, %FALSE if there are
+ * no idle connections.
+ **/
+gboolean
+soup_session_try_prune_connection (SoupSession *session)
 {
        SoupConnection *oldest = NULL;
 
+       g_mutex_lock (session->priv->host_lock);
        g_hash_table_foreach (session->priv->conns, find_oldest_connection,
                              &oldest);
        if (oldest) {
+               /* Ref the connection before unlocking the mutex in
+                * case someone else tries to prune it too.
+                */
+               g_object_ref (oldest);
+               g_mutex_unlock (session->priv->host_lock);
                soup_connection_disconnect (oldest);
                g_object_unref (oldest);
                return TRUE;
-       } else
+       } else {
+               g_mutex_unlock (session->priv->host_lock);
                return FALSE;
+       }
 }
 
-static void connection_closed (SoupConnection *conn, SoupSession *session);
-
 static void
-cleanup_connection (SoupSession *session, SoupConnection *conn)
+connection_disconnected (SoupConnection *conn, gpointer user_data)
 {
-       SoupSessionHost *host =
-               g_hash_table_lookup (session->priv->conns, conn);
+       SoupSession *session = user_data;
+       SoupSessionHost *host;
 
-       g_return_if_fail (host != NULL);
+       g_mutex_lock (session->priv->host_lock);
 
-       g_hash_table_remove (session->priv->conns, conn);
-       g_signal_handlers_disconnect_by_func (conn, connection_closed, session);
-       session->priv->num_conns--;
-
-       host->connections = g_slist_remove (host->connections, conn);
-       host->num_conns--;
-}
+       host = g_hash_table_lookup (session->priv->conns, conn);
+       if (host) {
+               g_hash_table_remove (session->priv->conns, conn);
+               host->connections = g_slist_remove (host->connections, conn);
+               host->num_conns--;
+       }
 
-static void
-connection_closed (SoupConnection *conn, SoupSession *session)
-{
-       cleanup_connection (session, conn);
+       g_signal_handlers_disconnect_by_func (conn, connection_disconnected, session);
+       session->priv->num_conns--;
 
-       /* Run the queue in case anyone was waiting for a connection
-        * to be closed.
-        */
-       run_queue (session, FALSE);
+       g_mutex_unlock (session->priv->host_lock);
+       g_object_unref (conn);
 }
 
 static void
-got_connection (SoupConnection *conn, guint status, gpointer user_data)
+connect_result (SoupConnection *conn, guint status, gpointer user_data)
 {
        SoupSession *session = user_data;
-       SoupSessionHost *host = g_hash_table_lookup (session->priv->conns, conn);
+       SoupSessionHost *host;
+       SoupMessageQueueIter iter;
+       SoupMessage *msg;
 
-       g_return_if_fail (host != NULL);
+       g_mutex_lock (session->priv->host_lock);
+
+       host = g_hash_table_lookup (session->priv->conns, conn);
+       if (!host) {
+               g_mutex_unlock (session->priv->host_lock);
+               return;
+       }
 
        if (status == SOUP_STATUS_OK) {
                host->connections = g_slist_prepend (host->connections, conn);
-               run_queue (session, FALSE);
+               g_mutex_unlock (session->priv->host_lock);
                return;
        }
 
-       /* We failed */
-       cleanup_connection (session, conn);
-       g_object_unref (conn);
+       /* The connection failed. */
+       g_mutex_unlock (session->priv->host_lock);
+       connection_disconnected (conn, session);
 
        if (host->connections) {
                /* Something went wrong this time, but we have at
@@ -851,240 +847,258 @@ got_connection (SoupConnection *conn, guint status, gpointer user_data)
                return;
        }
 
-       /* Flush any queued messages for this host */
-       host->error = status;
-       run_queue (session, FALSE);
-
-       if (status != SOUP_STATUS_CANT_RESOLVE &&
-           status != SOUP_STATUS_CANT_RESOLVE_PROXY) {
-               /* If the error was "can't resolve", then it's not likely
-                * to improve. But if it was something else, it may have
-                * been transient, so we clear the error so the user can
-                * try again later.
-                */
-               host->error = 0;
+       /* It's hopeless. Cancel everything that was waiting for this host. */
+       for (msg = soup_message_queue_first (session->queue, &iter); msg; msg = soup_message_queue_next (session->queue, &iter)) {
+               if (get_host_for_message (session, msg) == host) {
+                       soup_message_set_status (msg, status);
+                       soup_session_cancel_message (session, msg);
+               }
        }
 }
 
-static gboolean
-run_queue (SoupSession *session, gboolean try_pruning)
+/**
+ * soup_session_get_connection:
+ * @session: a #SoupSession
+ * @msg: a #SoupMessage
+ * @try_pruning: on return, whether or not to try pruning a connection
+ * @is_new: on return, %TRUE if the returned connection is new and not
+ * yet connected
+ * 
+ * Tries to find or create a connection for @msg. If there is an idle
+ * connection to the relevant host available, then it will be returned
+ * (with *@is_new set to %FALSE). Otherwise, if it is possible to
+ * create a new connection, one will be created and returned, with
+ * *@is_new set to %TRUE.
+ *
+ * If no connection can be made, it will return %NULL. If @session has
+ * the maximum number of open connections open, but does not have the
+ * maximum number of per-host connections open to the relevant host, then
+ * *@try_pruning will be set to %TRUE. In this case, the caller can
+ * call soup_session_try_prune_connection() to close an idle connection,
+ * and then try soup_session_get_connection() again. (If calling
+ * soup_session_try_prune_connection() wouldn't help, then *@try_pruning
+ * is left untouched; it is NOT set to %FALSE.)
+ *
+ * Return value: a #SoupConnection, or %NULL
+ **/
+SoupConnection *
+soup_session_get_connection (SoupSession *session, SoupMessage *msg,
+                            gboolean *try_pruning, gboolean *is_new)
 {
-       SoupMessageQueueIter iter;
-       SoupMessage *msg;
        SoupConnection *conn;
        SoupSessionHost *host;
-       gboolean skipped_any = FALSE, started_any = FALSE;
        GSList *conns;
 
-       /* FIXME: prefer CONNECTING messages */
-
- try_again:
-       for (msg = soup_message_queue_first (session->priv->queue, &iter); msg; msg = soup_message_queue_next (session->priv->queue, &iter)) {
-
-               if (!SOUP_MESSAGE_IS_STARTING (msg))
-                       continue;
-
-               host = get_host_for_message (session, msg);
+       g_mutex_lock (session->priv->host_lock);
 
-               /* If the hostname is known to be bad, fail right away */
-               if (host->error) {
-                       soup_message_set_status (msg, host->error);
-                       msg->status = SOUP_MESSAGE_STATUS_FINISHED;
-                       soup_message_finished (msg);
+       host = get_host_for_message (session, msg);
+       for (conns = host->connections; conns; conns = conns->next) {
+               if (!soup_connection_is_in_use (conns->data)) {
+                       soup_connection_reserve (conns->data);
+                       g_mutex_unlock (session->priv->host_lock);
+                       *is_new = FALSE;
+                       return conns->data;
                }
+       }
 
-               /* If there is an idle connection, use it */
-               for (conns = host->connections; conns; conns = conns->next) {
-                       if (!soup_connection_is_in_use (conns->data))
-                               break;
-               }
-               if (conns) {
-                       send_request (session, msg, conns->data);
-                       started_any = TRUE;
-                       continue;
-               }
+       if (msg->status == SOUP_MESSAGE_STATUS_CONNECTING) {
+               /* We already started a connection for this
+                * message, so don't start another one.
+                */
+               g_mutex_unlock (session->priv->host_lock);
+               return NULL;
+       }
 
-               if (msg->status == SOUP_MESSAGE_STATUS_CONNECTING) {
-                       /* We already started a connection for this
-                        * message, so don't start another one.
-                        */
-                       continue;
-               }
+       if (host->num_conns >= session->priv->max_conns_per_host) {
+               g_mutex_unlock (session->priv->host_lock);
+               return NULL;
+       }
 
-               /* If we have the max number of per-host connections
-                * or total connections open, we'll have to wait.
-                */
-               if (host->num_conns >= session->priv->max_conns_per_host)
-                       continue;
-               else if (session->priv->num_conns >= session->priv->max_conns) {
-                       /* In this case, closing an idle connection
-                        * somewhere else would let us open one here.
-                        */
-                       skipped_any = TRUE;
-                       continue;
-               }
+       if (session->priv->num_conns >= session->priv->max_conns) {
+               *try_pruning = TRUE;
+               g_mutex_unlock (session->priv->host_lock);
+               return NULL;
+       }
 
-               /* Otherwise, open a new connection */
-               conn = g_object_new (
-                       (session->priv->use_ntlm ?
-                        SOUP_TYPE_CONNECTION_NTLM : SOUP_TYPE_CONNECTION),
-                       SOUP_CONNECTION_ORIGIN_URI, host->root_uri,
-                       SOUP_CONNECTION_PROXY_URI, session->priv->proxy_uri,
-                       SOUP_CONNECTION_SSL_CREDENTIALS, session->priv->ssl_creds,
-                       NULL);
-               g_signal_connect (conn, "authenticate",
-                                 G_CALLBACK (connection_authenticate),
-                                 session);
-               g_signal_connect (conn, "reauthenticate",
-                                 G_CALLBACK (connection_reauthenticate),
-                                 session);
-
-               soup_connection_connect_async (conn, got_connection, session);
-               g_signal_connect (conn, "disconnected",
-                                 G_CALLBACK (connection_closed), session);
-               g_hash_table_insert (session->priv->conns, conn, host);
-               session->priv->num_conns++;
-
-               /* Increment the host's connection count, but don't add
-                * this connection to the list yet, since it's not ready.
-                */
-               host->num_conns++;
+       /* Make sure session->priv->proxy_host gets set now while
+        * we have the host_lock.
+        */
+       if (session->priv->proxy_uri)
+               get_proxy_host (session);
+
+       conn = g_object_new (
+               (session->priv->use_ntlm ?
+                SOUP_TYPE_CONNECTION_NTLM : SOUP_TYPE_CONNECTION),
+               SOUP_CONNECTION_ORIGIN_URI, host->root_uri,
+               SOUP_CONNECTION_PROXY_URI, session->priv->proxy_uri,
+               SOUP_CONNECTION_SSL_CREDENTIALS, session->priv->ssl_creds,
+               NULL);
+       g_signal_connect (conn, "connect_result",
+                         G_CALLBACK (connect_result),
+                         session);
+       g_signal_connect (conn, "disconnected",
+                         G_CALLBACK (connection_disconnected),
+                         session);
+       g_signal_connect (conn, "authenticate",
+                         G_CALLBACK (connection_authenticate),
+                         session);
+       g_signal_connect (conn, "reauthenticate",
+                         G_CALLBACK (connection_reauthenticate),
+                         session);
+
+       g_hash_table_insert (session->priv->conns, conn, host);
+
+       /* We increment the connection counts so it counts against the
+        * totals, but we don't add it to the host's connection list
+        * yet, since it's not ready for use.
+        */
+       session->priv->num_conns++;
+       host->num_conns++;
 
-               /* Mark the request as connecting, so we don't try to
-                * open another new connection for it next time around.
-                */
-               msg->status = SOUP_MESSAGE_STATUS_CONNECTING;
+       /* Mark the request as connecting, so we don't try to open
+        * another new connection for it while waiting for this one.
+        */
+       msg->status = SOUP_MESSAGE_STATUS_CONNECTING;
 
-               started_any = TRUE;
-       }
+       g_mutex_unlock (session->priv->host_lock);
+       *is_new = TRUE;
+       return conn;
+}
 
-       if (try_pruning && skipped_any && !started_any) {
-               /* We didn't manage to start any message, but there is
-                * at least one message in the queue that could be
-                * sent if we pruned an idle connection from some
-                * other server.
-                */
-               if (try_prune_connection (session)) {
-                       try_pruning = FALSE;
-                       goto try_again;
-               }
-       }
+static void
+message_finished (SoupMessage *msg, gpointer user_data)
+{
+       SoupSession *session = user_data;
 
-       return started_any;
+       if (!SOUP_MESSAGE_IS_STARTING (msg)) {
+               soup_message_queue_remove_message (session->queue, msg);
+               g_signal_handlers_disconnect_by_func (msg, message_finished, session);
+       }
 }
 
 static void
-queue_message (SoupSession *session, SoupMessage *req, gboolean requeue)
+queue_message (SoupSession *session, SoupMessage *msg,
+              SoupMessageCallbackFn callback, gpointer user_data)
 {
-       req->status = SOUP_MESSAGE_STATUS_QUEUED;
-       if (!requeue) {
-               soup_message_queue_append (session->priv->queue, req);
-               run_queue (session, TRUE);
+       g_signal_connect_after (msg, "finished",
+                               G_CALLBACK (message_finished), session);
+
+       soup_message_add_status_code_handler  (msg, SOUP_STATUS_UNAUTHORIZED,
+                                              SOUP_HANDLER_POST_BODY,
+                                              authorize_handler, session);
+       soup_message_add_status_code_handler  (msg,
+                                              SOUP_STATUS_PROXY_UNAUTHORIZED,
+                                              SOUP_HANDLER_POST_BODY,
+                                              authorize_handler, session);
+
+       if (!(soup_message_get_flags (msg) & SOUP_MESSAGE_NO_REDIRECT)) {
+               soup_message_add_status_class_handler (
+                       msg, SOUP_STATUS_CLASS_REDIRECT,
+                       SOUP_HANDLER_POST_BODY,
+                       redirect_handler, session);
        }
+
+       msg->status = SOUP_MESSAGE_STATUS_QUEUED;
+       soup_message_queue_append (session->queue, msg);
 }
 
 /**
  * soup_session_queue_message:
  * @session: a #SoupSession
- * @req: the message to queue
+ * @msg: the message to queue
  * @callback: a #SoupMessageCallbackFn 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,
+ * Queues the message @msg for sending. All messages are processed
+ * while the glib main loop runs. If @msg 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.
+ * not been requeued, @msg will be unreffed.
  */
 void
-soup_session_queue_message (SoupSession *session, SoupMessage *req,
+soup_session_queue_message (SoupSession *session, SoupMessage *msg,
                            SoupMessageCallbackFn callback, gpointer user_data)
 {
        g_return_if_fail (SOUP_IS_SESSION (session));
-       g_return_if_fail (SOUP_IS_MESSAGE (req));
-
-       g_signal_connect (req, "restarted",
-                         G_CALLBACK (request_restarted), session);
-
-       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_status_code_handler  (req, SOUP_STATUS_UNAUTHORIZED,
-                                              SOUP_HANDLER_POST_BODY,
-                                              authorize_handler, session);
-       soup_message_add_status_code_handler  (req,
-                                              SOUP_STATUS_PROXY_UNAUTHORIZED,
-                                              SOUP_HANDLER_POST_BODY,
-                                              authorize_handler, session);
+       g_return_if_fail (SOUP_IS_MESSAGE (msg));
 
-       if (!(soup_message_get_flags (req) & SOUP_MESSAGE_NO_REDIRECT)) {
-               soup_message_add_status_class_handler (
-                       req, SOUP_STATUS_CLASS_REDIRECT,
-                       SOUP_HANDLER_POST_BODY,
-                       redirect_handler, session);
-       }
+       SOUP_SESSION_GET_CLASS (session)->queue_message (session, msg,
+                                                        callback, user_data);
+}
 
-       queue_message (session, req, FALSE);
+static void
+requeue_message (SoupSession *session, SoupMessage *msg)
+{
+       msg->status = SOUP_MESSAGE_STATUS_QUEUED;
 }
 
 /**
  * soup_session_requeue_message:
  * @session: a #SoupSession
- * @req: the message to requeue
+ * @msg: the message to requeue
  *
- * This causes @req to be placed back on the queue to be attempted
+ * This causes @msg to be placed back on the queue to be attempted
  * again.
  **/
 void
-soup_session_requeue_message (SoupSession *session, SoupMessage *req)
+soup_session_requeue_message (SoupSession *session, SoupMessage *msg)
 {
        g_return_if_fail (SOUP_IS_SESSION (session));
-       g_return_if_fail (SOUP_IS_MESSAGE (req));
+       g_return_if_fail (SOUP_IS_MESSAGE (msg));
 
-       queue_message (session, req, TRUE);
+       SOUP_SESSION_GET_CLASS (session)->requeue_message (session, msg);
 }
 
 
 /**
  * soup_session_send_message:
  * @session: a #SoupSession
- * @req: the message to send
+ * @msg: the message to send
  * 
- * Synchronously send @req. This call will not return until the
+ * Synchronously send @msg. This call will not return until the
  * transfer is finished successfully or there is an unrecoverable
  * error.
  *
- * @req is not freed upon return.
+ * @msg is not freed upon return.
  *
  * Return value: the HTTP status code of the response
  */
 guint
-soup_session_send_message (SoupSession *session, SoupMessage *req)
+soup_session_send_message (SoupSession *session, SoupMessage *msg)
 {
        g_return_val_if_fail (SOUP_IS_SESSION (session), SOUP_STATUS_MALFORMED);
-       g_return_val_if_fail (SOUP_IS_MESSAGE (req), SOUP_STATUS_MALFORMED);
+       g_return_val_if_fail (SOUP_IS_MESSAGE (msg), SOUP_STATUS_MALFORMED);
 
-       /* Balance out the unref that final_finished will do */
-       g_object_ref (req);
+       return SOUP_SESSION_GET_CLASS (session)->send_message (session, msg);
+}
 
-       soup_session_queue_message (session, req, NULL, NULL);
 
-       while (req->status != SOUP_MESSAGE_STATUS_FINISHED &&
-              !SOUP_STATUS_IS_TRANSPORT_ERROR (req->status_code))
-               g_main_iteration (TRUE);
+static void
+cancel_message (SoupSession *session, SoupMessage *msg)
+{
+       soup_message_queue_remove_message (session->queue, msg);
+       soup_message_finished (msg);
+}
 
-       return req->status_code;
+/**
+ * soup_session_cancel_message:
+ * @session: a #SoupSession
+ * @msg: the message to cancel
+ *
+ * Causes @session to immediately finish processing @msg. You should
+ * set a status code on @msg with soup_message_set_status() before
+ * calling this function.
+ **/
+void
+soup_session_cancel_message (SoupSession *session, SoupMessage *msg)
+{
+       g_return_if_fail (SOUP_IS_SESSION (session));
+       g_return_if_fail (SOUP_IS_MESSAGE (msg));
+
+       SOUP_SESSION_GET_CLASS (session)->cancel_message (session, msg);
 }
 
 /**
@@ -1099,8 +1113,10 @@ soup_session_abort (SoupSession *session)
        SoupMessageQueueIter iter;
        SoupMessage *msg;
 
-       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);
+       g_return_if_fail (SOUP_IS_SESSION (session));
+
+       for (msg = soup_message_queue_first (session->queue, &iter); msg; msg = soup_message_queue_next (session->queue, &iter)) {
+               soup_message_set_status (msg, SOUP_STATUS_CANCELLED);
+               soup_session_cancel_message (session, msg);
        }
 }
index 2f2d9e7..a8f9bf1 100644 (file)
@@ -8,6 +8,7 @@
 
 #include <libsoup/soup-types.h>
 #include <libsoup/soup-message.h>
+#include <libsoup/soup-message-queue.h>
 
 #define SOUP_TYPE_SESSION            (soup_session_get_type ())
 #define SOUP_SESSION(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_SESSION, SoupSession))
@@ -22,6 +23,9 @@ struct SoupSession {
        GObject parent;
 
        SoupSessionPrivate *priv;
+
+       /* protected */
+       SoupMessageQueue *queue;
 };
 
 typedef struct {
@@ -35,6 +39,15 @@ typedef struct {
                                const char *auth_type, const char *auth_realm,
                                char **username, char **password);
 
+       /* methods */
+       void  (*queue_message)   (SoupSession *session, SoupMessage *msg,
+                                 SoupMessageCallbackFn callback,
+                                 gpointer user_data);
+       void  (*requeue_message) (SoupSession *session, SoupMessage *msg);
+       guint (*send_message)    (SoupSession *session, SoupMessage *msg);
+
+       void  (*cancel_message)  (SoupSession *session, SoupMessage *msg);
+
 } SoupSessionClass;
 
 GType soup_session_get_type (void);
@@ -45,21 +58,31 @@ GType soup_session_get_type (void);
 #define SOUP_SESSION_USE_NTLM           "use-ntlm"
 #define SOUP_SESSION_SSL_CA_FILE        "ssl-ca-file"
 
-SoupSession    *soup_session_new              (void);
-SoupSession    *soup_session_new_with_options (const char            *optname1,
-                                              ...);
-
 void            soup_session_queue_message    (SoupSession           *session,
-                                              SoupMessage           *req,
+                                              SoupMessage           *msg,
                                               SoupMessageCallbackFn  callback,
                                               gpointer               user_data);
 void            soup_session_requeue_message  (SoupSession           *session,
-                                              SoupMessage           *req);
+                                              SoupMessage           *msg);
 
 guint           soup_session_send_message     (SoupSession           *session,
-                                              SoupMessage           *req);
+                                              SoupMessage           *msg);
 
+void            soup_session_cancel_message   (SoupSession           *session,
+                                              SoupMessage           *msg);
 void            soup_session_abort            (SoupSession           *session);
 
 
+/* Protected methods */
+SoupConnection *soup_session_get_connection       (SoupSession    *session,
+                                                  SoupMessage    *msg,
+                                                  gboolean       *try_pruning,
+                                                  gboolean       *is_new);
+gboolean        soup_session_try_prune_connection (SoupSession    *session);
+
+void            soup_session_send_message_via     (SoupSession    *session,
+                                                  SoupMessage    *msg,
+                                                  SoupConnection *conn);
+
+
 #endif /* SOUP_SESSION_H */
index 9e6cc9a..ece538f 100644 (file)
@@ -90,11 +90,24 @@ init (GObject *object)
        sock->priv->reuseaddr = TRUE;
 }
 
-static void
+static gboolean
 disconnect_internal (SoupSocket *sock)
 {
-       g_io_channel_unref (sock->priv->iochannel);
+       GIOChannel *iochannel;
+
+       /* If we close the socket from one thread while
+        * reading/writing from another, it's possible that the other
+        * thread will get an I/O error and try to close the socket
+        * while we're still in this function. So we clear
+        * sock->priv->iochannel early to make sure that the other
+        * thread's attempt to close the socket becomes a no-op.
+        */
+       iochannel = sock->priv->iochannel;
        sock->priv->iochannel = NULL;
+       if (iochannel == NULL)
+               return FALSE;
+
+       g_io_channel_unref (iochannel);
 
        if (sock->priv->read_tag) {
                g_source_remove (sock->priv->read_tag);
@@ -108,6 +121,8 @@ disconnect_internal (SoupSocket *sock)
                g_source_remove (sock->priv->error_tag);
                sock->priv->error_tag = 0;
        }
+
+       return TRUE;
 }
 
 static void
@@ -471,9 +486,11 @@ soup_socket_connect (SoupSocket *sock, SoupAddress *remote_addr)
        if (sock->priv->non_blocking) {
                sock->priv->watch = g_idle_add (idle_connect_result, sock);
                return SOUP_STATUS_CONTINUE;
-       } else {
-               return sock->priv->sockfd != -1 ?
-                       SOUP_STATUS_OK : SOUP_STATUS_CANT_CONNECT;
+       } else if (sock->priv->sockfd == -1)
+               return SOUP_STATUS_CANT_CONNECT;
+       else {
+               get_iochannel (sock);
+               return SOUP_STATUS_OK;
        }
 }
 
@@ -723,11 +740,9 @@ soup_socket_disconnect (SoupSocket *sock)
 {
        g_return_if_fail (SOUP_IS_SOCKET (sock));
 
-       if (!sock->priv->iochannel)
+       if (!disconnect_internal (sock))
                return;
 
-       disconnect_internal (sock);
-
        /* Give all readers a chance to notice the connection close */
        g_signal_emit (sock, signals[READABLE], 0);
 
index 17842be..ffdcbba 100644 (file)
@@ -19,6 +19,8 @@ typedef union  SoupServerAuth        SoupServerAuth;
 typedef struct SoupServerAuthContext SoupServerAuthContext;
 typedef struct SoupServerMessage     SoupServerMessage;
 typedef struct SoupSession           SoupSession;
+typedef struct SoupSessionAsync      SoupSessionAsync;
+typedef struct SoupSessionSync       SoupSessionSync;
 typedef struct SoupSocket            SoupSocket;
 typedef struct SoupUri               SoupUri;
 
index a2f4774..da67d87 100644 (file)
@@ -12,7 +12,8 @@ extern "C" {
 
 #include <libsoup/soup-message.h>
 #include <libsoup/soup-misc.h>
-#include <libsoup/soup-session.h>
+#include <libsoup/soup-session-async.h>
+#include <libsoup/soup-session-sync.h>
 #include <libsoup/soup-socket.h>
 #include <libsoup/soup-uri.h>
 
index cb099e2..9d8fc35 100644 (file)
@@ -17,7 +17,6 @@ get_SOURCES = get.c
 simple_httpd_SOURCES = simple-httpd.c
 simple_proxy_SOURCES = simple-proxy.c
 revserver_SOURCES = revserver.c
-revserver_LDFLAGS = `pkg-config --libs gthread-2.0`
 uri_parsing_SOURCES = uri-parsing.c
 
 EXTRA_DIST = libsoup.supp test-cert.pem test-key.pem
index 151a281..65d7cb4 100644 (file)
@@ -37,150 +37,150 @@ typedef struct {
 
 SoupAuthTest tests[] = {
        { "No auth available, should fail",
-         "http://primates.ximian.com/~danw/soup-test/Basic/realm1/index.txt",
+         "http://developer.ximian.com/test/soup-test/Basic/realm1/index.txt",
          "", "0", SOUP_STATUS_UNAUTHORIZED },
 
        { "Should fail with no auth, fail again with bad password, and give up",
-         "http://primates.ximian.com/~danw/soup-test/Basic/realm2/index.txt",
+         "http://developer.ximian.com/test/soup-test/Basic/realm2/index.txt",
          "4", "04", SOUP_STATUS_UNAUTHORIZED },
 
        { "Known realm, auth provided, so should succeed immediately",
-         "http://primates.ximian.com/~danw/soup-test/Basic/realm1/index.txt",
+         "http://developer.ximian.com/test/soup-test/Basic/realm1/index.txt",
          "1", "1", SOUP_STATUS_OK },
 
        { "Now should automatically reuse previous auth",
-         "http://primates.ximian.com/~danw/soup-test/Basic/realm1/index.txt",
+         "http://developer.ximian.com/test/soup-test/Basic/realm1/index.txt",
          "", "1", SOUP_STATUS_OK },
 
        { "Subdir should also automatically reuse auth",
-         "http://primates.ximian.com/~danw/soup-test/Basic/realm1/subdir/index.txt",
+         "http://developer.ximian.com/test/soup-test/Basic/realm1/subdir/index.txt",
          "", "1", SOUP_STATUS_OK },
 
        { "Subdir should retry last auth, but will fail this time",
-         "http://primates.ximian.com/~danw/soup-test/Basic/realm1/realm2/index.txt",
+         "http://developer.ximian.com/test/soup-test/Basic/realm1/realm2/index.txt",
          "", "1", SOUP_STATUS_UNAUTHORIZED },
 
        { "Now should use provided auth on first try",
-         "http://primates.ximian.com/~danw/soup-test/Basic/realm1/realm2/index.txt",
+         "http://developer.ximian.com/test/soup-test/Basic/realm1/realm2/index.txt",
          "2", "2", SOUP_STATUS_OK },
 
        { "Reusing last auth. Should succeed on first try",
-         "http://primates.ximian.com/~danw/soup-test/Basic/realm1/realm2/index.txt",
+         "http://developer.ximian.com/test/soup-test/Basic/realm1/realm2/index.txt",
          "", "2", SOUP_STATUS_OK },
 
        { "Reuse will fail, but 2nd try will succeed because it's a known realm",
-         "http://primates.ximian.com/~danw/soup-test/Basic/realm1/realm2/realm1/index.txt",
+         "http://developer.ximian.com/test/soup-test/Basic/realm1/realm2/realm1/index.txt",
          "", "21", SOUP_STATUS_OK },
 
        { "Should succeed on first try. (Known realm with cached password)",
-         "http://primates.ximian.com/~danw/soup-test/Basic/realm2/index.txt",
+         "http://developer.ximian.com/test/soup-test/Basic/realm2/index.txt",
          "", "2", SOUP_STATUS_OK },
 
        { "Fail once, then use typoed password, then use right password",
-         "http://primates.ximian.com/~danw/soup-test/Basic/realm3/index.txt",
+         "http://developer.ximian.com/test/soup-test/Basic/realm3/index.txt",
          "43", "043", SOUP_STATUS_OK },
 
 
        { "No auth available, should fail",
-         "http://primates.ximian.com/~danw/soup-test/Digest/realm1/index.txt",
+         "http://developer.ximian.com/test/soup-test/Digest/realm1/index.txt",
          "", "0", SOUP_STATUS_UNAUTHORIZED },
 
        { "Should fail with no auth, fail again with bad password, and give up",
-         "http://primates.ximian.com/~danw/soup-test/Digest/realm2/index.txt",
+         "http://developer.ximian.com/test/soup-test/Digest/realm2/index.txt",
          "4", "04", SOUP_STATUS_UNAUTHORIZED },
 
        { "Known realm, auth provided, so should succeed immediately",
-         "http://primates.ximian.com/~danw/soup-test/Digest/realm1/index.txt",
+         "http://developer.ximian.com/test/soup-test/Digest/realm1/index.txt",
          "1", "1", SOUP_STATUS_OK },
 
        { "Now should automatically reuse previous auth",
-         "http://primates.ximian.com/~danw/soup-test/Digest/realm1/index.txt",
+         "http://developer.ximian.com/test/soup-test/Digest/realm1/index.txt",
          "", "1", SOUP_STATUS_OK },
 
        { "Subdir should also automatically reuse auth",
-         "http://primates.ximian.com/~danw/soup-test/Digest/realm1/subdir/index.txt",
+         "http://developer.ximian.com/test/soup-test/Digest/realm1/subdir/index.txt",
          "", "1", SOUP_STATUS_OK },
 
        { "Subdir should retry last auth, but will fail this time",
-         "http://primates.ximian.com/~danw/soup-test/Digest/realm1/realm2/index.txt",
+         "http://developer.ximian.com/test/soup-test/Digest/realm1/realm2/index.txt",
          "", "1", SOUP_STATUS_UNAUTHORIZED },
 
        { "Now should use provided auth on first try",
-         "http://primates.ximian.com/~danw/soup-test/Digest/realm1/realm2/index.txt",
+         "http://developer.ximian.com/test/soup-test/Digest/realm1/realm2/index.txt",
          "2", "2", SOUP_STATUS_OK },
 
        { "Reusing last auth. Should succeed on first try",
-         "http://primates.ximian.com/~danw/soup-test/Digest/realm1/realm2/index.txt",
+         "http://developer.ximian.com/test/soup-test/Digest/realm1/realm2/index.txt",
          "", "2", SOUP_STATUS_OK },
 
        { "Should succeed on first try because of earlier domain directive",
-         "http://primates.ximian.com/~danw/soup-test/Digest/realm1/realm2/realm1/index.txt",
+         "http://developer.ximian.com/test/soup-test/Digest/realm1/realm2/realm1/index.txt",
          "", "1", SOUP_STATUS_OK },
 
        { "Should succeed on first try. (Known realm with cached password)",
-         "http://primates.ximian.com/~danw/soup-test/Digest/realm2/index.txt",
+         "http://developer.ximian.com/test/soup-test/Digest/realm2/index.txt",
          "", "2", SOUP_STATUS_OK },
 
        { "Fail once, then use typoed password, then use right password",
-         "http://primates.ximian.com/~danw/soup-test/Digest/realm3/index.txt",
+         "http://developer.ximian.com/test/soup-test/Digest/realm3/index.txt",
          "43", "043", SOUP_STATUS_OK },
 
 
        { "Make sure we haven't forgotten anything",
-         "http://primates.ximian.com/~danw/soup-test/Basic/realm1/index.txt",
+         "http://developer.ximian.com/test/soup-test/Basic/realm1/index.txt",
          "", "1", SOUP_STATUS_OK },
 
        { "Make sure we haven't forgotten anything",
-         "http://primates.ximian.com/~danw/soup-test/Basic/realm1/realm2/index.txt",
+         "http://developer.ximian.com/test/soup-test/Basic/realm1/realm2/index.txt",
          "", "2", SOUP_STATUS_OK },
 
        { "Make sure we haven't forgotten anything",
-         "http://primates.ximian.com/~danw/soup-test/Basic/realm1/realm2/realm1/index.txt",
+         "http://developer.ximian.com/test/soup-test/Basic/realm1/realm2/realm1/index.txt",
          "", "1", SOUP_STATUS_OK },
 
        { "Make sure we haven't forgotten anything",
-         "http://primates.ximian.com/~danw/soup-test/Basic/realm2/index.txt",
+         "http://developer.ximian.com/test/soup-test/Basic/realm2/index.txt",
          "", "2", SOUP_STATUS_OK },
 
        { "Make sure we haven't forgotten anything",
-         "http://primates.ximian.com/~danw/soup-test/Basic/realm3/index.txt",
+         "http://developer.ximian.com/test/soup-test/Basic/realm3/index.txt",
          "", "3", SOUP_STATUS_OK },
 
 
        { "Make sure we haven't forgotten anything",
-         "http://primates.ximian.com/~danw/soup-test/Digest/realm1/index.txt",
+         "http://developer.ximian.com/test/soup-test/Digest/realm1/index.txt",
          "", "1", SOUP_STATUS_OK },
 
        { "Make sure we haven't forgotten anything",
-         "http://primates.ximian.com/~danw/soup-test/Digest/realm1/realm2/index.txt",
+         "http://developer.ximian.com/test/soup-test/Digest/realm1/realm2/index.txt",
          "", "2", SOUP_STATUS_OK },
 
        { "Make sure we haven't forgotten anything",
-         "http://primates.ximian.com/~danw/soup-test/Digest/realm1/realm2/realm1/index.txt",
+         "http://developer.ximian.com/test/soup-test/Digest/realm1/realm2/realm1/index.txt",
          "", "1", SOUP_STATUS_OK },
 
        { "Make sure we haven't forgotten anything",
-         "http://primates.ximian.com/~danw/soup-test/Digest/realm2/index.txt",
+         "http://developer.ximian.com/test/soup-test/Digest/realm2/index.txt",
          "", "2", SOUP_STATUS_OK },
 
        { "Make sure we haven't forgotten anything",
-         "http://primates.ximian.com/~danw/soup-test/Digest/realm3/index.txt",
+         "http://developer.ximian.com/test/soup-test/Digest/realm3/index.txt",
          "", "3", SOUP_STATUS_OK },
 
        { "Now the server will reject the formerly-good password",
-         "http://primates.ximian.com/~danw/soup-test/Basic/realm1/not/index.txt",
+         "http://developer.ximian.com/test/soup-test/Basic/realm1/not/index.txt",
          "1" /* should not be used */, "1", SOUP_STATUS_UNAUTHORIZED },
 
        { "Make sure we've forgotten it",
-         "http://primates.ximian.com/~danw/soup-test/Basic/realm1/index.txt",
+         "http://developer.ximian.com/test/soup-test/Basic/realm1/index.txt",
          "", "0", SOUP_STATUS_UNAUTHORIZED },
 
        { "Likewise, reject the formerly-good Digest password",
-         "http://primates.ximian.com/~danw/soup-test/Digest/realm1/not/index.txt",
+         "http://developer.ximian.com/test/soup-test/Digest/realm1/not/index.txt",
          "1" /* should not be used */, "1", SOUP_STATUS_UNAUTHORIZED },
 
        { "Make sure we've forgotten it",
-         "http://primates.ximian.com/~danw/soup-test/Digest/realm1/index.txt",
+         "http://developer.ximian.com/test/soup-test/Digest/realm1/index.txt",
          "", "0", SOUP_STATUS_UNAUTHORIZED }
 };
 int ntests = sizeof (tests) / sizeof (tests[0]);
@@ -283,8 +283,9 @@ main (int argc, char **argv)
        int i;
 
        g_type_init ();
+       g_thread_init (NULL);
 
-       session = soup_session_new ();
+       session = soup_session_async_new ();
        g_signal_connect (session, "authenticate",
                          G_CALLBACK (authenticate), &i);
        g_signal_connect (session, "reauthenticate",
index d3f4928..6956652 100644 (file)
@@ -208,6 +208,7 @@ main (int argc, char **argv)
        int opt;
 
        g_type_init ();
+       g_thread_init (NULL);
 
        while ((opt = getopt (argc, argv, "c:r")) != -1) {
                switch (opt) {
@@ -236,7 +237,7 @@ main (int argc, char **argv)
                exit (1);
        }
 
-       session = soup_session_new_with_options (
+       session = soup_session_async_new_with_options (
                SOUP_SESSION_SSL_CA_FILE, cafile,
                NULL);
 
index fafebcc..79044ef 100644 (file)
@@ -160,7 +160,7 @@ main (int argc, char **argv)
                soup_server_get_port (server));
        soup_server_run_async (server);
 
-       session = soup_session_new ();
+       session = soup_session_async_new ();
 
        printf ("\nWaiting for requests...\n");