libsoup r1110 - in trunk: . libsoup tests
- From: danw svn gnome org
- To: svn-commits-list gnome org
- Subject: libsoup r1110 - in trunk: . libsoup tests
- Date: Sun, 16 Mar 2008 02:28:37 +0000 (GMT)
Author: danw
Date: Sun Mar 16 02:28:36 2008
New Revision: 1110
URL: http://svn.gnome.org/viewvc/libsoup?rev=1110&view=rev
Log:
* libsoup/soup-session.c: Define two new signals, request_queued
and request_unqueued, to provided a clearer (and
clearly-documented) lifecycle for messages, helping us (and other
people) avoid bugs like #522601, SoupSession::authenticate signal
emitted multiple times per message (reported and analyzed by Tommi
Komulainen).
* libsoup/soup-logger.c:
* libsoup/soup-auth-manager.c:
* libsoup/soup-auth-manager-ntlm.c: Use request_queued/unqueued
* tests/auth-test.c (do_async_auth_test): add a regression test
Modified:
trunk/ChangeLog
trunk/libsoup/soup-auth-manager-ntlm.c
trunk/libsoup/soup-auth-manager.c
trunk/libsoup/soup-logger.c
trunk/libsoup/soup-session.c
trunk/tests/auth-test.c
Modified: trunk/libsoup/soup-auth-manager-ntlm.c
==============================================================================
--- trunk/libsoup/soup-auth-manager-ntlm.c (original)
+++ trunk/libsoup/soup-auth-manager-ntlm.c Sun Mar 16 02:28:36 2008
@@ -45,8 +45,12 @@
GHashTable *connections_by_id;
};
+static void ntlm_request_queued (SoupSession *session, SoupMessage *msg,
+ gpointer ntlm);
static void ntlm_request_started (SoupSession *session, SoupMessage *msg,
SoupSocket *socket, gpointer ntlm);
+static void ntlm_request_unqueued (SoupSession *session, SoupMessage *msg,
+ gpointer ntlm);
static char *soup_ntlm_request (void);
static gboolean soup_ntlm_parse_challenge (const char *challenge,
@@ -67,8 +71,12 @@
ntlm->session = session;
ntlm->connections_by_id = g_hash_table_new (NULL, NULL);
ntlm->connections_by_msg = g_hash_table_new (NULL, NULL);
+ g_signal_connect (session, "request_queued",
+ G_CALLBACK (ntlm_request_queued), ntlm);
g_signal_connect (session, "request_started",
G_CALLBACK (ntlm_request_started), ntlm);
+ g_signal_connect (session, "request_unqueued",
+ G_CALLBACK (ntlm_request_unqueued), ntlm);
return ntlm;
}
@@ -97,7 +105,11 @@
g_hash_table_destroy (ntlm->connections_by_id);
g_hash_table_destroy (ntlm->connections_by_msg);
g_signal_handlers_disconnect_by_func (ntlm->session,
+ ntlm_request_queued, ntlm);
+ g_signal_handlers_disconnect_by_func (ntlm->session,
ntlm_request_started, ntlm);
+ g_signal_handlers_disconnect_by_func (ntlm->session,
+ ntlm_request_unqueued, ntlm);
g_slice_free (SoupAuthManagerNTLM, ntlm);
}
@@ -253,13 +265,16 @@
}
static void
-ntlm_cleanup_msg (SoupMessage *msg, gpointer ntlm)
+ntlm_request_queued (SoupSession *session, SoupMessage *msg, gpointer ntlm)
{
- /* Do this when the message is restarted, in case it's
- * restarted on a different connection.
- */
- g_signal_handlers_disconnect_by_func (msg, ntlm_authorize_pre, ntlm);
- g_signal_handlers_disconnect_by_func (msg, ntlm_authorize_post, ntlm);
+ soup_message_add_status_code_handler (msg, "got_headers",
+ SOUP_STATUS_UNAUTHORIZED,
+ G_CALLBACK (ntlm_authorize_pre),
+ ntlm);
+ soup_message_add_status_code_handler (msg, "got_body",
+ SOUP_STATUS_UNAUTHORIZED,
+ G_CALLBACK (ntlm_authorize_post),
+ ntlm);
}
static void
@@ -292,20 +307,14 @@
"Authorization", header);
g_free (header);
}
+}
- soup_message_add_status_code_handler (msg, "got_headers",
- SOUP_STATUS_UNAUTHORIZED,
- G_CALLBACK (ntlm_authorize_pre),
- ntlm);
- soup_message_add_status_code_handler (msg, "got_body",
- SOUP_STATUS_UNAUTHORIZED,
- G_CALLBACK (ntlm_authorize_post),
- ntlm);
- g_signal_connect (msg, "restarted",
- G_CALLBACK (ntlm_cleanup_msg), ntlm);
- g_signal_connect (msg, "finished",
- G_CALLBACK (ntlm_cleanup_msg), ntlm);
-
+static void
+ntlm_request_unqueued (SoupSession *session, SoupMessage *msg,
+ gpointer ntlm)
+{
+ g_signal_handlers_disconnect_by_func (msg, ntlm_authorize_pre, ntlm);
+ g_signal_handlers_disconnect_by_func (msg, ntlm_authorize_post, ntlm);
}
Modified: trunk/libsoup/soup-auth-manager.c
==============================================================================
--- trunk/libsoup/soup-auth-manager.c (original)
+++ trunk/libsoup/soup-auth-manager.c Sun Mar 16 02:28:36 2008
@@ -19,8 +19,12 @@
#include "soup-session-private.h"
#include "soup-uri.h"
+static void session_request_queued (SoupSession *session, SoupMessage *msg,
+ gpointer data);
static void session_request_started (SoupSession *session, SoupMessage *msg,
SoupSocket *socket, gpointer data);
+static void session_request_unqueued (SoupSession *session, SoupMessage *msg,
+ gpointer data);
struct SoupAuthManager {
SoupSession *session;
@@ -53,8 +57,12 @@
manager->auth_hosts = g_hash_table_new (soup_uri_host_hash,
soup_uri_host_equal);
+ g_signal_connect (session, "request_queued",
+ G_CALLBACK (session_request_queued), manager);
g_signal_connect (session, "request_started",
G_CALLBACK (session_request_started), manager);
+ g_signal_connect (session, "request_unqueued",
+ G_CALLBACK (session_request_unqueued), manager);
return manager;
}
@@ -81,7 +89,13 @@
g_signal_handlers_disconnect_by_func (
manager->session,
+ G_CALLBACK (session_request_queued), manager);
+ g_signal_handlers_disconnect_by_func (
+ manager->session,
G_CALLBACK (session_request_started), manager);
+ g_signal_handlers_disconnect_by_func (
+ manager->session,
+ G_CALLBACK (session_request_unqueued), manager);
for (i = 0; i < manager->auth_types->len; i++)
g_type_class_unref (manager->auth_types->pdata[i]);
@@ -431,16 +445,11 @@
}
static void
-session_request_started (SoupSession *session, SoupMessage *msg,
- SoupSocket *socket, gpointer data)
+session_request_queued (SoupSession *session, SoupMessage *msg,
+ gpointer data)
{
SoupAuthManager *manager = data;
- SoupAuth *auth;
- auth = lookup_auth (manager, msg);
- if (!auth || !authenticate_auth (manager, auth, msg, FALSE, FALSE))
- auth = NULL;
- soup_message_set_auth (msg, auth);
soup_message_add_status_code_handler (
msg, "got_headers", SOUP_STATUS_UNAUTHORIZED,
G_CALLBACK (update_auth), manager);
@@ -448,10 +457,6 @@
msg, "got_body", SOUP_STATUS_UNAUTHORIZED,
G_CALLBACK (requeue_if_authenticated), manager);
- auth = manager->proxy_auth;
- if (!auth || !authenticate_auth (manager, auth, msg, FALSE, TRUE))
- auth = NULL;
- soup_message_set_proxy_auth (msg, auth);
soup_message_add_status_code_handler (
msg, "got_headers", SOUP_STATUS_PROXY_UNAUTHORIZED,
G_CALLBACK (update_proxy_auth), manager);
@@ -459,3 +464,31 @@
msg, "got_body", SOUP_STATUS_PROXY_UNAUTHORIZED,
G_CALLBACK (requeue_if_proxy_authenticated), manager);
}
+
+static void
+session_request_started (SoupSession *session, SoupMessage *msg,
+ SoupSocket *socket, gpointer data)
+{
+ SoupAuthManager *manager = data;
+ SoupAuth *auth;
+
+ auth = lookup_auth (manager, msg);
+ if (!auth || !authenticate_auth (manager, auth, msg, FALSE, FALSE))
+ auth = NULL;
+ soup_message_set_auth (msg, auth);
+
+ auth = manager->proxy_auth;
+ if (!auth || !authenticate_auth (manager, auth, msg, FALSE, TRUE))
+ auth = NULL;
+ soup_message_set_proxy_auth (msg, auth);
+}
+
+static void
+session_request_unqueued (SoupSession *session, SoupMessage *msg,
+ gpointer data)
+{
+ SoupAuthManager *manager = data;
+
+ g_signal_handlers_disconnect_matched (msg, G_SIGNAL_MATCH_DATA,
+ 0, 0, NULL, NULL, manager);
+}
Modified: trunk/libsoup/soup-logger.c
==============================================================================
--- trunk/libsoup/soup-logger.c (original)
+++ trunk/libsoup/soup-logger.c Sun Mar 16 02:28:36 2008
@@ -321,8 +321,12 @@
g_object_set_qdata (object, priv->tag, NULL);
}
+static void request_queued (SoupSession *session, SoupMessage *msg,
+ gpointer user_data);
static void request_started (SoupSession *session, SoupMessage *msg,
SoupSocket *socket, gpointer user_data);
+static void request_unqueued (SoupSession *session, SoupMessage *msg,
+ gpointer user_data);
static void
weak_notify_unref (gpointer logger, GObject *ex_session)
@@ -348,8 +352,12 @@
{
if (!soup_logger_get_id (logger, session))
soup_logger_set_id (logger, session);
+ g_signal_connect (session, "request_queued",
+ G_CALLBACK (request_queued), logger);
g_signal_connect (session, "request_started",
G_CALLBACK (request_started), logger);
+ g_signal_connect (session, "request_unqueued",
+ G_CALLBACK (request_unqueued), logger);
g_object_weak_ref (G_OBJECT (session),
weak_notify_unref, g_object_ref (logger));
@@ -366,7 +374,9 @@
soup_logger_detach (SoupLogger *logger,
SoupSession *session)
{
+ g_signal_handlers_disconnect_by_func (session, request_queued, logger);
g_signal_handlers_disconnect_by_func (session, request_started, logger);
+ g_signal_handlers_disconnect_by_func (session, request_unqueued, logger);
g_object_weak_unref (G_OBJECT (session),
weak_notify_unref, logger);
@@ -600,15 +610,16 @@
}
static void
-finished_handler (SoupMessage *msg, gpointer user_data)
+request_queued (SoupSession *session, SoupMessage *msg, gpointer user_data)
{
SoupLogger *logger = user_data;
- g_signal_handlers_disconnect_by_func (msg, got_informational, logger);
- g_signal_handlers_disconnect_by_func (msg, got_body, logger);
- g_signal_handlers_disconnect_by_func (msg, finished_handler, logger);
-
- soup_logger_clear_id (logger, msg);
+ g_signal_connect (msg, "got-informational",
+ G_CALLBACK (got_informational),
+ logger);
+ g_signal_connect (msg, "got-body",
+ G_CALLBACK (got_body),
+ logger);
}
static void
@@ -623,18 +634,8 @@
if (msg_id)
restarted = TRUE;
else {
- msg_id = soup_logger_set_id (logger, msg);
+ soup_logger_set_id (logger, msg);
restarted = FALSE;
-
- g_signal_connect (msg, "got-informational",
- G_CALLBACK (got_informational),
- logger);
- g_signal_connect (msg, "got-body",
- G_CALLBACK (got_body),
- logger);
- g_signal_connect (msg, "finished",
- G_CALLBACK (finished_handler),
- logger);
}
if (!soup_logger_get_id (logger, socket))
@@ -643,3 +644,14 @@
print_request (logger, msg, session, socket, restarted);
soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL, ' ', "");
}
+
+static void
+request_unqueued (SoupSession *session, SoupMessage *msg, gpointer user_data)
+{
+ SoupLogger *logger = user_data;
+
+ g_signal_handlers_disconnect_by_func (msg, got_informational, logger);
+ g_signal_handlers_disconnect_by_func (msg, got_body, logger);
+
+ soup_logger_clear_id (logger, msg);
+}
Modified: trunk/libsoup/soup-session.c
==============================================================================
--- trunk/libsoup/soup-session.c (original)
+++ trunk/libsoup/soup-session.c Sun Mar 16 02:28:36 2008
@@ -121,7 +121,9 @@
G_DEFINE_TYPE (SoupSession, soup_session, G_TYPE_OBJECT)
enum {
+ REQUEST_QUEUED,
REQUEST_STARTED,
+ REQUEST_UNQUEUED,
AUTHENTICATE,
LAST_SIGNAL
};
@@ -255,12 +257,65 @@
/* signals */
/**
+ * SoupSession::request-queued:
+ * @session: the session
+ * @msg: the request that was queued
+ *
+ * Emitted when a request is queued on @session. (Note that
+ * "queued" doesn't just mean soup_session_queue_message();
+ * soup_session_send_message() implicitly queues the message
+ * as well.)
+ *
+ * When sending a request, first #SoupSession::request_queued
+ * is emitted, indicating that the session has become aware of
+ * the request.
+ *
+ * Once a connection is available to send the request on, the
+ * session emits #SoupSession::request_started. Then, various
+ * #SoupMessage signals are emitted as the message is
+ * processed. If the message is requeued, it will emit
+ * #SoupMessage::restarted, which will then be followed by
+ * another #SoupSession::request_started and another set of
+ * #SoupMessage signals when the message is re-sent.
+ *
+ * Eventually, the message will emit #SoupMessage::finished.
+ * Normally, this signals the completion of message
+ * processing. However, it is possible that the application
+ * will requeue the message from the "finished" handler (or
+ * equivalently, from the soup_session_queue_message()
+ * callback). In that case, the process will loop back to
+ * #SoupSession::request_started.
+ *
+ * Eventually, a message will reach "finished" and not be
+ * requeued. At that point, the session will emit
+ * #SoupSession::request_unqueued to indicate that it is done
+ * with the message.
+ *
+ * To sum up: #SoupSession::request_queued and
+ * #SoupSession::request_unqueued are guaranteed to be emitted
+ * exactly once, but #SoupSession::request_started and
+ * #SoupMessage::finished (and all of the other #SoupMessage
+ * signals) may be invoked multiple times for a given message.
+ **/
+ signals[REQUEST_QUEUED] =
+ g_signal_new ("request-queued",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0, /* FIXME? */
+ NULL, NULL,
+ soup_marshal_NONE__OBJECT,
+ G_TYPE_NONE, 1,
+ SOUP_TYPE_MESSAGE);
+
+ /**
* SoupSession::request-started:
* @session: the session
* @msg: the request being sent
* @socket: the socket the request is being sent on
*
- * Emitted just before a request is sent.
+ * Emitted just before a request is sent. See
+ * #SoupSession::request_queued for a detailed description of
+ * the message lifecycle within a session.
**/
signals[REQUEST_STARTED] =
g_signal_new ("request-started",
@@ -274,6 +329,26 @@
SOUP_TYPE_SOCKET);
/**
+ * SoupSession::request-unqueued:
+ * @session: the session
+ * @msg: the request that was unqueued
+ *
+ * Emitted when a request is removed from @session's queue,
+ * indicating that @session is done with it. See
+ * #SoupSession::request_queued for a detailed description of the
+ * message lifecycle within a session.
+ **/
+ signals[REQUEST_UNQUEUED] =
+ g_signal_new ("request-unqueued",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0, /* FIXME? */
+ NULL, NULL,
+ soup_marshal_NONE__OBJECT,
+ G_TYPE_NONE, 1,
+ SOUP_TYPE_MESSAGE);
+
+ /**
* SoupSession::authenticate:
* @session: the session
* @msg: the #SoupMessage being sent
@@ -716,6 +791,12 @@
"User-Agent", priv->user_agent);
}
+ /* Kludge to deal with the fact that CONNECT msgs come from the
+ * SoupConnection rather than being queued normally.
+ */
+ if (msg->method == SOUP_METHOD_CONNECT)
+ g_signal_emit (session, signals[REQUEST_QUEUED], 0, msg);
+
g_signal_emit (session, signals[REQUEST_STARTED], 0,
msg, soup_connection_get_socket (conn));
}
@@ -984,6 +1065,8 @@
if (!SOUP_MESSAGE_IS_STARTING (msg)) {
soup_message_queue_remove_message (priv->queue, msg);
g_signal_handlers_disconnect_by_func (msg, message_finished, session);
+ g_signal_handlers_disconnect_by_func (msg, redirect_handler, session);
+ g_signal_emit (session, signals[REQUEST_UNQUEUED], 0, msg);
}
}
@@ -1004,6 +1087,8 @@
soup_message_set_io_status (msg, SOUP_MESSAGE_IO_STATUS_QUEUED);
soup_message_queue_append (priv->queue, msg);
+
+ g_signal_emit (session, signals[REQUEST_QUEUED], 0, msg);
}
/**
Modified: trunk/tests/auth-test.c
==============================================================================
--- trunk/tests/auth-test.c (original)
+++ trunk/tests/auth-test.c Sun Mar 16 02:28:36 2008
@@ -398,6 +398,24 @@
}
static void
+async_authenticate_522601 (SoupSession *session, SoupMessage *msg,
+ SoupAuth *auth, gboolean retrying, gpointer data)
+{
+ gboolean *been_here = data;
+
+ debug_printf (2, " async_authenticate_522601\n");
+
+ if (*been_here) {
+ debug_printf (1, " ERROR: async_authenticate_522601 called twice\n");
+ errors++;
+ }
+ *been_here = TRUE;
+
+ soup_session_pause_message (session, msg);
+ g_main_loop_quit (loop);
+}
+
+static void
do_async_auth_test (const char *base_uri)
{
SoupSession *session;
@@ -406,6 +424,7 @@
char *uri;
SoupAuth *auth = NULL;
int finished = 0;
+ gboolean been_there = FALSE;
debug_printf (1, "\nTesting async auth:\n");
@@ -457,6 +476,7 @@
/* Now do the auth, and restart */
if (auth) {
soup_auth_authenticate (auth, "user1", "realm1");
+ g_object_unref (auth);
soup_session_unpause_message (session, msg1);
soup_session_unpause_message (session, msg3);
@@ -490,7 +510,38 @@
g_object_unref (msg3);
memcpy (msg2, &msg2_bak, sizeof (SoupMessage));
g_object_unref (msg2);
+
+ /* Test that giving the wrong password doesn't cause multiple
+ * authenticate signals the second time.
+ */
+ debug_printf (1, "\nTesting async auth with wrong password (#522601):\n");
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
+ auth = NULL;
+
+ msg1 = soup_message_new ("GET", uri);
+ g_object_set_data (G_OBJECT (msg1), "id", GINT_TO_POINTER (1));
+ auth_id = g_signal_connect (session, "authenticate",
+ G_CALLBACK (async_authenticate), &auth);
+ g_object_ref (msg1);
+ soup_session_queue_message (session, msg1, async_finished, &finished);
+ g_main_loop_run (loop);
+ g_signal_handler_disconnect (session, auth_id);
+ soup_auth_authenticate (auth, "user1", "wrong");
g_object_unref (auth);
+ soup_session_unpause_message (session, msg1);
+
+ auth_id = g_signal_connect (session, "authenticate",
+ G_CALLBACK (async_authenticate_522601),
+ &been_there);
+ g_main_loop_run (loop);
+ g_signal_handler_disconnect (session, auth_id);
+
+ soup_session_abort (session);
+ g_object_unref (session);
+
+ g_object_unref (msg1);
+
g_free (uri);
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]