diff options
author | Dan Winship <danw@src.gnome.org> | 2003-12-17 19:49:14 +0000 |
---|---|---|
committer | Dan Winship <danw@src.gnome.org> | 2003-12-17 19:49:14 +0000 |
commit | c2720d6770639fed2bf57a972fa7c9b47919d0ac (patch) | |
tree | d00e278329a0e3f3053f556099a3eb518a6a8c02 | |
parent | de3cf1fc96532d10cdd1d6e55aea3c4f81501579 (diff) | |
download | libsoup-c2720d6770639fed2bf57a972fa7c9b47919d0ac.tar.gz |
Add gthread to glib check
* 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().
-rw-r--r-- | ChangeLog | 42 | ||||
-rw-r--r-- | configure.in | 2 | ||||
-rw-r--r-- | libsoup/Makefile.am | 4 | ||||
-rw-r--r-- | libsoup/soup-connection.c | 21 | ||||
-rw-r--r-- | libsoup/soup-connection.h | 1 | ||||
-rw-r--r-- | libsoup/soup-dns.c | 4 | ||||
-rw-r--r-- | libsoup/soup-message-queue.c | 103 | ||||
-rw-r--r-- | libsoup/soup-message.c | 31 | ||||
-rw-r--r-- | libsoup/soup-message.h | 2 | ||||
-rw-r--r-- | libsoup/soup-session-async.c | 213 | ||||
-rw-r--r-- | libsoup/soup-session-async.h | 39 | ||||
-rw-r--r-- | libsoup/soup-session-sync.c | 197 | ||||
-rw-r--r-- | libsoup/soup-session-sync.h | 39 | ||||
-rw-r--r-- | libsoup/soup-session.c | 552 | ||||
-rw-r--r-- | libsoup/soup-session.h | 37 | ||||
-rw-r--r-- | libsoup/soup-socket.c | 31 | ||||
-rw-r--r-- | libsoup/soup-types.h | 2 | ||||
-rw-r--r-- | libsoup/soup.h | 3 | ||||
-rw-r--r-- | tests/Makefile.am | 1 | ||||
-rw-r--r-- | tests/auth-test.c | 75 | ||||
-rw-r--r-- | tests/get.c | 3 | ||||
-rw-r--r-- | tests/simple-proxy.c | 2 |
22 files changed, 1018 insertions, 386 deletions
@@ -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): diff --git a/configure.in b/configure.in index d0454895..8215a25c 100644 --- a/configure.in +++ b/configure.in @@ -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) diff --git a/libsoup/Makefile.am b/libsoup/Makefile.am index 2781ff8d..e49a43e0 100644 --- a/libsoup/Makefile.am +++ b/libsoup/Makefile.am @@ -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 \ diff --git a/libsoup/soup-connection.c b/libsoup/soup-connection.c index c37f813a..67bbfca3 100644 --- a/libsoup/soup-connection.c +++ b/libsoup/soup-connection.c @@ -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) diff --git a/libsoup/soup-connection.h b/libsoup/soup-connection.h index 2d88b5a9..d25b6a92 100644 --- a/libsoup/soup-connection.h +++ b/libsoup/soup-connection.h @@ -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, diff --git a/libsoup/soup-dns.c b/libsoup/soup-dns.c index a3fed01e..1789abba 100644 --- a/libsoup/soup-dns.c +++ b/libsoup/soup-dns.c @@ -613,9 +613,9 @@ check_hostent (SoupDNSEntry *entry, gboolean block) } if (block) - tvp = &tv; - else tvp = NULL; + else + tvp = &tv; do { FD_ZERO (&readfds); diff --git a/libsoup/soup-message-queue.c b/libsoup/soup-message-queue.c index 0bf1f7d5..9b89381e 100644 --- a/libsoup/soup-message-queue.c +++ b/libsoup/soup-message-queue.c @@ -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); } diff --git a/libsoup/soup-message.c b/libsoup/soup-message.c index fe6edf8e..6d858259 100644 --- a/libsoup/soup-message.c +++ b/libsoup/soup-message.c @@ -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 diff --git a/libsoup/soup-message.h b/libsoup/soup-message.h index 89a1753c..f72fb7ad 100644 --- a/libsoup/soup-message.h +++ b/libsoup/soup-message.h @@ -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 index 00000000..2d073a19 --- /dev/null +++ b/libsoup/soup-session-async.c @@ -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 index 00000000..26f5fe75 --- /dev/null +++ b/libsoup/soup-session-async.h @@ -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 index 00000000..5c078041 --- /dev/null +++ b/libsoup/soup-session-sync.c @@ -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 index 00000000..87652791 --- /dev/null +++ b/libsoup/soup-session-sync.h @@ -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 */ diff --git a/libsoup/soup-session.c b/libsoup/soup-session.c index bc4d79c1..65da4641 100644 --- a/libsoup/soup-session.c +++ b/libsoup/soup-session.c @@ -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); } } diff --git a/libsoup/soup-session.h b/libsoup/soup-session.h index 2f2d9e72..a8f9bf15 100644 --- a/libsoup/soup-session.h +++ b/libsoup/soup-session.h @@ -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 */ diff --git a/libsoup/soup-socket.c b/libsoup/soup-socket.c index 9e6cc9a7..ece538fc 100644 --- a/libsoup/soup-socket.c +++ b/libsoup/soup-socket.c @@ -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); diff --git a/libsoup/soup-types.h b/libsoup/soup-types.h index 17842be7..ffdcbba4 100644 --- a/libsoup/soup-types.h +++ b/libsoup/soup-types.h @@ -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; diff --git a/libsoup/soup.h b/libsoup/soup.h index a2f4774e..da67d871 100644 --- a/libsoup/soup.h +++ b/libsoup/soup.h @@ -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> diff --git a/tests/Makefile.am b/tests/Makefile.am index cb099e20..9d8fc350 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -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 diff --git a/tests/auth-test.c b/tests/auth-test.c index 151a2818..65d7cb4d 100644 --- a/tests/auth-test.c +++ b/tests/auth-test.c @@ -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", diff --git a/tests/get.c b/tests/get.c index d3f4928e..69566521 100644 --- a/tests/get.c +++ b/tests/get.c @@ -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); diff --git a/tests/simple-proxy.c b/tests/simple-proxy.c index fafebccd..79044efa 100644 --- a/tests/simple-proxy.c +++ b/tests/simple-proxy.c @@ -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"); |