[libsoup/carlosgc/authenticate: 2/6] Move authenticate signal from SoupSession to SoupMessage




commit fe65b44a6a860de05d29eb71ba826cb7134e57e2
Author: Carlos Garcia Campos <cgarcia igalia com>
Date:   Mon Oct 26 10:20:01 2020 +0100

    Move authenticate signal from SoupSession to SoupMessage
    
    In case of tunneling the authenticate signal is emitted in the original
    message, since the tunneling message is an internal implementation
    detail. The authenticate signal is now true handled, and SoupAuthManager
    automatically pauses and unpauses the message when handled
    asynchronously. soup_auth_cancel() has been added to complete an async
    authentication when no credentials are provided.

 docs/reference/libsoup-3.0-sections.txt |   2 +
 libsoup/auth/soup-auth-manager.c        |  37 +++-
 libsoup/auth/soup-auth.c                | 100 ++++++++++
 libsoup/auth/soup-auth.h                |   9 +
 libsoup/soup-message-private.h          |   3 +
 libsoup/soup-message.c                  |  46 +++++
 libsoup/soup-message.h                  |   3 +
 libsoup/soup-session-private.h          |   2 +
 libsoup/soup-session.c                  |  72 +++-----
 tests/auth-test.c                       | 311 +++++++++++++++++---------------
 tests/misc-test.c                       |  18 +-
 tests/ntlm-test.c                       |  85 +++++----
 tests/proxy-test.c                      |  33 ++--
 tests/pull-api-test.c                   |  21 ++-
 14 files changed, 479 insertions(+), 263 deletions(-)
---
diff --git a/docs/reference/libsoup-3.0-sections.txt b/docs/reference/libsoup-3.0-sections.txt
index 3c333970..9233c069 100644
--- a/docs/reference/libsoup-3.0-sections.txt
+++ b/docs/reference/libsoup-3.0-sections.txt
@@ -519,6 +519,8 @@ soup_auth_authenticate
 soup_auth_can_authenticate
 soup_auth_is_authenticated
 soup_auth_is_ready
+soup_auth_cancel
+soup_auth_is_cancelled
 <SUBSECTION>
 soup_auth_get_authorization
 soup_auth_get_protection_space
diff --git a/libsoup/auth/soup-auth-manager.c b/libsoup/auth/soup-auth-manager.c
index fad393f1..60494909 100644
--- a/libsoup/auth/soup-auth-manager.c
+++ b/libsoup/auth/soup-auth-manager.c
@@ -507,6 +507,21 @@ lookup_proxy_auth (SoupAuthManagerPrivate *priv, SoupMessage *msg)
        return priv->proxy_auth;
 }
 
+static void
+async_auth_finished (SoupAuth    *auth,
+                    GParamSpec  *pspec,
+                    SoupMessage *msg)
+{
+       SoupSession *session;
+
+       session = g_object_steal_data (G_OBJECT (msg), "auth-msg-session");
+       if (!session)
+               return;
+
+       soup_session_unpause_message (session, msg);
+       g_object_unref (session);
+}
+
 static void
 authenticate_auth (SoupAuthManager *manager, SoupAuth *auth,
                   SoupMessage *msg, gboolean prior_auth_failed,
@@ -533,8 +548,24 @@ authenticate_auth (SoupAuthManager *manager, SoupAuth *auth,
                soup_uri_set_password (uri, NULL);
                soup_uri_set_user (uri, NULL);
        } else if (!soup_auth_is_authenticated (auth) && can_interact) {
-               g_signal_emit (manager, signals[AUTHENTICATE], 0,
-                              msg, auth, prior_auth_failed);
+               SoupMessage *original_msg;
+               gboolean handled;
+
+               original_msg = soup_session_get_original_message_for_authentication (priv->session,
+                                                                                    msg);
+               handled = soup_message_authenticate (original_msg, auth, prior_auth_failed);
+               if (handled && !soup_auth_is_authenticated (auth)) {
+                       soup_session_pause_message (priv->session, msg);
+                       g_object_set_data_full (G_OBJECT (msg), "auth-msg-session",
+                                               g_object_ref (priv->session),
+                                               g_object_unref);
+                       g_signal_connect_object (auth, "notify::is-authenticated",
+                                                G_CALLBACK (async_auth_finished),
+                                                msg, 0);
+                       g_signal_connect_object (auth, "notify::is-cancelled",
+                                                G_CALLBACK (async_auth_finished),
+                                                msg, 0);
+               }
        }
 }
 
@@ -578,7 +609,7 @@ record_auth_for_uri (SoupAuthManagerPrivate *priv, SoupURI *uri,
         * since the old one might already be authenticated.)
         */
        old_auth = g_hash_table_lookup (host->auths, auth_info);
-       if (old_auth && (old_auth != auth || !prior_auth_failed)) {
+       if (old_auth && (old_auth != auth || !prior_auth_failed) && !soup_auth_is_cancelled (old_auth)) {
                g_free (auth_info);
                return old_auth;
        } else {
diff --git a/libsoup/auth/soup-auth.c b/libsoup/auth/soup-auth.c
index 7e5a5f70..56612c52 100644
--- a/libsoup/auth/soup-auth.c
+++ b/libsoup/auth/soup-auth.c
@@ -38,6 +38,7 @@ typedef struct {
         char *realm;
        char *host;
        gboolean proxy;
+       gboolean cancelled;
 } SoupAuthPrivate;
 
 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (SoupAuth, soup_auth, G_TYPE_OBJECT)
@@ -50,6 +51,7 @@ enum {
        PROP_HOST,
        PROP_IS_FOR_PROXY,
        PROP_IS_AUTHENTICATED,
+       PROP_IS_CANCELLED,
 
        LAST_PROP
 };
@@ -59,6 +61,18 @@ soup_auth_init (SoupAuth *auth)
 {
 }
 
+static void
+soup_auth_dispose (GObject *object)
+{
+       SoupAuth *auth = SOUP_AUTH (object);
+       SoupAuthPrivate *priv = soup_auth_get_instance_private (auth);
+
+       if (!priv->cancelled && !soup_auth_is_authenticated (auth))
+               soup_auth_cancel (auth);
+
+       G_OBJECT_CLASS (soup_auth_parent_class)->dispose (object);
+}
+
 static void
 soup_auth_finalize (GObject *object)
 {
@@ -119,6 +133,8 @@ soup_auth_get_property (GObject *object, guint prop_id,
        case PROP_IS_AUTHENTICATED:
                g_value_set_boolean (value, soup_auth_is_authenticated (auth));
                break;
+       case PROP_IS_CANCELLED:
+               g_value_set_boolean (value, priv->cancelled);
        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
                break;
@@ -138,6 +154,7 @@ soup_auth_class_init (SoupAuthClass *auth_class)
 
        auth_class->can_authenticate = auth_can_authenticate;
 
+       object_class->dispose = soup_auth_dispose;
        object_class->finalize     = soup_auth_finalize;
        object_class->set_property = soup_auth_set_property;
        object_class->get_property = soup_auth_get_property;
@@ -213,6 +230,20 @@ soup_auth_class_init (SoupAuthClass *auth_class)
                                      FALSE,
                                      G_PARAM_READABLE |
                                      G_PARAM_STATIC_STRINGS));
+       /**
+        * SOUP_AUTH_IS_CANCELLED:
+        *
+        * An alias for the #SoupAuth:is-cancelled property.
+        * (Whether or not the auth has been cancelled.)
+        **/
+       g_object_class_install_property (
+               object_class, PROP_IS_CANCELLED,
+               g_param_spec_boolean (SOUP_AUTH_IS_CANCELLED,
+                                     "Cancelled",
+                                     "Whether or not the auth is cancelled",
+                                     FALSE,
+                                     G_PARAM_READABLE |
+                                     G_PARAM_STATIC_STRINGS));
 }
 
 /**
@@ -294,6 +325,9 @@ soup_auth_update (SoupAuth *auth, SoupMessage *msg, const char *auth_header)
        g_return_val_if_fail (SOUP_IS_MESSAGE (msg), FALSE);
        g_return_val_if_fail (auth_header != NULL, FALSE);
 
+       if (priv->cancelled)
+               return FALSE;
+
        scheme = soup_auth_get_scheme_name (auth);
        if (g_ascii_strncasecmp (auth_header, scheme, strlen (scheme)) != 0)
                return FALSE;
@@ -328,18 +362,47 @@ soup_auth_update (SoupAuth *auth, SoupMessage *msg, const char *auth_header)
 void
 soup_auth_authenticate (SoupAuth *auth, const char *username, const char *password)
 {
+       SoupAuthPrivate *priv;
        gboolean was_authenticated;
 
        g_return_if_fail (SOUP_IS_AUTH (auth));
        g_return_if_fail (username != NULL);
        g_return_if_fail (password != NULL);
 
+       priv = soup_auth_get_instance_private (auth);
+       if (priv->cancelled)
+               return;
+
        was_authenticated = soup_auth_is_authenticated (auth);
        SOUP_AUTH_GET_CLASS (auth)->authenticate (auth, username, password);
        if (was_authenticated != soup_auth_is_authenticated (auth))
                g_object_notify (G_OBJECT (auth), SOUP_AUTH_IS_AUTHENTICATED);
 }
 
+/**
+ * soup_auth_cancel:
+ * @auth: a #SoupAuth
+ *
+ * Call this on an auth to cancel it. You need to cancel an auth to complete
+ * an asynchronous authenticate operation when no credentials are provided
+ * (soup_auth_authenticate() is not called).
+ * The #SoupAuth will be cancelled on dispose if it hans't been authenticated.
+ */
+void
+soup_auth_cancel (SoupAuth *auth)
+{
+       SoupAuthPrivate *priv;
+
+       g_return_if_fail (SOUP_IS_AUTH (auth));
+
+       priv = soup_auth_get_instance_private (auth);
+       if (priv->cancelled)
+               return;
+
+       priv->cancelled = TRUE;
+       g_object_notify (G_OBJECT (auth), SOUP_AUTH_IS_CANCELLED);
+}
+
 /**
  * soup_auth_is_for_proxy:
  * @auth: a #SoupAuth
@@ -452,11 +515,36 @@ soup_auth_get_info (SoupAuth *auth)
 gboolean
 soup_auth_is_authenticated (SoupAuth *auth)
 {
+       SoupAuthPrivate *priv;
+
        g_return_val_if_fail (SOUP_IS_AUTH (auth), TRUE);
 
+       priv = soup_auth_get_instance_private (auth);
+       if (priv->cancelled)
+               return FALSE;
+
        return SOUP_AUTH_GET_CLASS (auth)->is_authenticated (auth);
 }
 
+/**
+ * soup_auth_is_cancelled:
+ * @auth: a #SoupAuth
+ *
+ * Tests if @auth has been cancelled
+ *
+ * Returns: %TRUE if @auth has been cancelled
+ */
+gboolean
+soup_auth_is_cancelled (SoupAuth *auth)
+{
+       SoupAuthPrivate *priv;
+
+       g_return_val_if_fail (SOUP_IS_AUTH (auth), TRUE);
+
+       priv = soup_auth_get_instance_private (auth);
+       return priv->cancelled;
+}
+
 /**
  * soup_auth_get_authorization:
  * @auth: a #SoupAuth
@@ -495,9 +583,15 @@ gboolean
 soup_auth_is_ready (SoupAuth    *auth,
                    SoupMessage *msg)
 {
+       SoupAuthPrivate *priv;
+
        g_return_val_if_fail (SOUP_IS_AUTH (auth), TRUE);
        g_return_val_if_fail (SOUP_IS_MESSAGE (msg), TRUE);
 
+       priv = soup_auth_get_instance_private (auth);
+       if (priv->cancelled)
+               return FALSE;
+
        if (SOUP_AUTH_GET_CLASS (auth)->is_ready)
                return SOUP_AUTH_GET_CLASS (auth)->is_ready (auth, msg);
        else
@@ -518,8 +612,14 @@ soup_auth_is_ready (SoupAuth    *auth,
 gboolean
 soup_auth_can_authenticate (SoupAuth *auth)
 {
+       SoupAuthPrivate *priv;
+
        g_return_val_if_fail (SOUP_IS_AUTH (auth), FALSE);
 
+       priv = soup_auth_get_instance_private (auth);
+       if (priv->cancelled)
+               return FALSE;
+
        return SOUP_AUTH_GET_CLASS (auth)->can_authenticate (auth);
 }
 
diff --git a/libsoup/auth/soup-auth.h b/libsoup/auth/soup-auth.h
index c9b78933..c3375a7a 100644
--- a/libsoup/auth/soup-auth.h
+++ b/libsoup/auth/soup-auth.h
@@ -48,6 +48,7 @@ struct _SoupAuthClass {
 #define SOUP_AUTH_HOST             "host"
 #define SOUP_AUTH_IS_FOR_PROXY     "is-for-proxy"
 #define SOUP_AUTH_IS_AUTHENTICATED "is-authenticated"
+#define SOUP_AUTH_IS_CANCELLED     "is-cancelled"
 
 SOUP_AVAILABLE_IN_2_4
 SoupAuth   *soup_auth_new                   (GType          type,
@@ -73,8 +74,16 @@ SOUP_AVAILABLE_IN_2_4
 void        soup_auth_authenticate          (SoupAuth      *auth,
                                             const char    *username,
                                             const char    *password);
+
+SOUP_AVAILABLE_IN_ALL
+void        soup_auth_cancel                (SoupAuth      *auth);
+
 SOUP_AVAILABLE_IN_2_4
 gboolean    soup_auth_is_authenticated      (SoupAuth      *auth);
+
+SOUP_AVAILABLE_IN_ALL
+gboolean    soup_auth_is_cancelled          (SoupAuth      *auth);
+
 SOUP_AVAILABLE_IN_2_42
 gboolean    soup_auth_is_ready              (SoupAuth      *auth,
                                             SoupMessage   *msg);
diff --git a/libsoup/soup-message-private.h b/libsoup/soup-message-private.h
index 5ff12512..47068bac 100644
--- a/libsoup/soup-message-private.h
+++ b/libsoup/soup-message-private.h
@@ -130,6 +130,9 @@ void soup_message_content_sniffed   (SoupMessage *msg,
 void soup_message_starting          (SoupMessage *msg);
 void soup_message_restarted         (SoupMessage *msg);
 void soup_message_finished          (SoupMessage *msg);
+gboolean soup_message_authenticate  (SoupMessage *msg,
+                                    SoupAuth    *auth,
+                                    gboolean     retrying);
 
 gboolean soup_message_disables_feature (SoupMessage *msg,
                                        gpointer     feature);
diff --git a/libsoup/soup-message.c b/libsoup/soup-message.c
index 3b327413..7897ac38 100644
--- a/libsoup/soup-message.c
+++ b/libsoup/soup-message.c
@@ -73,6 +73,7 @@ enum {
        RESTARTED,
        FINISHED,
 
+       AUTHENTICATE,
        NETWORK_EVENT,
 
        LAST_SIGNAL
@@ -482,6 +483,40 @@ soup_message_class_init (SoupMessageClass *message_class)
                              NULL,
                              G_TYPE_NONE, 0);
 
+       /**
+        * SoupMessage::authenticate:
+        * @msg: the message
+        * @auth: the #SoupAuth to authenticate
+        * @retrying: %TRUE if this is the second (or later) attempt
+        *
+        * Emitted when the message requires authentication. If
+        * credentials are available call soup_auth_authenticate() on
+        * @auth. If these credentials fail, the signal will be
+        * emitted again, with @retrying set to %TRUE, which will
+        * continue until you return without calling
+        * soup_auth_authenticate() on @auth.
+        *
+        * Note that this may be emitted before @msg's body has been
+        * fully read.
+        *
+        * You can authenticate @auth asynchronously by calling g_object_ref()
+        * on @auth and returning %TRUE. The operation will complete once
+        * either soup_auth_authenticate() or soup_auth_cancel() are called.
+        *
+        * Returns: %TRUE to stop other handlers from being invoked
+        *    or %FALSE to propagate the event further.
+        **/
+       signals[AUTHENTICATE] =
+               g_signal_new ("authenticate",
+                             G_OBJECT_CLASS_TYPE (object_class),
+                             G_SIGNAL_RUN_LAST,
+                             G_STRUCT_OFFSET (SoupMessageClass, authenticate),
+                             g_signal_accumulator_true_handled, NULL,
+                             NULL,
+                             G_TYPE_BOOLEAN, 2,
+                             SOUP_TYPE_AUTH,
+                             G_TYPE_BOOLEAN);
+
        /**
         * SoupMessage::network-event:
         * @msg: the message
@@ -939,6 +974,17 @@ soup_message_network_event (SoupMessage         *msg,
                       event, connection);
 }
 
+gboolean
+soup_message_authenticate (SoupMessage *msg,
+                          SoupAuth    *auth,
+                          gboolean     retrying)
+{
+       gboolean handled;
+       g_signal_emit (msg, signals[AUTHENTICATE], 0,
+                      auth, retrying, &handled);
+       return handled;
+}
+
 static void
 header_handler_free (gpointer header_name, GClosure *closure)
 {
diff --git a/libsoup/soup-message.h b/libsoup/soup-message.h
index 45d50562..3e21b07d 100644
--- a/libsoup/soup-message.h
+++ b/libsoup/soup-message.h
@@ -46,6 +46,9 @@ typedef struct {
        void     (*restarted)           (SoupMessage *msg);
        void     (*finished)            (SoupMessage *msg);
        void     (*starting)            (SoupMessage *msg);
+       void     (*authenticate)        (SoupMessage *msg,
+                                        SoupAuth    *auth,
+                                        gboolean     retrying);
 
        /* Padding for future expansion */
        void (*_libsoup_reserved1) (void);
diff --git a/libsoup/soup-session-private.h b/libsoup/soup-session-private.h
index 57ce3fd1..d641c69b 100644
--- a/libsoup/soup-session-private.h
+++ b/libsoup/soup-session-private.h
@@ -15,6 +15,8 @@ SoupURI *soup_session_get_message_proxy_uri (SoupSession *session,
                                             SoupMessage *msg);
 void     soup_session_requeue_message       (SoupSession *session,
                                             SoupMessage *msg);
+SoupMessage *soup_session_get_original_message_for_authentication (SoupSession *session,
+                                                                  SoupMessage *msg);
 
 G_END_DECLS
 
diff --git a/libsoup/soup-session.c b/libsoup/soup-session.c
index b2e4452c..5722a8e1 100644
--- a/libsoup/soup-session.c
+++ b/libsoup/soup-session.c
@@ -140,10 +140,6 @@ static void connection_disconnected (SoupConnection *conn, gpointer user_data);
 static void drop_connection (SoupSession *session, SoupSessionHost *host,
                             SoupConnection *conn);
 
-static void auth_manager_authenticate (SoupAuthManager *manager,
-                                      SoupMessage *msg, SoupAuth *auth,
-                                      gboolean retrying, gpointer user_data);
-
 static void async_run_queue (SoupSession *session);
 
 static void async_send_request_running (SoupSession *session, SoupMessageQueueItem *item);
@@ -168,7 +164,6 @@ G_DEFINE_TYPE_WITH_PRIVATE (SoupSession, soup_session, G_TYPE_OBJECT)
 enum {
        REQUEST_QUEUED,
        REQUEST_UNQUEUED,
-       AUTHENTICATE,
        LAST_SIGNAL
 };
 
@@ -246,8 +241,6 @@ soup_session_init (SoupSession *session)
        priv->features_cache = g_hash_table_new (NULL, NULL);
 
        auth_manager = g_object_new (SOUP_TYPE_AUTH_MANAGER, NULL);
-       g_signal_connect (auth_manager, "authenticate",
-                         G_CALLBACK (auth_manager_authenticate), session);
        soup_session_feature_add_feature (SOUP_SESSION_FEATURE (auth_manager),
                                          SOUP_TYPE_AUTH_BASIC);
        soup_session_feature_add_feature (SOUP_SESSION_FEATURE (auth_manager),
@@ -780,14 +773,6 @@ free_host (SoupSessionHost *host)
        g_slice_free (SoupSessionHost, host);
 }
 
-static void
-auth_manager_authenticate (SoupAuthManager *manager, SoupMessage *msg,
-                          SoupAuth *auth, gboolean retrying,
-                          gpointer session)
-{
-       g_signal_emit (session, signals[AUTHENTICATE], 0, msg, auth, retrying);
-}
-
 #define SOUP_SESSION_WOULD_REDIRECT_AS_GET(session, msg) \
        ((msg)->status_code == SOUP_STATUS_SEE_OTHER || \
         ((msg)->status_code == SOUP_STATUS_FOUND && \
@@ -2441,41 +2426,6 @@ soup_session_class_init (SoupSessionClass *session_class)
                              G_TYPE_NONE, 1,
                              SOUP_TYPE_MESSAGE);
 
-       /**
-        * SoupSession::authenticate:
-        * @session: the session
-        * @msg: the #SoupMessage being sent
-        * @auth: the #SoupAuth to authenticate
-        * @retrying: %TRUE if this is the second (or later) attempt
-        *
-        * Emitted when the session requires authentication. If
-        * credentials are available call soup_auth_authenticate() on
-        * @auth. If these credentials fail, the signal will be
-        * emitted again, with @retrying set to %TRUE, which will
-        * continue until you return without calling
-        * soup_auth_authenticate() on @auth.
-        *
-        * Note that this may be emitted before @msg's body has been
-        * fully read.
-        *
-        * If you call soup_session_pause_message() on @msg before
-        * returning, then you can authenticate @auth asynchronously
-        * (as long as you g_object_ref() it to make sure it doesn't
-        * get destroyed), and then unpause @msg when you are ready
-        * for it to continue.
-        **/
-       signals[AUTHENTICATE] =
-               g_signal_new ("authenticate",
-                             G_OBJECT_CLASS_TYPE (object_class),
-                             G_SIGNAL_RUN_FIRST,
-                             0,
-                             NULL, NULL,
-                             NULL,
-                             G_TYPE_NONE, 3,
-                             SOUP_TYPE_MESSAGE,
-                             SOUP_TYPE_AUTH,
-                             G_TYPE_BOOLEAN);
-
        /* properties */
        /**
         * SoupSession:proxy-resolver:
@@ -4357,3 +4307,25 @@ soup_session_get_message_proxy_uri (SoupSession *session,
        soup_message_queue_item_unref (item);
        return uri;
 }
+
+SoupMessage *
+soup_session_get_original_message_for_authentication (SoupSession *session,
+                                                     SoupMessage *msg)
+{
+       SoupSessionPrivate *priv = soup_session_get_instance_private (session);
+       SoupMessageQueueItem *item;
+       SoupMessage *original_msg;
+
+       item = soup_message_queue_lookup (priv->queue, msg);
+       if (!item)
+                return msg;
+
+       if (msg->method != SOUP_METHOD_CONNECT) {
+               soup_message_queue_item_unref (item);
+               return msg;
+       }
+
+       original_msg = item->related ? item->related->msg : msg;
+       soup_message_queue_item_unref (item);
+       return original_msg;
+}
diff --git a/tests/auth-test.c b/tests/auth-test.c
index 0bacbb80..da2c28fb 100644
--- a/tests/auth-test.c
+++ b/tests/auth-test.c
@@ -213,19 +213,20 @@ handler (SoupMessage *msg, gpointer data)
        }
 }
 
-static void
-authenticate (SoupSession *session, SoupMessage *msg,
-             SoupAuth *auth, gboolean retrying, gpointer data)
+static gboolean
+authenticate (SoupMessage  *msg,
+             SoupAuth     *auth,
+             gboolean      retrying,
+             SoupAuthTest *test)
 {
-       SoupAuthTest *test = data;
        char *username, *password;
        char num;
 
        if (!test->provided[0])
-               return;
+               return FALSE;
        if (retrying) {
                if (!test->provided[1])
-                       return;
+                       return FALSE;
                num = test->provided[1];
        } else
                num = test->provided[0];
@@ -235,6 +236,8 @@ authenticate (SoupSession *session, SoupMessage *msg,
        soup_auth_authenticate (auth, username, password);
        g_free (username);
        g_free (password);
+
+       return TRUE;
 }
 
 static void
@@ -250,16 +253,17 @@ bug271540_sent (SoupMessage *msg, gpointer data)
                          "sent unauthenticated message %d after authenticating", n);
 }
 
-static void
-bug271540_authenticate (SoupSession *session, SoupMessage *msg,
-                       SoupAuth *auth, gboolean retrying, gpointer data)
+static gboolean
+bug271540_authenticate (SoupMessage *msg,
+                       SoupAuth    *auth,
+                       gboolean     retrying,
+                       gboolean    *authenticated)
 {
        int n = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (msg), "#"));
-       gboolean *authenticated = data;
 
        if (strcmp (soup_auth_get_scheme_name (auth), "Basic") != 0 ||
            strcmp (soup_auth_get_realm (auth), "realm1") != 0)
-               return;
+               return FALSE;
 
        if (!*authenticated) {
                debug_printf (1, "    authenticating message %d\n", n);
@@ -269,6 +273,8 @@ bug271540_authenticate (SoupSession *session, SoupMessage *msg,
                soup_test_assert (!*authenticated,
                                  "asked to authenticate message %d after authenticating", n);
        }
+
+       return TRUE;
 }
 
 static void
@@ -299,13 +305,12 @@ do_pipelined_auth_test (void)
        session = soup_test_session_new (SOUP_TYPE_SESSION, NULL);
 
        authenticated = FALSE;
-       g_signal_connect (session, "authenticate",
-                         G_CALLBACK (bug271540_authenticate), &authenticated);
-
        uri = g_strconcat (base_uri, "Basic/realm1/", NULL);
        for (i = 0; i < 10; i++) {
                msg = soup_message_new (SOUP_METHOD_GET, uri);
                g_object_set_data (G_OBJECT (msg), "#", GINT_TO_POINTER (i + 1));
+               g_signal_connect (msg, "authenticate",
+                                 G_CALLBACK (bug271540_authenticate), &authenticated);
                g_signal_connect (msg, "wrote_headers",
                                  G_CALLBACK (bug271540_sent), &authenticated);
 
@@ -393,18 +398,21 @@ do_pipelined_auth_test (void)
  *
  */
 
-static void
-digest_nonce_authenticate (SoupSession *session, SoupMessage *msg,
-                          SoupAuth *auth, gboolean retrying, gpointer data)
+static gboolean
+digest_nonce_authenticate (SoupMessage *msg,
+                          SoupAuth    *auth,
+                          gboolean     retrying)
 {
        if (retrying)
-               return;
+               return FALSE;
 
        if (strcmp (soup_auth_get_scheme_name (auth), "Digest") != 0 ||
            strcmp (soup_auth_get_realm (auth), "realm1") != 0)
-               return;
+               return FALSE;
 
        soup_auth_authenticate (auth, "user1", "realm1");
+
+       return TRUE;
 }
 
 static void
@@ -429,7 +437,7 @@ do_digest_nonce_test (SoupSession *session,
                soup_message_set_flags (msg, flags | SOUP_MESSAGE_DO_NOT_USE_AUTH_CACHE);
        }
        if (expect_signal) {
-               g_signal_connect (session, "authenticate",
+               g_signal_connect (msg, "authenticate",
                                  G_CALLBACK (digest_nonce_authenticate),
                                  NULL);
        }
@@ -486,11 +494,12 @@ do_digest_expiration_test (void)
  * succeed.
  */
 
-static void
-async_authenticate (SoupSession *session, SoupMessage *msg,
-                   SoupAuth *auth, gboolean retrying, gpointer data)
+static gboolean
+async_authenticate (SoupMessage *msg,
+                   SoupAuth    *auth,
+                   gboolean     retrying,
+                   SoupAuth   **saved_auth)
 {
-       SoupAuth **saved_auth = data;
        int id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (msg), "id"));
 
        debug_printf (2, "  async_authenticate msg%d\n", id);
@@ -501,13 +510,14 @@ async_authenticate (SoupSession *session, SoupMessage *msg,
         */
        if (msg->status_code != SOUP_STATUS_UNAUTHORIZED) {
                debug_printf (2, "    (ignoring)\n");
-               return;
+               return FALSE;
        }
 
-       soup_session_pause_message (session, msg);
        if (saved_auth)
                *saved_auth = g_object_ref (auth);
        g_main_loop_quit (loop);
+
+       return TRUE;
 }
 
 static void
@@ -524,33 +534,36 @@ async_finished (SoupMessage *msg,
                g_main_loop_quit (loop);
 }
 
-static void
-async_authenticate_assert_once (SoupSession *session, SoupMessage *msg,
-                                SoupAuth *auth, gboolean retrying, gpointer data)
+static gboolean
+async_authenticate_assert_once (SoupMessage *msg,
+                                SoupAuth    *auth,
+                               gboolean     retrying,
+                               gboolean    *been_here)
 {
-       gboolean *been_here = data;
-
        debug_printf (2, "  async_authenticate_assert_once\n");
 
        soup_test_assert (!*been_here,
                          "async_authenticate_assert_once called twice");
        *been_here = TRUE;
+
+       return FALSE;
 }
 
-static void
-async_authenticate_assert_once_and_stop (SoupSession *session, SoupMessage *msg,
-                                        SoupAuth *auth, gboolean retrying, gpointer data)
+static gboolean
+async_authenticate_assert_once_and_stop (SoupMessage *msg,
+                                        SoupAuth    *auth,
+                                        gboolean     retrying,
+                                        SoupAuth   **saved_auth)
 {
-       gboolean *been_here = data;
-
        debug_printf (2, "  async_authenticate_assert_once_and_stop\n");
 
-       soup_test_assert (!*been_here,
+       soup_test_assert (!*saved_auth,
                          "async_authenticate_assert_once called twice");
-       *been_here = TRUE;
+       *saved_auth = g_object_ref (auth);
 
-       soup_session_pause_message (session, msg);
        g_main_loop_quit (loop);
+
+       return TRUE;
 }
 
 static void
@@ -558,7 +571,6 @@ do_async_auth_good_password_test (void)
 {
        SoupSession *session;
        SoupMessage *msg1, *msg2, *msg3, msg2_bak;
-       guint auth_id;
        char *uri;
        SoupAuth *auth = NULL;
        int remaining;
@@ -572,14 +584,13 @@ do_async_auth_good_password_test (void)
 
        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_signal_connect (msg1, "authenticate",
                                    G_CALLBACK (async_authenticate), &auth);
        remaining++;
        g_signal_connect (msg1, "finished",
                          G_CALLBACK (async_finished), &remaining);
        soup_session_send_async (session, msg1, G_PRIORITY_DEFAULT, NULL, NULL, NULL);
        g_main_loop_run (loop);
-       g_signal_handler_disconnect (session, auth_id);
 
        /* async_authenticate will pause msg1 and quit loop */
 
@@ -598,14 +609,13 @@ do_async_auth_good_password_test (void)
 
        msg3 = soup_message_new ("GET", uri);
        g_object_set_data (G_OBJECT (msg3), "id", GINT_TO_POINTER (3));
-       auth_id = g_signal_connect (session, "authenticate",
-                                   G_CALLBACK (async_authenticate), NULL);
+       g_signal_connect (msg3, "authenticate",
+                         G_CALLBACK (async_authenticate), NULL);
        remaining++;
        g_signal_connect (msg3, "finished",
                          G_CALLBACK (async_finished), &remaining);
        soup_session_send_async (session, msg3, G_PRIORITY_DEFAULT, NULL, NULL, NULL);
        g_main_loop_run (loop);
-       g_signal_handler_disconnect (session, auth_id);
 
        /* async_authenticate will pause msg3 and quit loop */
 
@@ -613,8 +623,6 @@ do_async_auth_good_password_test (void)
        if (auth) {
                soup_auth_authenticate (auth, "user1", "realm1");
                g_object_unref (auth);
-               soup_session_unpause_message (session, msg1);
-               soup_session_unpause_message (session, msg3);
 
                g_main_loop_run (loop);
 
@@ -658,28 +666,25 @@ do_async_auth_bad_password_test (void)
        session = soup_test_session_new (SOUP_TYPE_SESSION, NULL);
        uri = g_strconcat (base_uri, "Basic/realm1/", NULL);
        remaining = 0;
-       auth = NULL;
 
        msg = soup_message_new ("GET", uri);
        g_object_set_data (G_OBJECT (msg), "id", GINT_TO_POINTER (1));
-       auth_id = g_signal_connect (session, "authenticate",
+       auth_id = g_signal_connect (msg, "authenticate",
                                    G_CALLBACK (async_authenticate), &auth);
        remaining++;
        g_signal_connect (msg, "finished",
                          G_CALLBACK (async_finished), &remaining);
        soup_session_send_async (session, msg, G_PRIORITY_DEFAULT, NULL, NULL, NULL);
        g_main_loop_run (loop);
-       g_signal_handler_disconnect (session, auth_id);
+       g_signal_handler_disconnect (msg, auth_id);
        soup_auth_authenticate (auth, "user1", "wrong");
        g_object_unref (auth);
-       soup_session_unpause_message (session, msg);
 
        been_there = FALSE;
-       auth_id = g_signal_connect (session, "authenticate",
-                                   G_CALLBACK (async_authenticate_assert_once),
-                                   &been_there);
+       g_signal_connect (msg, "authenticate",
+                         G_CALLBACK (async_authenticate_assert_once),
+                         &been_there);
        g_main_loop_run (loop);
-       g_signal_handler_disconnect (session, auth_id);
 
        soup_test_assert (been_there,
                          "authenticate not emitted");
@@ -696,10 +701,9 @@ do_async_auth_no_password_test (void)
 {
        SoupSession *session;
        SoupMessage *msg;
-       guint auth_id;
        char *uri;
        int remaining;
-       gboolean been_there;
+       SoupAuth *auth = NULL;
 
        /* Test that giving no password doesn't cause multiple
         * authenticate signals the second time.
@@ -717,34 +721,32 @@ do_async_auth_no_password_test (void)
         */
        msg = soup_message_new ("GET", uri);
        g_object_set_data (G_OBJECT (msg), "id", GINT_TO_POINTER (1));
-       auth_id = g_signal_connect (session, "authenticate",
-                                   G_CALLBACK (async_authenticate), NULL);
+       g_signal_connect (msg, "authenticate",
+                         G_CALLBACK (async_authenticate), &auth);
        remaining++;
        g_signal_connect (msg, "finished",
                          G_CALLBACK (async_finished), &remaining);
        soup_session_send_async (session, msg, G_PRIORITY_DEFAULT, NULL, NULL, NULL);
        g_main_loop_run (loop);
-       g_signal_handler_disconnect (session, auth_id);
-       soup_session_unpause_message (session, msg);
+       soup_auth_cancel (auth);
+       g_clear_object (&auth);
        g_main_loop_run (loop);
        g_object_unref(msg);
 
        /* Now send a second message */
        msg = soup_message_new ("GET", uri);
        g_object_set_data (G_OBJECT (msg), "id", GINT_TO_POINTER (2));
-       been_there = FALSE;
-       auth_id = g_signal_connect (session, "authenticate",
-                                   G_CALLBACK (async_authenticate_assert_once_and_stop),
-                                   &been_there);
+       g_signal_connect (msg, "authenticate",
+                         G_CALLBACK (async_authenticate_assert_once_and_stop),
+                         &auth);
        remaining++;
        g_signal_connect (msg, "finished",
                          G_CALLBACK (async_finished), &remaining);
        soup_session_send_async (session, msg, G_PRIORITY_DEFAULT, NULL, NULL, NULL);
        g_main_loop_run (loop);
-       soup_session_unpause_message (session, msg);
-
+       soup_auth_cancel (auth);
        g_main_loop_run (loop);
-       g_signal_handler_disconnect (session, auth_id);
+       g_object_unref (auth);
 
        soup_test_session_abort_unref (session);
        g_object_unref (msg);
@@ -761,11 +763,12 @@ typedef struct {
        } round[2];
 } SelectAuthData;
 
-static void
-select_auth_authenticate (SoupSession *session, SoupMessage *msg,
-                         SoupAuth *auth, gboolean retrying, gpointer data)
+static gboolean
+select_auth_authenticate (SoupMessage    *msg,
+                         SoupAuth       *auth,
+                         gboolean        retrying,
+                         SelectAuthData *sad)
 {
-       SelectAuthData *sad = data;
        const char *header, *basic, *digest;
        int round = retrying ? 1 : 0;
 
@@ -784,8 +787,13 @@ select_auth_authenticate (SoupSession *session, SoupMessage *msg,
                sad->round[round].headers = "Digest";
 
        sad->round[round].response = soup_auth_get_scheme_name (auth);
-       if (sad->password && !retrying)
+       if (sad->password && !retrying) {
                soup_auth_authenticate (auth, "user", sad->password);
+
+               return TRUE;
+       }
+
+       return FALSE;
 }
 
 static void
@@ -803,12 +811,11 @@ select_auth_test_one (SoupURI *uri,
        if (disable_digest)
                soup_session_remove_feature_by_type (session, SOUP_TYPE_AUTH_DIGEST);
 
-       g_signal_connect (session, "authenticate",
-                         G_CALLBACK (select_auth_authenticate), &sad);
-       memset (&sad, 0, sizeof (sad));
-       sad.password = password;
-
        msg = soup_message_new_from_uri ("GET", uri);
+       g_signal_connect (msg, "authenticate",
+                          G_CALLBACK (select_auth_authenticate), &sad);
+        memset (&sad, 0, sizeof (sad));
+        sad.password = password;
        soup_test_session_send_message (session, msg);
 
        soup_test_assert (strcmp (sad.round[0].headers, first_headers) == 0,
@@ -1013,21 +1020,21 @@ auth_close_idle_authenticate (gpointer user_data)
        AuthCloseData *acd = user_data;
 
        soup_auth_authenticate (acd->auth, "user", "good-basic");
-       soup_session_unpause_message (acd->session, acd->msg);
 
        g_object_unref (acd->auth);
        return FALSE;
 }
 
-static void
-auth_close_authenticate (SoupSession *session, SoupMessage *msg,
-                        SoupAuth *auth, gboolean retrying, gpointer data)
+static gboolean
+auth_close_authenticate (SoupMessage   *msg,
+                        SoupAuth      *auth,
+                        gboolean       retrying,
+                        AuthCloseData *acd)
 {
-       AuthCloseData *acd = data;
-
-       soup_session_pause_message (session, msg);
        acd->auth = g_object_ref (auth);
        g_idle_add (auth_close_idle_authenticate, acd);
+
+       return TRUE;
 }
 
 static void
@@ -1058,10 +1065,9 @@ do_auth_close_test (void)
                          G_CALLBACK (auth_close_request_started), NULL);
 
        acd.session = soup_test_session_new (SOUP_TYPE_SESSION, NULL);
-       g_signal_connect (acd.session, "authenticate",
-                         G_CALLBACK (auth_close_authenticate), &acd);
-
        acd.msg = soup_message_new_from_uri ("GET", uri);
+       g_signal_connect (acd.msg, "authenticate",
+                         G_CALLBACK (auth_close_authenticate), &acd);
        soup_uri_free (uri);
        body = soup_test_session_async_send (acd.session, acd.msg);
 
@@ -1080,11 +1086,14 @@ infinite_cancel (gpointer session)
        return FALSE;
 }
 
-static void
-infinite_authenticate (SoupSession *session, SoupMessage *msg,
-                      SoupAuth *auth, gboolean retrying, gpointer data)
+static gboolean
+infinite_authenticate (SoupMessage *msg,
+                      SoupAuth    *auth,
+                      gboolean     retrying)
 {
        soup_auth_authenticate (auth, "user", "bad");
+
+       return TRUE;
 }
 
 static void
@@ -1098,11 +1107,10 @@ do_infinite_auth_test (void)
        SOUP_TEST_SKIP_IF_NO_APACHE;
 
        session = soup_test_session_new (SOUP_TYPE_SESSION, NULL);
-       g_signal_connect (session, "authenticate",
-                         G_CALLBACK (infinite_authenticate), NULL);
-
        uri = g_strconcat (base_uri, "Basic/realm1/", NULL);
        msg = soup_message_new ("GET", uri);
+       g_signal_connect (msg, "authenticate",
+                          G_CALLBACK (infinite_authenticate), NULL);
        g_free (uri);
 
        timeout = g_timeout_add (500, infinite_cancel, session);
@@ -1137,15 +1145,20 @@ disappear_request_read (SoupServer        *server,
                soup_message_headers_remove (response_headers, "WWW-Authenticate");
 }
 
-static void
-disappear_authenticate (SoupSession *session, SoupMessage *msg,
-                       SoupAuth *auth, gboolean retrying, gpointer data)
+static gboolean
+disappear_authenticate (SoupMessage *msg,
+                       SoupAuth    *auth,
+                       gboolean     retrying,
+                       int         *counter)
 {
-       int *counter = data;
-
        (*counter)++;
-       if (!retrying)
+       if (!retrying) {
                soup_auth_authenticate (auth, "user", "bad");
+
+               return TRUE;
+       }
+
+       return FALSE;
 }
 
 static void
@@ -1179,10 +1192,10 @@ do_disappearing_auth_test (void)
        session = soup_test_session_new (SOUP_TYPE_SESSION, NULL);
 
        counter = 0;
-       g_signal_connect (session, "authenticate",
+       msg = soup_message_new_from_uri ("GET", uri);
+       g_signal_connect (msg, "authenticate",
                          G_CALLBACK (disappear_authenticate), &counter);
 
-       msg = soup_message_new_from_uri ("GET", uri);
        body = soup_test_session_async_send (session, msg);
 
        soup_test_assert (counter <= 2,
@@ -1242,7 +1255,6 @@ do_batch_tests (gconstpointer data)
        SoupMessage *msg;
        char *expected, *uristr;
        SoupURI *base;
-       guint signal;
        int i;
 
        SOUP_TEST_SKIP_IF_NO_APACHE;
@@ -1283,11 +1295,10 @@ do_batch_tests (gconstpointer data)
                        msg, "got_headers", SOUP_STATUS_OK,
                        G_CALLBACK (handler), expected);
 
-               signal = g_signal_connect (session, "authenticate",
-                                          G_CALLBACK (authenticate),
-                                          (gpointer)&current_tests[i]);
+               g_signal_connect (msg, "authenticate",
+                                 G_CALLBACK (authenticate),
+                                 (gpointer)&current_tests[i]);
                soup_test_session_send_message (session, msg);
-               g_signal_handler_disconnect (session, signal);
 
                soup_test_assert_message_status (msg, current_tests[i].final_status);
                soup_test_assert (!*expected,
@@ -1385,15 +1396,18 @@ do_message_do_not_use_auth_cache_test (void)
        soup_test_session_abort_unref (session);
 }
 
-static void
-async_no_auth_cache_authenticate (SoupSession *session, SoupMessage *msg,
-                                 SoupAuth *auth, gboolean retrying, SoupAuth **auth_out)
+static gboolean
+async_no_auth_cache_authenticate (SoupMessage *msg,
+                                 SoupAuth    *auth,
+                                 gboolean     retrying,
+                                 SoupAuth   **auth_out)
 {
        debug_printf (1, "  async_no_auth_cache_authenticate\n");
 
-       soup_session_pause_message (session, msg);
        *auth_out = g_object_ref (auth);
        g_main_loop_quit (loop);
+
+       return TRUE;
 }
 
 static void
@@ -1421,7 +1435,7 @@ do_async_message_do_not_use_auth_cache_test (void)
 
        msg = soup_message_new ("GET", uri);
        g_free (uri);
-       g_signal_connect (session, "authenticate",
+       g_signal_connect (msg, "authenticate",
                          G_CALLBACK (async_no_auth_cache_authenticate), &auth);
        flags = soup_message_get_flags (msg);
        soup_message_set_flags (msg, flags | SOUP_MESSAGE_DO_NOT_USE_AUTH_CACHE);
@@ -1436,7 +1450,6 @@ do_async_message_do_not_use_auth_cache_test (void)
        soup_auth_authenticate (auth, "user1", "realm1");
        g_object_unref (auth);
 
-       soup_session_unpause_message (session, msg);
        g_main_loop_run (loop);
 
        soup_test_assert_message_status (msg, SOUP_STATUS_OK);
@@ -1446,19 +1459,22 @@ do_async_message_do_not_use_auth_cache_test (void)
        g_main_loop_unref (loop);
 }
 
-static void
-has_authorization_header_authenticate (SoupSession *session, SoupMessage *msg,
-                                      SoupAuth *auth, gboolean retrying, gpointer data)
+static gboolean
+has_authorization_header_authenticate (SoupMessage *msg,
+                                      SoupAuth    *auth,
+                                      gboolean     retrying,
+                                      SoupAuth   **saved_auth)
 {
-       SoupAuth **saved_auth = data;
-
        soup_auth_authenticate (auth, "user1", "realm1");
        *saved_auth = g_object_ref (auth);
+
+       return TRUE;
 }
 
 static void
-has_authorization_header_authenticate_assert (SoupSession *session, SoupMessage *msg,
-                                             SoupAuth *auth, gboolean retrying, gpointer data)
+has_authorization_header_authenticate_assert (SoupMessage *msg,
+                                             SoupAuth    *auth,
+                                             gboolean     retrying)
 {
        soup_test_assert (FALSE, "authenticate emitted unexpectedly");
 }
@@ -1471,7 +1487,6 @@ do_message_has_authorization_header_test (void)
        SoupAuthManager *manager;
        SoupAuth *auth = NULL;
        char *token;
-       guint auth_id;
        char *uri;
 
        g_test_bug ("775882");
@@ -1482,7 +1497,7 @@ do_message_has_authorization_header_test (void)
        uri = g_strconcat (base_uri, "Digest/realm1/", NULL);
 
        msg = soup_message_new ("GET", uri);
-       auth_id = g_signal_connect (session, "authenticate",
+       g_signal_connect (msg, "authenticate",
                          G_CALLBACK (has_authorization_header_authenticate), &auth);
        soup_test_session_send_message (session, msg);
        soup_test_assert_message_status (msg, SOUP_STATUS_OK);
@@ -1490,16 +1505,15 @@ do_message_has_authorization_header_test (void)
        token = soup_auth_get_authorization (auth, msg);
        g_object_unref (auth);
        g_object_unref (msg);
-       g_signal_handler_disconnect (session, auth_id);
 
        manager = SOUP_AUTH_MANAGER (soup_session_get_feature (session, SOUP_TYPE_AUTH_MANAGER));
        soup_auth_manager_clear_cached_credentials (manager);
 
        msg = soup_message_new ("GET", uri);
        soup_message_headers_replace (msg->request_headers, "Authorization", token);
-       auth_id = g_signal_connect (session, "authenticate",
-                                   G_CALLBACK (has_authorization_header_authenticate_assert),
-                                   NULL);
+       g_signal_connect (msg, "authenticate",
+                         G_CALLBACK (has_authorization_header_authenticate_assert),
+                         NULL);
        soup_test_session_send_message (session, msg);
        soup_test_assert_message_status (msg, SOUP_STATUS_OK);
        g_object_unref (msg);
@@ -1513,24 +1527,32 @@ do_message_has_authorization_header_test (void)
        soup_test_assert_message_status (msg, SOUP_STATUS_OK);
        g_object_unref (msg);
        g_free (token);
-       g_signal_handler_disconnect (session, auth_id);
 
        g_free (uri);
        soup_test_session_abort_unref (session);
 }
 
-static void
-cancel_after_retry_authenticate (SoupSession  *session,
-                                 SoupMessage  *msg,
-                                 SoupAuth     *auth,
-                                 gboolean      retrying,
-                                 GCancellable *cancellable)
+typedef struct {
+       SoupSession *session;
+       GCancellable *cancellable;
+} CancelAfterRetryData;
+
+static gboolean
+cancel_after_retry_authenticate (SoupMessage          *msg,
+                                 SoupAuth             *auth,
+                                 gboolean              retrying,
+                                 CancelAfterRetryData *data)
 {
         if (retrying) {
-                soup_session_cancel_message (session, msg, SOUP_STATUS_CANCELLED);
-                g_cancellable_cancel (cancellable);
-        } else
-                soup_auth_authenticate (auth, "user1", "wrong");
+                soup_session_cancel_message (data->session, msg, SOUP_STATUS_CANCELLED);
+                g_cancellable_cancel (data->cancellable);
+
+               return FALSE;
+       }
+
+       soup_auth_authenticate (auth, "user1", "wrong");
+
+       return TRUE;
 }
 
 static void
@@ -1554,27 +1576,28 @@ do_cancel_after_retry_test (void)
         SoupSession *session;
         SoupMessage *msg;
         char *uri;
-        GCancellable *cancellable;
+       CancelAfterRetryData data;
         GMainLoop *loop;
 
         SOUP_TEST_SKIP_IF_NO_APACHE;
 
         session = soup_test_session_new (SOUP_TYPE_SESSION, NULL);
-        cancellable = g_cancellable_new ();
-        g_signal_connect (session, "authenticate",
-                          G_CALLBACK (cancel_after_retry_authenticate),
-                          cancellable);
+       data.session = session;
+        data.cancellable = g_cancellable_new ();
 
         loop = g_main_loop_new (NULL, FALSE);
 
         uri = g_strconcat (base_uri, "Digest/realm1/", NULL);
         msg = soup_message_new ("GET", uri);
-        soup_session_send_async (session, msg, G_PRIORITY_DEFAULT, cancellable,
+       g_signal_connect (msg, "authenticate",
+                          G_CALLBACK (cancel_after_retry_authenticate),
+                          &data);
+        soup_session_send_async (session, msg, G_PRIORITY_DEFAULT, data.cancellable,
                                 (GAsyncReadyCallback)request_send_cb, loop);
         g_main_loop_run (loop);
 
         g_object_unref (msg);
-        g_object_unref (cancellable);
+        g_object_unref (data.cancellable);
         g_free (uri);
         soup_test_session_abort_unref (session);
 }
diff --git a/tests/misc-test.c b/tests/misc-test.c
index 7dde71be..56f5ab44 100644
--- a/tests/misc-test.c
+++ b/tests/misc-test.c
@@ -254,7 +254,7 @@ do_callback_unref_test (void)
        /* Otherwise, if we haven't crashed, we're ok. */
 }
 
-/* SoupSession should clean up all signal handlers on a message after
+/* SoupSession should clean up all internal signal handlers on a message after
  * it is finished, allowing the message to be reused if desired.
  */
 static void
@@ -264,6 +264,9 @@ ensure_no_signal_handlers (SoupMessage *msg, guint *signal_ids, guint n_signal_i
        guint id;
 
        for (i = 0; i < n_signal_ids; i++) {
+               if (strcmp (g_signal_name (signal_ids[i]), "authenticate") == 0)
+                       continue;
+
                id = g_signal_handler_find (msg, G_SIGNAL_MATCH_ID, signal_ids[i],
                                            0, NULL, NULL, NULL);
                soup_test_assert (id == 0,
@@ -272,15 +275,18 @@ ensure_no_signal_handlers (SoupMessage *msg, guint *signal_ids, guint n_signal_i
        }
 }
 
-static void
-reuse_test_authenticate (SoupSession *session, SoupMessage *msg,
-                        SoupAuth *auth, gboolean retrying)
+static gboolean
+reuse_test_authenticate (SoupMessage *msg,
+                        SoupAuth    *auth,
+                        gboolean     retrying)
 {
        /* Get it wrong the first time, then succeed */
        if (!retrying)
                soup_auth_authenticate (auth, "user", "wrong password");
        else
                soup_auth_authenticate (auth, "user", "password");
+
+       return TRUE;
 }
 
 static void
@@ -296,11 +302,11 @@ do_msg_reuse_test (void)
        signal_ids = g_signal_list_ids (SOUP_TYPE_MESSAGE, &n_signal_ids);
 
        session = soup_test_session_new (SOUP_TYPE_SESSION, NULL);
-       g_signal_connect (session, "authenticate",
-                         G_CALLBACK (reuse_test_authenticate), NULL);
 
        debug_printf (1, "  First message\n");
        msg = soup_message_new_from_uri ("GET", base_uri);
+       g_signal_connect (msg, "authenticate",
+                          G_CALLBACK (reuse_test_authenticate), NULL);
        soup_test_session_async_send (session, msg);
        ensure_no_signal_handlers (msg, signal_ids, n_signal_ids);
 
diff --git a/tests/ntlm-test.c b/tests/ntlm-test.c
index c3d69c9b..2b4016ee 100644
--- a/tests/ntlm-test.c
+++ b/tests/ntlm-test.c
@@ -207,15 +207,21 @@ teardown_server (TestServer *ts,
 
 static gboolean authenticated_ntlm = FALSE;
 
-static void
-authenticate (SoupSession *session, SoupMessage *msg,
-             SoupAuth *auth, gboolean retrying, gpointer user)
+static gboolean
+authenticate (SoupMessage *msg,
+             SoupAuth    *auth,
+             gboolean    retrying,
+             gpointer    user)
 {
        if (!retrying) {
                soup_auth_authenticate (auth, user, "password");
                if (g_str_equal (soup_auth_get_scheme_name (auth), "NTLM"))
                        authenticated_ntlm = TRUE;
+
+               return TRUE;
        }
+
+       return FALSE;
 }
 
 typedef struct {
@@ -311,10 +317,15 @@ response_check (SoupMessage *msg, gpointer user_data)
 }
 
 static void
-do_message (SoupSession *session, SoupURI *base_uri, const char *path,
-           gboolean get_ntlm_prompt, gboolean do_ntlm,
-           gboolean get_basic_prompt, gboolean do_basic,
-           guint status_code)
+do_message (SoupSession *session,
+           SoupURI     *base_uri,
+           const char  *path,
+           const char  *user,
+           gboolean     get_ntlm_prompt,
+           gboolean     do_ntlm,
+           gboolean     get_basic_prompt,
+           gboolean     do_basic,
+           guint        status_code)
 {
        SoupURI *uri;
        SoupMessage *msg;
@@ -325,6 +336,11 @@ do_message (SoupSession *session, SoupURI *base_uri, const char *path,
        msg = soup_message_new_from_uri ("GET", uri);
        soup_uri_free (uri);
 
+       if (user) {
+               g_signal_connect (msg, "authenticate",
+                                 G_CALLBACK (authenticate),
+                                 (gpointer)user);
+       }
        g_signal_connect (msg, "got_headers",
                          G_CALLBACK (prompt_check), &state);
        g_signal_connect (msg, "got_headers",
@@ -409,12 +425,9 @@ do_ntlm_round (SoupURI *base_uri, gboolean use_ntlm,
 
        session = soup_test_session_new (SOUP_TYPE_SESSION, NULL);
 
-       if (user) {
-               g_signal_connect (session, "authenticate",
-                                 G_CALLBACK (authenticate), (char *)user);
-               if (use_ntlm && !use_builtin_ntlm)
-                       g_setenv ("NTLMUSER", user, TRUE);
-       }
+       if (user && use_ntlm && !use_builtin_ntlm)
+               g_setenv ("NTLMUSER", user, TRUE);
+
        if (use_ntlm) {
                SoupAuthManager *auth_manager;
                SoupAuth *ntlm;
@@ -432,7 +445,7 @@ do_ntlm_round (SoupURI *base_uri, gboolean use_ntlm,
         * asking.
         */
        authenticated_ntlm = FALSE;
-       do_message (session, base_uri, "/noauth/",
+       do_message (session, base_uri, "/noauth/", user,
                    FALSE, use_ntlm,
                    FALSE, FALSE,
                    SOUP_STATUS_OK);
@@ -448,7 +461,7 @@ do_ntlm_round (SoupURI *base_uri, gboolean use_ntlm,
         * previous step, then we'll just immediately get a 401 here.
         * So in no case will we see the client try to do_ntlm.
         */
-       do_message (session, base_uri, "/alice/",
+       do_message (session, base_uri, "/alice/", user,
                    !alice_via_ntlm, FALSE,
                    !alice_via_ntlm, alice_via_basic,
                    alice ? SOUP_STATUS_OK :
@@ -461,14 +474,14 @@ do_ntlm_round (SoupURI *base_uri, gboolean use_ntlm,
         * to send auth without it being requested, so also won't get
         * prompts. But Bob/nobody will.
         */
-       do_message (session, base_uri, "/alice/404",
+       do_message (session, base_uri, "/alice/404", user,
                    !alice, bob_via_ntlm,
                    !alice, alice_via_basic,
                    alice ? SOUP_STATUS_NOT_FOUND :
                    SOUP_STATUS_UNAUTHORIZED);
 
        /* 4. Should be exactly the same as #3, except the status code */
-       do_message (session, base_uri, "/alice/",
+       do_message (session, base_uri, "/alice/", user,
                    !alice, bob_via_ntlm,
                    !alice, alice_via_basic,
                    alice ? SOUP_STATUS_OK :
@@ -480,7 +493,7 @@ do_ntlm_round (SoupURI *base_uri, gboolean use_ntlm,
         * (and fail). Bob-via-NTLM will try to do NTLM right away and
         * succeed.
         */
-       do_message (session, base_uri, "/bob/",
+       do_message (session, base_uri, "/bob/", user,
                    !bob_via_ntlm, bob_via_ntlm,
                    !bob_via_ntlm, alice_via_basic,
                    bob ? SOUP_STATUS_OK :
@@ -492,7 +505,7 @@ do_ntlm_round (SoupURI *base_uri, gboolean use_ntlm,
         * still knows about this path, so will try Basic right away
         * and succeed.
         */
-       do_message (session, base_uri, "/alice/",
+       do_message (session, base_uri, "/alice/", user,
                    !alice_via_ntlm, alice_via_ntlm,
                    !alice_via_ntlm, alice_via_basic,
                    alice ? SOUP_STATUS_OK :
@@ -502,7 +515,7 @@ do_ntlm_round (SoupURI *base_uri, gboolean use_ntlm,
         * Since Bob-via-NTLM is unauthenticated at this point, he'll try
         * NTLM before realizing that the server doesn't support it.
         */
-       do_message (session, base_uri, "/basic/",
+       do_message (session, base_uri, "/basic/", user,
                    FALSE, bob_via_ntlm,
                    TRUE, user != NULL,
                    user != NULL ? SOUP_STATUS_OK :
@@ -513,7 +526,7 @@ do_ntlm_round (SoupURI *base_uri, gboolean use_ntlm,
         * previous NTLM connections will have been closed by the 401
         * from /basic). Non-NTLM users will be prompted for either.
         */
-       do_message (session, base_uri, "/either/",
+       do_message (session, base_uri, "/either/", user,
                    !use_ntlm, use_ntlm,
                    !use_ntlm, !use_ntlm && user != NULL,
                    user != NULL ? SOUP_STATUS_OK :
@@ -617,23 +630,30 @@ do_ntlm_test (TestServer *ts,
        do_ntlm_round (ts->uri, test->conn_uses_ntlm, test->user, use_builtin_ntlm);
 }
 
-static void
-retry_test_authenticate (SoupSession *session, SoupMessage *msg,
-                        SoupAuth *auth, gboolean retrying,
-                        gpointer user_data)
+static gboolean
+retry_test_authenticate (SoupMessage *msg,
+                        SoupAuth    *auth,
+                        gboolean     retrying,
+                        gboolean    *retried)
 {
-       gboolean *retried = user_data;
-
        if (!retrying) {
                /* server_callback doesn't actually verify the password,
                 * only the username. So we pass an incorrect username
                 * rather than an incorrect password.
                 */
                soup_auth_authenticate (auth, "wrong", "password");
-       } else if (!*retried) {
+
+               return TRUE;
+       }
+
+       if (!*retried) {
                soup_auth_authenticate (auth, "alice", "password");
                *retried = TRUE;
+
+               return TRUE;
        }
+
+       return FALSE;
 }
 
 static void
@@ -655,11 +675,11 @@ do_retrying_test (TestServer *ts,
        session = soup_test_session_new (SOUP_TYPE_SESSION,
                                         SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_AUTH_NTLM,
                                         NULL);
-       g_signal_connect (session, "authenticate",
-                         G_CALLBACK (retry_test_authenticate), &retried);
 
        uri = soup_uri_new_with_base (ts->uri, "/alice");
        msg = soup_message_new_from_uri ("GET", uri);
+       g_signal_connect (msg, "authenticate",
+                         G_CALLBACK (retry_test_authenticate), &retried);
        soup_uri_free (uri);
 
        body = soup_test_session_send (session, msg, NULL, NULL);
@@ -677,12 +697,11 @@ do_retrying_test (TestServer *ts,
        session = soup_test_session_new (SOUP_TYPE_SESSION,
                                         SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_AUTH_NTLM,
                                         NULL);
-       g_signal_connect (session, "authenticate",
-                         G_CALLBACK (retry_test_authenticate), &retried);
        retried = FALSE;
-
        uri = soup_uri_new_with_base (ts->uri, "/bob");
        msg = soup_message_new_from_uri ("GET", uri);
+       g_signal_connect (msg, "authenticate",
+                         G_CALLBACK (retry_test_authenticate), &retried);
        soup_uri_free (uri);
 
        body = soup_test_session_send (session, msg, NULL, NULL);
diff --git a/tests/proxy-test.c b/tests/proxy-test.c
index 1f5e646a..bc6fb2b8 100644
--- a/tests/proxy-test.c
+++ b/tests/proxy-test.c
@@ -41,24 +41,18 @@ static const char *proxy_names[] = {
 static GProxyResolver *proxy_resolvers[3];
 static const char *ignore_hosts[] = { "localhost", NULL };
 
-static void
-authenticate (SoupSession *session, SoupMessage *msg,
-             SoupAuth *auth, gboolean retrying, gpointer data)
+static gboolean
+authenticate (SoupMessage *msg,
+             SoupAuth    *auth,
+             gboolean     retrying)
 {
-       if (msg->status_code == SOUP_STATUS_UNAUTHORIZED) {
-               soup_test_assert (!soup_auth_is_for_proxy (auth),
-                                 "got proxy auth object for 401");
-       } else if (msg->status_code == SOUP_STATUS_PROXY_UNAUTHORIZED) {
-               soup_test_assert (soup_auth_is_for_proxy (auth),
-                                 "got regular auth object for 407");
-       } else {
-               soup_test_assert (FALSE,
-                                 "got authenticate signal with status %d\n",
-                                 msg->status_code);
+       if (!retrying) {
+               soup_auth_authenticate (auth, "user1", "realm1");
+
+               return TRUE;
        }
 
-       if (!retrying)
-               soup_auth_authenticate (auth, "user1", "realm1");
+       return FALSE;
 }
 
 static void
@@ -97,8 +91,6 @@ test_url (const char *url, int proxy, guint expected, gboolean close)
                                         SOUP_SESSION_PROXY_RESOLVER, proxy_resolvers[proxy],
                                         SOUP_SESSION_SSL_STRICT, FALSE,
                                         NULL);
-       g_signal_connect (session, "authenticate",
-                         G_CALLBACK (authenticate), NULL);
 
        msg = soup_message_new (SOUP_METHOD_GET, url);
        if (!msg) {
@@ -106,6 +98,9 @@ test_url (const char *url, int proxy, guint expected, gboolean close)
                exit (1);
        }
 
+       g_signal_connect (msg, "authenticate",
+                          G_CALLBACK (authenticate), NULL);
+
        if (close) {
                /* FIXME g_test_bug ("611663") */
                g_signal_connect (msg, "starting",
@@ -250,6 +245,8 @@ do_proxy_auth_request (const char *url, SoupSession *session, gboolean do_read)
        GError *error = NULL;
 
        msg = soup_message_new ("GET", url);
+       g_signal_connect (msg, "authenticate",
+                         G_CALLBACK (authenticate), NULL);
 
        stream = soup_test_request_send (session, msg, NULL, 0, &error);
        g_assert_no_error (error);
@@ -299,8 +296,6 @@ do_proxy_auth_cache_test (void)
                                         SOUP_SESSION_PROXY_RESOLVER, proxy_resolvers[AUTH_PROXY],
                                         SOUP_SESSION_ADD_FEATURE, cache,
                                         NULL);
-       g_signal_connect (session, "authenticate",
-                         G_CALLBACK (authenticate), NULL);
 
        url = g_strconcat (HTTP_SERVER, "/Basic/realm1/", NULL);
 
diff --git a/tests/pull-api-test.c b/tests/pull-api-test.c
index edb4726d..24c4f8e1 100644
--- a/tests/pull-api-test.c
+++ b/tests/pull-api-test.c
@@ -4,12 +4,18 @@
 
 static GBytes *correct_response;
 
-static void
-authenticate (SoupSession *session, SoupMessage *msg,
-             SoupAuth *auth, gboolean retrying, gpointer data)
+static gboolean
+authenticate (SoupMessage *msg,
+             SoupAuth    *auth,
+             gboolean     retrying)
 {
-       if (!retrying)
+       if (!retrying) {
                soup_auth_authenticate (auth, "user2", "realm2");
+
+               return TRUE;
+       }
+
+       return FALSE;
 }
 
 #ifdef HAVE_APACHE
@@ -97,6 +103,9 @@ do_fully_async_test (SoupSession *session,
        ad.read_so_far = 0;
        ad.expected_status = expected_status;
 
+       g_signal_connect (msg, "authenticate",
+                         G_CALLBACK (authenticate), NULL);
+
        /* Connect to "got_headers", from which we'll decide where to
         * go next.
         */
@@ -192,8 +201,6 @@ do_fast_async_test (gconstpointer data)
        SOUP_TEST_SKIP_IF_NO_APACHE;
 
        session = soup_test_session_new (SOUP_TYPE_SESSION, NULL);
-       g_signal_connect (session, "authenticate",
-                         G_CALLBACK (authenticate), NULL);
        do_fully_async_test (session, base_uri, "/",
                             TRUE, SOUP_STATUS_OK);
        do_fully_async_test (session, base_uri, "/Basic/realm1/",
@@ -212,8 +219,6 @@ do_slow_async_test (gconstpointer data)
        SOUP_TEST_SKIP_IF_NO_APACHE;
 
        session = soup_test_session_new (SOUP_TYPE_SESSION, NULL);
-       g_signal_connect (session, "authenticate",
-                         G_CALLBACK (authenticate), NULL);
        do_fully_async_test (session, base_uri, "/",
                             FALSE, SOUP_STATUS_OK);
        do_fully_async_test (session, base_uri, "/Basic/realm1/",


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]