libsoup r1045 - in trunk: . docs/reference libsoup tests
- From: danw svn gnome org
- To: svn-commits-list gnome org
- Subject: libsoup r1045 - in trunk: . docs/reference libsoup tests
- Date: Wed, 16 Jan 2008 21:49:55 +0000 (GMT)
Author: danw
Date: Wed Jan 16 21:49:54 2008
New Revision: 1045
URL: http://svn.gnome.org/viewvc/libsoup?rev=1045&view=rev
Log:
* libsoup/soup-auth-manager.c (authorize_handler, etc): Allow the
session authenticate signal to be handled asynchronously, by
pausing the message and then authenticating the auth later.
(auth_type_compare_func): make this work. oops.
(extract_challenge): plug leak
* libsoup/soup-auth-manager-ntlm.c: Make this work async too.
* libsoup/soup-headers.c (soup_header_parse_list):
(soup_header_parse_param_list): plug leaks
* tests/auth-test.c (do_async_auth_test): test async auth
* docs/reference/client-howto.xml (Handling Authentication):
mention async auth
Modified:
trunk/ChangeLog
trunk/docs/reference/client-howto.xml
trunk/libsoup/soup-auth-manager-ntlm.c
trunk/libsoup/soup-auth-manager.c
trunk/libsoup/soup-headers.c
trunk/libsoup/soup-session.c
trunk/tests/auth-test.c
Modified: trunk/docs/reference/client-howto.xml
==============================================================================
--- trunk/docs/reference/client-howto.xml (original)
+++ trunk/docs/reference/client-howto.xml Wed Jan 16 21:49:54 2008
@@ -345,6 +345,19 @@
message to fail (with status 401 or 407).
</para>
+<para>
+If you need to handle authentication asynchronously (eg, to pop up a
+password dialog without recursively entering the main loop), you can
+do that as well. Just call <link
+linkend="soup-session-pause-message"><function>soup_session_pause_message</function></link>
+on the message before returning from the signal handler, and
+<function>g_object_ref</function> the <type>SoupAuth</type>. Then,
+later on, after calling <function>soup_auth_authenticate</function>
+(or deciding not to), call <link
+linkend="soup-session-unpause-message"><function>soup_session_unpause_message</function></link>
+to resume the paused message.
+</para>
+
</refsect2>
<refsect2>
Modified: trunk/libsoup/soup-auth-manager-ntlm.c
==============================================================================
--- trunk/libsoup/soup-auth-manager-ntlm.c (original)
+++ trunk/libsoup/soup-auth-manager-ntlm.c Wed Jan 16 21:49:54 2008
@@ -33,6 +33,9 @@
SoupSocket *socket;
SoupNTLMState state;
char *response_header;
+
+ char *nonce, *domain;
+ SoupAuth *auth;
} SoupNTLMConnection;
struct SoupAuthManagerNTLM {
@@ -72,6 +75,10 @@
free_ntlm_connection (SoupNTLMConnection *conn)
{
g_free (conn->response_header);
+ g_free (conn->nonce);
+ g_free (conn->domain);
+ if (conn->auth)
+ g_object_unref (conn->auth);
g_slice_free (SoupNTLMConnection, conn);
}
@@ -161,11 +168,7 @@
{
SoupAuthManagerNTLM *ntlm = user_data;
SoupNTLMConnection *conn;
- SoupAuth *auth;
const char *val;
- char *nonce;
- const char *username = NULL, *password = NULL;
- char *slash, *domain;
conn = get_connection_for_msg (ntlm, msg);
if (!conn)
@@ -189,38 +192,15 @@
goto done;
}
- if (!soup_ntlm_parse_challenge (val, &nonce, &domain)) {
+ if (!soup_ntlm_parse_challenge (val, &conn->nonce, &conn->domain)) {
conn->state = SOUP_NTLM_FAILED;
goto done;
}
- auth = soup_auth_ntlm_new (domain, soup_message_get_uri (msg)->host);
- soup_session_emit_authenticate (ntlm->session, msg, auth, FALSE);
- username = soup_auth_ntlm_get_username (auth);
- password = soup_auth_ntlm_get_password (auth);
- if (!username || !password) {
- g_free (nonce);
- g_free (domain);
- g_object_unref (auth);
- goto done;
- }
-
- slash = strpbrk (username, "\\/");
- if (slash) {
- g_free (domain);
- domain = g_strdup (username);
- slash = domain + (slash - username);
- *slash = '\0';
- username = slash + 1;
- }
-
- conn->response_header =
- soup_ntlm_response (nonce, username, password, NULL, domain);
conn->state = SOUP_NTLM_RECEIVED_CHALLENGE;
-
- g_free (domain);
- g_free (nonce);
- g_object_unref (auth);
+ conn->auth = soup_auth_ntlm_new (conn->domain,
+ soup_message_get_uri (msg)->host);
+ soup_session_emit_authenticate (ntlm->session, msg, conn->auth, FALSE);
done:
/* Remove the WWW-Authenticate headers so the session won't try
@@ -234,14 +214,41 @@
{
SoupAuthManagerNTLM *ntlm = user_data;
SoupNTLMConnection *conn;
+ const char *username = NULL, *password = NULL;
+ char *slash, *domain;
conn = get_connection_for_msg (ntlm, msg);
- if (!conn)
+ if (!conn || !conn->auth)
return;
- if (conn->state == SOUP_NTLM_RECEIVED_CHALLENGE &&
- conn->response_header)
- soup_session_requeue_message (ntlm->session, msg);
+ username = soup_auth_ntlm_get_username (conn->auth);
+ password = soup_auth_ntlm_get_password (conn->auth);
+ if (!username || !password)
+ goto done;
+
+ slash = strpbrk (username, "\\/");
+ if (slash) {
+ domain = g_strdup (username);
+ slash = domain + (slash - username);
+ *slash = '\0';
+ username = slash + 1;
+ } else
+ domain = conn->domain;
+
+ conn->response_header = soup_ntlm_response (conn->nonce,
+ username, password,
+ NULL, domain);
+ soup_session_requeue_message (ntlm->session, msg);
+
+done:
+ if (domain != conn->domain)
+ g_free (domain);
+ g_free (conn->domain);
+ conn->domain = NULL;
+ g_free (conn->nonce);
+ conn->nonce = NULL;
+ g_object_unref (conn->auth);
+ conn->auth = NULL;
}
static void
Modified: trunk/libsoup/soup-auth-manager.c
==============================================================================
--- trunk/libsoup/soup-auth-manager.c (original)
+++ trunk/libsoup/soup-auth-manager.c Wed Jan 16 21:49:54 2008
@@ -99,10 +99,10 @@
static int
auth_type_compare_func (gconstpointer a, gconstpointer b)
{
- SoupAuthClass *auth1 = (SoupAuthClass *)a;
- SoupAuthClass *auth2 = (SoupAuthClass *)b;
+ SoupAuthClass **auth1 = (SoupAuthClass **)a;
+ SoupAuthClass **auth2 = (SoupAuthClass **)b;
- return auth2->strength - auth1->strength;
+ return (*auth2)->strength - (*auth1)->strength;
}
void
@@ -180,8 +180,10 @@
g_ascii_isspace (item[schemelen]))
break;
}
- if (!i)
+ if (!i) {
+ soup_header_free_list (items);
return NULL;
+ }
/* The challenge extends from this item until the end, or until
* the next item that has a space before an equals sign.
@@ -315,9 +317,10 @@
return soup_auth_is_authenticated (auth);
}
-static gboolean
-update_auth (SoupAuthManager *manager, SoupMessage *msg)
+static void
+update_auth (SoupMessage *msg, gpointer user_data)
{
+ SoupAuthManager *manager = user_data;
SoupAuthHost *host;
SoupAuth *auth, *prior_auth, *old_auth;
const char *path;
@@ -336,7 +339,7 @@
} else {
auth = create_auth (manager, msg);
if (!auth)
- return FALSE;
+ return;
}
auth_info = soup_auth_get_info (auth);
@@ -378,13 +381,24 @@
}
/* If we need to authenticate, try to do it. */
- return authenticate_auth (manager, auth, msg,
- prior_auth_failed, FALSE);
+ authenticate_auth (manager, auth, msg,
+ prior_auth_failed, FALSE);
}
-static gboolean
-update_proxy_auth (SoupAuthManager *manager, SoupMessage *msg)
+static void
+requeue_if_authenticated (SoupMessage *msg, gpointer user_data)
+{
+ SoupAuthManager *manager = user_data;
+ SoupAuth *auth = lookup_auth (manager, msg);
+
+ if (auth && soup_auth_is_authenticated (auth))
+ soup_session_requeue_message (manager->session, msg);
+}
+
+static void
+update_proxy_auth (SoupMessage *msg, gpointer user_data)
{
+ SoupAuthManager *manager = user_data;
SoupAuth *prior_auth;
gboolean prior_auth_failed = FALSE;
@@ -398,29 +412,21 @@
if (!manager->proxy_auth) {
manager->proxy_auth = create_auth (manager, msg);
if (!manager->proxy_auth)
- return FALSE;
+ return;
}
/* If we need to authenticate, try to do it. */
- return authenticate_auth (manager, manager->proxy_auth, msg,
- prior_auth_failed, TRUE);
+ authenticate_auth (manager, manager->proxy_auth, msg,
+ prior_auth_failed, TRUE);
}
static void
-authorize_handler (SoupMessage *msg, gpointer user_data)
+requeue_if_proxy_authenticated (SoupMessage *msg, gpointer user_data)
{
SoupAuthManager *manager = user_data;
+ SoupAuth *auth = manager->proxy_auth;
- if (update_auth (manager, msg))
- soup_session_requeue_message (manager->session, msg);
-}
-
-static void
-proxy_authorize_handler (SoupMessage *msg, gpointer user_data)
-{
- SoupAuthManager *manager = user_data;
-
- if (update_proxy_auth (manager, msg))
+ if (auth && soup_auth_is_authenticated (auth))
soup_session_requeue_message (manager->session, msg);
}
@@ -436,16 +442,20 @@
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);
+ soup_message_add_status_code_handler (
msg, "got_body", SOUP_STATUS_UNAUTHORIZED,
- G_CALLBACK (authorize_handler), manager);
+ 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);
+ soup_message_add_status_code_handler (
msg, "got_body", SOUP_STATUS_PROXY_UNAUTHORIZED,
- G_CALLBACK (proxy_authorize_handler), manager);
+ G_CALLBACK (requeue_if_proxy_authenticated), manager);
}
-
-
Modified: trunk/libsoup/soup-headers.c
==============================================================================
--- trunk/libsoup/soup-headers.c (original)
+++ trunk/libsoup/soup-headers.c Wed Jan 16 21:49:54 2008
@@ -511,7 +511,7 @@
for (l = list; l; l = l->next)
g_free (l->data);
- g_slist_free (l);
+ g_slist_free (list);
}
/**
@@ -609,6 +609,7 @@
g_hash_table_insert (params, item, value);
}
+ g_slist_free (list);
return params;
}
Modified: trunk/libsoup/soup-session.c
==============================================================================
--- trunk/libsoup/soup-session.c (original)
+++ trunk/libsoup/soup-session.c Wed Jan 16 21:49:54 2008
@@ -257,6 +257,15 @@
* 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",
Modified: trunk/tests/auth-test.c
==============================================================================
--- trunk/tests/auth-test.c (original)
+++ trunk/tests/auth-test.c Wed Jan 16 21:49:54 2008
@@ -352,6 +352,148 @@
g_object_unref (msg);
}
+/* Async auth test. We queue three requests to /Basic/realm1, ensuring
+ * that they are sent in order. The first and third ones will be
+ * paused from the authentication callback. The second will be allowed
+ * to fail. Shortly after the third one requests auth, we'll provide
+ * the auth and unpause the two remaining messages, allowing them to
+ * succeed.
+ */
+
+static void
+async_authenticate (SoupSession *session, SoupMessage *msg,
+ SoupAuth *auth, gboolean retrying, gpointer data)
+{
+ 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);
+
+ /* The session will try to authenticate msg3 *before* sending
+ * it, because it already knows it's going to need the auth.
+ * Ignore that.
+ */
+ if (msg->status_code != SOUP_STATUS_UNAUTHORIZED) {
+ debug_printf (2, " (ignoring)\n");
+ return;
+ }
+
+ soup_session_pause_message (session, msg);
+ if (saved_auth)
+ *saved_auth = g_object_ref (auth);
+ g_main_loop_quit (loop);
+}
+
+static void
+async_finished (SoupSession *session, SoupMessage *msg, gpointer user_data)
+{
+ int *finished = user_data;
+ int id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (msg), "id"));
+
+ debug_printf (2, " async_finished msg%d\n", id);
+
+ (*finished)++;
+ if (*finished == 2)
+ g_main_loop_quit (loop);
+}
+
+static void
+do_async_auth_test (const char *base_uri)
+{
+ SoupSession *session;
+ SoupMessage *msg1, *msg2, *msg3, msg2_bak;
+ guint auth_id;
+ char *uri;
+ SoupAuth *auth = NULL;
+ int finished = 0;
+
+ debug_printf (1, "\nTesting async auth:\n");
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
+
+ uri = g_strconcat (base_uri, "Basic/realm1/", 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);
+
+ /* async_authenticate will pause msg1 and quit loop */
+
+ msg2 = soup_message_new ("GET", uri);
+ g_object_set_data (G_OBJECT (msg2), "id", GINT_TO_POINTER (2));
+ soup_session_send_message (session, msg2);
+
+ if (msg2->status_code == SOUP_STATUS_UNAUTHORIZED)
+ debug_printf (1, " msg2 failed as expected\n");
+ else {
+ debug_printf (1, " msg2 got wrong status! (%u)\n",
+ msg2->status_code);
+ errors++;
+ }
+
+ /* msg2 should be done at this point; assuming everything is
+ * working correctly, the session won't look at it again; we
+ * ensure that if it does, it will crash the test program.
+ */
+ memcpy (&msg2_bak, msg2, sizeof (SoupMessage));
+ memset (msg2, 0, sizeof (SoupMessage));
+
+ 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_object_ref (msg3);
+ soup_session_queue_message (session, msg3, async_finished, &finished);
+ g_main_loop_run (loop);
+ g_signal_handler_disconnect (session, auth_id);
+
+ /* async_authenticate will pause msg3 and quit loop */
+
+ /* Now do the auth, and restart */
+ if (auth) {
+ soup_auth_authenticate (auth, "user1", "realm1");
+ soup_session_unpause_message (session, msg1);
+ soup_session_unpause_message (session, msg3);
+
+ g_main_loop_run (loop);
+
+ /* async_finished will quit the loop */
+ } else {
+ debug_printf (1, " msg1 didn't get authenticate signal!\n");
+ errors++;
+ }
+
+ if (msg1->status_code == SOUP_STATUS_OK)
+ debug_printf (1, " msg1 succeeded\n");
+ else {
+ debug_printf (1, " msg1 FAILED! (%u %s)\n",
+ msg1->status_code, msg1->reason_phrase);
+ errors++;
+ }
+ if (msg3->status_code == SOUP_STATUS_OK)
+ debug_printf (1, " msg3 succeeded\n");
+ else {
+ debug_printf (1, " msg3 FAILED! (%u %s)\n",
+ msg3->status_code, msg3->reason_phrase);
+ errors++;
+ }
+
+ soup_session_abort (session);
+ g_object_unref (session);
+
+ g_object_unref (msg1);
+ g_object_unref (msg3);
+ memcpy (msg2, &msg2_bak, sizeof (SoupMessage));
+ g_object_unref (msg2);
+ g_object_unref (auth);
+ g_free (uri);
+}
+
int
main (int argc, char **argv)
{
@@ -417,6 +559,7 @@
g_object_unref (session);
/* And now for some regression tests */
+ loop = g_main_loop_new (NULL, TRUE);
debug_printf (1, "Testing pipelined auth (bug 271540):\n");
session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
@@ -437,8 +580,9 @@
}
g_free (uri);
- loop = g_main_loop_new (NULL, TRUE);
g_main_loop_run (loop);
+ soup_session_abort (session);
+ g_object_unref (session);
debug_printf (1, "\nTesting digest nonce expiration:\n");
@@ -516,6 +660,8 @@
*
*/
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
+
uri = g_strconcat (base_uri, "Digest/realm1/", NULL);
do_digest_nonce_test (session, "First", uri, TRUE, TRUE);
g_free (uri);
@@ -528,11 +674,14 @@
do_digest_nonce_test (session, "Fourth", uri, FALSE, FALSE);
g_free (uri);
- g_main_loop_unref (loop);
-
soup_session_abort (session);
g_object_unref (session);
+ /* Async auth */
+ do_async_auth_test (base_uri);
+
+ g_main_loop_unref (loop);
+
test_cleanup ();
return errors != 0;
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]