* Copyright (C) 2001-2003, Ximian, Inc.
*/
#include "test-utils.h"
static GUri *base_uri;
static struct {
gboolean client_sent_basic, client_sent_digest;
gboolean server_requested_basic, server_requested_digest;
gboolean succeeded;
} test_data;
static void
curl_exited (GPid pid, int status, gpointer data)
{
gboolean *done = data;
*done = TRUE;
test_data.succeeded = (status == 0);
}
static void
do_test (GUri *base_uri, const char *path,
gboolean good_user, gboolean good_password,
gboolean offer_basic, gboolean offer_digest,
gboolean client_sends_basic, gboolean client_sends_digest,
gboolean server_requests_basic, gboolean server_requests_digest,
gboolean success)
{
GUri *uri;
char *uri_str;
GPtrArray *args;
GPid pid;
gboolean done;
the encoded characters in tests 4. and 5. below. */
uri = g_uri_parse_relative (base_uri, path, G_URI_FLAGS_NONE, NULL);
uri_str = g_uri_to_string (uri);
g_uri_unref (uri);
args = g_ptr_array_new ();
g_ptr_array_add (args, "curl");
g_ptr_array_add (args, "--noproxy");
g_ptr_array_add (args, "*");
g_ptr_array_add (args, "-f");
g_ptr_array_add (args, "-s");
if (offer_basic || offer_digest) {
g_ptr_array_add (args, "-u");
if (good_user) {
if (good_password)
g_ptr_array_add (args, "user:password");
else
g_ptr_array_add (args, "user:badpassword");
} else {
if (good_password)
g_ptr_array_add (args, "baduser:password");
else
g_ptr_array_add (args, "baduser:badpassword");
}
if (offer_basic && offer_digest)
g_ptr_array_add (args, "--anyauth");
else if (offer_basic)
g_ptr_array_add (args, "--basic");
else
g_ptr_array_add (args, "--digest");
}
g_ptr_array_add (args, uri_str);
g_ptr_array_add (args, NULL);
memset (&test_data, 0, sizeof (test_data));
if (g_spawn_async (NULL, (char **)args->pdata, NULL,
G_SPAWN_SEARCH_PATH | G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL | G_SPAWN_DO_NOT_REAP_CHILD,
NULL, NULL, &pid, NULL)) {
done = FALSE;
g_child_watch_add (pid, curl_exited, &done);
while (!done)
g_main_context_iteration (NULL, TRUE);
} else
test_data.succeeded = FALSE;
g_ptr_array_free (args, TRUE);
g_free (uri_str);
g_assert_cmpint (server_requests_basic, ==, test_data.server_requested_basic);
g_assert_cmpint (server_requests_digest, ==, test_data.server_requested_digest);
g_assert_cmpint (client_sends_basic, ==, test_data.client_sent_basic);
g_assert_cmpint (client_sends_digest, ==, test_data.client_sent_digest);
g_assert_cmpint (success, ==, test_data.succeeded);
}
#define TEST_USES_BASIC(t) (((t) & 1) == 1)
#define TEST_USES_DIGEST(t) (((t) & 2) == 2)
#define TEST_GOOD_USER(t) (((t) & 4) == 4)
#define TEST_GOOD_PASSWORD(t) (((t) & 8) == 8)
#define TEST_GOOD_AUTH(t) (TEST_GOOD_USER (t) && TEST_GOOD_PASSWORD (t))
#define TEST_PREEMPTIVE_BASIC(t) (TEST_USES_BASIC (t) && !TEST_USES_DIGEST (t))
static void
do_server_auth_test (gconstpointer data)
{
int i = GPOINTER_TO_INT (data);
if (!have_curl()) {
g_test_skip ("curl is not available");
return;
}
* Authorization headers completely, and the request
* will always succeed.
*/
do_test (base_uri, "/foo",
TEST_GOOD_USER (i), TEST_GOOD_PASSWORD (i),
TEST_USES_BASIC (i), TEST_USES_DIGEST (i),
TEST_PREEMPTIVE_BASIC (i), FALSE,
FALSE, FALSE,
TRUE);
* "WWW-Authenticate: Basic" if the client fails to
* send an Authorization: Basic on the first request,
* or if it sends a bad password.
*/
do_test (base_uri, "/Basic/foo",
TEST_GOOD_USER (i), TEST_GOOD_PASSWORD (i),
TEST_USES_BASIC (i), TEST_USES_DIGEST (i),
TEST_USES_BASIC (i), FALSE,
!TEST_PREEMPTIVE_BASIC (i) || !TEST_GOOD_AUTH (i), FALSE,
TEST_USES_BASIC (i) && TEST_GOOD_AUTH (i));
* case because the client can't send Digest auth
* premptively.
*/
do_test (base_uri, "/Digest/foo",
TEST_GOOD_USER (i), TEST_GOOD_PASSWORD (i),
TEST_USES_BASIC (i), TEST_USES_DIGEST (i),
TEST_PREEMPTIVE_BASIC (i), TEST_USES_DIGEST (i),
FALSE, TRUE,
TEST_USES_DIGEST (i) && TEST_GOOD_AUTH (i));
*/
do_test (base_uri, "/Digest/A%20B",
TEST_GOOD_USER (i), TEST_GOOD_PASSWORD (i),
TEST_USES_BASIC (i), TEST_USES_DIGEST (i),
TEST_PREEMPTIVE_BASIC (i), TEST_USES_DIGEST (i),
FALSE, TRUE,
TEST_USES_DIGEST (i) && TEST_GOOD_AUTH (i));
*/
do_test (base_uri, "/Digest/A%20|%20B",
TEST_GOOD_USER (i), TEST_GOOD_PASSWORD (i),
TEST_USES_BASIC (i), TEST_USES_DIGEST (i),
TEST_PREEMPTIVE_BASIC (i), TEST_USES_DIGEST (i),
FALSE, TRUE,
TEST_USES_DIGEST (i) && TEST_GOOD_AUTH (i));
*/
do_test (base_uri, "/Digest/A௹B",
TEST_GOOD_USER (i), TEST_GOOD_PASSWORD (i),
TEST_USES_BASIC (i), TEST_USES_DIGEST (i),
TEST_PREEMPTIVE_BASIC (i), TEST_USES_DIGEST (i),
FALSE, TRUE,
TEST_USES_DIGEST (i) && TEST_GOOD_AUTH (i));
do_test (base_uri, "/Any/foo",
TEST_GOOD_USER (i), TEST_GOOD_PASSWORD (i),
TEST_USES_BASIC (i), TEST_USES_DIGEST (i),
TEST_PREEMPTIVE_BASIC (i), TEST_USES_DIGEST (i),
!TEST_PREEMPTIVE_BASIC (i) || !TEST_GOOD_AUTH (i), !TEST_PREEMPTIVE_BASIC (i) || !TEST_GOOD_AUTH (i),
(TEST_USES_BASIC (i) || TEST_USES_DIGEST (i)) && TEST_GOOD_AUTH (i));
* SoupAuthDomain:remove-path works.)
*/
do_test (base_uri, "/Any/Not/foo",
TEST_GOOD_USER (i), TEST_GOOD_PASSWORD (i),
TEST_USES_BASIC (i), TEST_USES_DIGEST (i),
TEST_PREEMPTIVE_BASIC (i), FALSE,
FALSE, FALSE,
TRUE);
}
static gboolean
basic_auth_callback (SoupAuthDomain *auth_domain,
SoupServerMessage *msg,
const char *username,
const char *password,
gpointer data)
{
return !strcmp (username, "user") && !strcmp (password, "password");
}
static char *
digest_auth_callback (SoupAuthDomain *auth_domain,
SoupServerMessage *msg,
const char *username,
gpointer data)
{
if (strcmp (username, "user") != 0)
return NULL;
* world; you should have the pre-encoded password stored in a
* database of some sort rather than using the cleartext
* password in the callback.
*/
return soup_auth_domain_digest_encode_password ("user",
"server-auth-test",
"password");
}
static void
server_callback (SoupServer *server,
SoupServerMessage *msg,
const char *path,
GHashTable *query,
gpointer data)
{
const char *method;
method = soup_server_message_get_method (msg);
if (method != SOUP_METHOD_GET && method != SOUP_METHOD_HEAD) {
soup_server_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED, NULL);
return;
}
soup_server_message_set_response (msg, "text/plain",
SOUP_MEMORY_STATIC,
"OK\r\n", 4);
soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL);
}
static void
got_headers_callback (SoupServerMessage *msg,
gpointer data)
{
const char *header;
header = soup_message_headers_get_one (soup_server_message_get_request_headers (msg),
"Authorization");
if (header) {
if (strstr (header, "Basic "))
test_data.client_sent_basic = TRUE;
if (strstr (header, "Digest "))
test_data.client_sent_digest = TRUE;
}
}
static void
wrote_headers_callback (SoupServerMessage *msg,
gpointer data)
{
const char *header;
header = soup_message_headers_get_list (soup_server_message_get_response_headers (msg),
"WWW-Authenticate");
if (header) {
if (strstr (header, "Basic "))
test_data.server_requested_basic = TRUE;
if (strstr (header, "Digest "))
test_data.server_requested_digest = TRUE;
}
}
static void
request_started_callback (SoupServer *server,
SoupServerMessage *msg,
gpointer data)
{
g_signal_connect (msg, "got-headers",
G_CALLBACK (got_headers_callback), NULL);
g_signal_connect (msg, "wrote-headers",
G_CALLBACK (wrote_headers_callback), NULL);
}
static gboolean run_tests = TRUE;
static GOptionEntry no_test_entry[] = {
{ "no-tests", 'n', G_OPTION_FLAG_REVERSE,
G_OPTION_ARG_NONE, &run_tests,
"Don't run tests, just run the test server", NULL },
{ NULL }
};
int
main (int argc, char **argv)
{
GMainLoop *loop;
SoupServer *server;
SoupAuthDomain *auth_domain;
int ret;
test_init (argc, argv, no_test_entry);
server = soup_test_server_new (SOUP_TEST_SERVER_DEFAULT);
g_signal_connect (server, "request_started",
G_CALLBACK (request_started_callback), NULL);
soup_server_add_handler (server, NULL,
server_callback, NULL, NULL);
auth_domain = soup_auth_domain_basic_new (
"realm", "server-auth-test",
"auth-callback", basic_auth_callback,
NULL);
soup_auth_domain_add_path (auth_domain, "/Basic");
soup_auth_domain_add_path (auth_domain, "/Any");
soup_auth_domain_remove_path (auth_domain, "/Any/Not");
soup_server_add_auth_domain (server, auth_domain);
g_object_unref (auth_domain);
auth_domain = soup_auth_domain_digest_new (
"realm", "server-auth-test",
"auth-callback", digest_auth_callback,
NULL);
soup_auth_domain_add_path (auth_domain, "/Digest");
soup_auth_domain_add_path (auth_domain, "/Any");
soup_auth_domain_remove_path (auth_domain, "/Any/Not");
soup_server_add_auth_domain (server, auth_domain);
g_object_unref (auth_domain);
loop = g_main_loop_new (NULL, TRUE);
base_uri = soup_test_server_get_uri (server, "http", NULL);
if (run_tests) {
int i;
for (i = 0; i < 16; i++) {
char *path;
const char *authtypes;
if (!TEST_GOOD_USER (i) && !TEST_GOOD_PASSWORD (i))
continue;
if (TEST_USES_BASIC (i)) {
if (TEST_USES_DIGEST (i))
authtypes = "basic+digest";
else
authtypes = "basic";
} else {
if (TEST_USES_DIGEST (i))
authtypes = "digest";
else
authtypes = "none";
}
path = g_strdup_printf ("/server-auth/%s/%s-user%c%s-password",
authtypes,
TEST_GOOD_USER (i) ? "good" : "bad",
TEST_GOOD_USER (i) ? '/' : '\0',
TEST_GOOD_PASSWORD (i) ? "good" : "bad");
g_test_add_data_func (path, GINT_TO_POINTER (i), do_server_auth_test);
g_free (path);
}
ret = g_test_run ();
} else {
g_print ("Listening on port %d\n", g_uri_get_port (base_uri));
g_main_loop_run (loop);
ret = 0;
}
g_uri_unref (base_uri);
g_main_loop_unref (loop);
soup_test_server_quit_unref (server);
if (run_tests)
test_cleanup ();
return ret;
}