1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 2001-2003, Ximian, Inc.
6 #include "test-utils.h"
8 static SoupURI *base_uri;
11 gboolean client_sent_basic, client_sent_digest;
12 gboolean server_requested_basic, server_requested_digest;
17 curl_exited (GPid pid, int status, gpointer data)
19 gboolean *done = data;
22 test_data.succeeded = (status == 0);
26 do_test (SoupURI *base_uri, const char *path,
27 gboolean good_user, gboolean good_password,
28 gboolean offer_basic, gboolean offer_digest,
29 gboolean client_sends_basic, gboolean client_sends_digest,
30 gboolean server_requests_basic, gboolean server_requests_digest,
39 uri = soup_uri_new_with_base (base_uri, path);
40 uri_str = soup_uri_to_string (uri, FALSE);
43 args = g_ptr_array_new ();
44 g_ptr_array_add (args, "curl");
45 g_ptr_array_add (args, "--noproxy");
46 g_ptr_array_add (args, "*");
47 g_ptr_array_add (args, "-f");
48 g_ptr_array_add (args, "-s");
49 if (offer_basic || offer_digest) {
50 g_ptr_array_add (args, "-u");
53 g_ptr_array_add (args, "user:password");
55 g_ptr_array_add (args, "user:badpassword");
58 g_ptr_array_add (args, "baduser:password");
60 g_ptr_array_add (args, "baduser:badpassword");
63 if (offer_basic && offer_digest)
64 g_ptr_array_add (args, "--anyauth");
66 g_ptr_array_add (args, "--basic");
68 g_ptr_array_add (args, "--digest");
70 g_ptr_array_add (args, uri_str);
71 g_ptr_array_add (args, NULL);
73 memset (&test_data, 0, sizeof (test_data));
74 if (g_spawn_async (NULL, (char **)args->pdata, NULL,
75 G_SPAWN_SEARCH_PATH | G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL | G_SPAWN_DO_NOT_REAP_CHILD,
76 NULL, NULL, &pid, NULL)) {
78 g_child_watch_add (pid, curl_exited, &done);
81 g_main_context_iteration (NULL, TRUE);
83 test_data.succeeded = FALSE;
84 g_ptr_array_free (args, TRUE);
87 g_assert_cmpint (server_requests_basic, ==, test_data.server_requested_basic);
88 g_assert_cmpint (server_requests_digest, ==, test_data.server_requested_digest);
89 g_assert_cmpint (client_sends_basic, ==, test_data.client_sent_basic);
90 g_assert_cmpint (client_sends_digest, ==, test_data.client_sent_digest);
92 g_assert_cmpint (success, ==, test_data.succeeded);
95 #define TEST_USES_BASIC(t) (((t) & 1) == 1)
96 #define TEST_USES_DIGEST(t) (((t) & 2) == 2)
97 #define TEST_GOOD_USER(t) (((t) & 4) == 4)
98 #define TEST_GOOD_PASSWORD(t) (((t) & 8) == 8)
100 #define TEST_GOOD_AUTH(t) (TEST_GOOD_USER (t) && TEST_GOOD_PASSWORD (t))
101 #define TEST_PREEMPTIVE_BASIC(t) (TEST_USES_BASIC (t) && !TEST_USES_DIGEST (t))
104 do_server_auth_test (gconstpointer data)
106 int i = GPOINTER_TO_INT (data);
109 g_test_skip ("/usr/bin/curl is not available");
113 /* 1. No auth required. The server will ignore the
114 * Authorization headers completely, and the request
115 * will always succeed.
117 do_test (base_uri, "/foo",
118 TEST_GOOD_USER (i), TEST_GOOD_PASSWORD (i),
120 TEST_USES_BASIC (i), TEST_USES_DIGEST (i),
121 /* expected from client */
122 TEST_PREEMPTIVE_BASIC (i), FALSE,
123 /* expected from server */
128 /* 2. Basic auth required. The server will send
129 * "WWW-Authenticate: Basic" if the client fails to
130 * send an Authorization: Basic on the first request,
131 * or if it sends a bad password.
133 do_test (base_uri, "/Basic/foo",
134 TEST_GOOD_USER (i), TEST_GOOD_PASSWORD (i),
136 TEST_USES_BASIC (i), TEST_USES_DIGEST (i),
137 /* expected from client */
138 TEST_USES_BASIC (i), FALSE,
139 /* expected from server */
140 !TEST_PREEMPTIVE_BASIC (i) || !TEST_GOOD_AUTH (i), FALSE,
142 TEST_USES_BASIC (i) && TEST_GOOD_AUTH (i));
144 /* 3. Digest auth required. Simpler than the basic
145 * case because the client can't send Digest auth
148 do_test (base_uri, "/Digest/foo",
149 TEST_GOOD_USER (i), TEST_GOOD_PASSWORD (i),
151 TEST_USES_BASIC (i), TEST_USES_DIGEST (i),
152 /* expected from client */
153 TEST_PREEMPTIVE_BASIC (i), TEST_USES_DIGEST (i),
154 /* expected from server */
157 TEST_USES_DIGEST (i) && TEST_GOOD_AUTH (i));
159 /* 4. Any auth required. */
160 do_test (base_uri, "/Any/foo",
161 TEST_GOOD_USER (i), TEST_GOOD_PASSWORD (i),
163 TEST_USES_BASIC (i), TEST_USES_DIGEST (i),
164 /* expected from client */
165 TEST_PREEMPTIVE_BASIC (i), TEST_USES_DIGEST (i),
166 /* expected from server */
167 !TEST_PREEMPTIVE_BASIC (i) || !TEST_GOOD_AUTH (i), !TEST_PREEMPTIVE_BASIC (i) || !TEST_GOOD_AUTH (i),
169 (TEST_USES_BASIC (i) || TEST_USES_DIGEST (i)) && TEST_GOOD_AUTH (i));
171 /* 5. No auth required again. (Makes sure that
172 * SOUP_AUTH_DOMAIN_REMOVE_PATH works.)
174 do_test (base_uri, "/Any/Not/foo",
175 TEST_GOOD_USER (i), TEST_GOOD_PASSWORD (i),
177 TEST_USES_BASIC (i), TEST_USES_DIGEST (i),
178 /* expected from client */
179 TEST_PREEMPTIVE_BASIC (i), FALSE,
180 /* expected from server */
187 basic_auth_callback (SoupAuthDomain *auth_domain, SoupMessage *msg,
188 const char *username, const char *password, gpointer data)
190 return !strcmp (username, "user") && !strcmp (password, "password");
194 digest_auth_callback (SoupAuthDomain *auth_domain, SoupMessage *msg,
195 const char *username, gpointer data)
197 if (strcmp (username, "user") != 0)
200 /* Note: this is exactly how you *shouldn't* do it in the real
201 * world; you should have the pre-encoded password stored in a
202 * database of some sort rather than using the cleartext
203 * password in the callback.
205 return soup_auth_domain_digest_encode_password ("user",
211 server_callback (SoupServer *server, SoupMessage *msg,
212 const char *path, GHashTable *query,
213 SoupClientContext *context, gpointer data)
215 if (msg->method != SOUP_METHOD_GET && msg->method != SOUP_METHOD_HEAD) {
216 soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
220 soup_message_set_response (msg, "text/plain",
223 soup_message_set_status (msg, SOUP_STATUS_OK);
227 got_headers_callback (SoupMessage *msg, gpointer data)
231 header = soup_message_headers_get_one (msg->request_headers,
234 if (strstr (header, "Basic "))
235 test_data.client_sent_basic = TRUE;
236 if (strstr (header, "Digest "))
237 test_data.client_sent_digest = TRUE;
242 wrote_headers_callback (SoupMessage *msg, gpointer data)
246 header = soup_message_headers_get_list (msg->response_headers,
249 if (strstr (header, "Basic "))
250 test_data.server_requested_basic = TRUE;
251 if (strstr (header, "Digest "))
252 test_data.server_requested_digest = TRUE;
257 request_started_callback (SoupServer *server, SoupMessage *msg,
258 SoupClientContext *client, gpointer data)
260 g_signal_connect (msg, "got_headers",
261 G_CALLBACK (got_headers_callback), NULL);
262 g_signal_connect (msg, "wrote_headers",
263 G_CALLBACK (wrote_headers_callback), NULL);
266 static gboolean run_tests = TRUE;
268 static GOptionEntry no_test_entry[] = {
269 { "no-tests", 'n', G_OPTION_FLAG_REVERSE,
270 G_OPTION_ARG_NONE, &run_tests,
271 "Don't run tests, just run the test server", NULL },
276 main (int argc, char **argv)
280 SoupAuthDomain *auth_domain;
283 test_init (argc, argv, no_test_entry);
285 server = soup_test_server_new (FALSE);
286 g_signal_connect (server, "request_started",
287 G_CALLBACK (request_started_callback), NULL);
288 soup_server_add_handler (server, NULL,
289 server_callback, NULL, NULL);
291 auth_domain = soup_auth_domain_basic_new (
292 SOUP_AUTH_DOMAIN_REALM, "server-auth-test",
293 SOUP_AUTH_DOMAIN_ADD_PATH, "/Basic",
294 SOUP_AUTH_DOMAIN_ADD_PATH, "/Any",
295 SOUP_AUTH_DOMAIN_REMOVE_PATH, "/Any/Not",
296 SOUP_AUTH_DOMAIN_BASIC_AUTH_CALLBACK, basic_auth_callback,
298 soup_server_add_auth_domain (server, auth_domain);
299 g_object_unref (auth_domain);
301 auth_domain = soup_auth_domain_digest_new (
302 SOUP_AUTH_DOMAIN_REALM, "server-auth-test",
303 SOUP_AUTH_DOMAIN_ADD_PATH, "/Digest",
304 SOUP_AUTH_DOMAIN_ADD_PATH, "/Any",
305 SOUP_AUTH_DOMAIN_REMOVE_PATH, "/Any/Not",
306 SOUP_AUTH_DOMAIN_DIGEST_AUTH_CALLBACK, digest_auth_callback,
308 soup_server_add_auth_domain (server, auth_domain);
309 g_object_unref (auth_domain);
311 loop = g_main_loop_new (NULL, TRUE);
316 base_uri = soup_uri_new ("http://127.0.0.1");
317 soup_uri_set_port (base_uri, soup_server_get_port (server));
319 for (i = 0; i < 16; i++) {
321 const char *authtypes;
323 if (!TEST_GOOD_USER (i) && !TEST_GOOD_PASSWORD (i))
325 if (TEST_USES_BASIC (i)) {
326 if (TEST_USES_DIGEST (i))
327 authtypes = "basic+digest";
331 if (TEST_USES_DIGEST (i))
332 authtypes = "digest";
337 path = g_strdup_printf ("/server-auth/%s/%s-user%c%s-password",
339 TEST_GOOD_USER (i) ? "good" : "bad",
340 TEST_GOOD_USER (i) ? '/' : '\0',
341 TEST_GOOD_PASSWORD (i) ? "good" : "bad");
342 g_test_add_data_func (path, GINT_TO_POINTER (i), do_server_auth_test);
348 soup_uri_free (base_uri);
350 g_print ("Listening on port %d\n", soup_server_get_port (server));
351 g_main_loop_run (loop);
355 g_main_loop_unref (loop);
356 soup_test_server_quit_unref (server);