1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 2001-2003, Ximian, Inc.
20 #include <libsoup/soup-address.h>
21 #include <libsoup/soup-auth-domain-basic.h>
22 #include <libsoup/soup-auth-domain-digest.h>
23 #include <libsoup/soup-message.h>
24 #include <libsoup/soup-server.h>
26 #include "test-utils.h"
29 gboolean client_sent_basic, client_sent_digest;
30 gboolean server_requested_basic, server_requested_digest;
35 curl_exited (GPid pid, int status, gpointer data)
37 gboolean *done = data;
40 test_data.succeeded = (status == 0);
44 do_test (int n, SoupURI *base_uri, const char *path,
45 gboolean good_user, gboolean good_password,
46 gboolean offer_basic, gboolean offer_digest,
47 gboolean client_sends_basic, gboolean client_sends_digest,
48 gboolean server_requests_basic, gboolean server_requests_digest,
57 debug_printf (1, "%2d. %s, %soffer Basic, %soffer Digest, %s user, %s password\n",
58 n, path, offer_basic ? "" : "don't ",
59 offer_digest ? "" : "don't ",
60 good_user ? "good" : "bad",
61 good_password ? "good" : "bad");
63 uri = soup_uri_new_with_base (base_uri, path);
64 uri_str = soup_uri_to_string (uri, FALSE);
67 args = g_ptr_array_new ();
68 g_ptr_array_add (args, "curl");
69 g_ptr_array_add (args, "-f");
70 g_ptr_array_add (args, "-s");
71 if (offer_basic || offer_digest) {
72 g_ptr_array_add (args, "-u");
75 g_ptr_array_add (args, "user:password");
77 g_ptr_array_add (args, "user:badpassword");
80 g_ptr_array_add (args, "baduser:password");
82 g_ptr_array_add (args, "baduser:badpassword");
85 if (offer_basic && offer_digest)
86 g_ptr_array_add (args, "--anyauth");
88 g_ptr_array_add (args, "--basic");
90 g_ptr_array_add (args, "--digest");
92 g_ptr_array_add (args, uri_str);
93 g_ptr_array_add (args, NULL);
95 memset (&test_data, 0, sizeof (test_data));
96 if (g_spawn_async (NULL, (char **)args->pdata, NULL,
97 G_SPAWN_SEARCH_PATH | G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL | G_SPAWN_DO_NOT_REAP_CHILD,
98 NULL, NULL, &pid, NULL)) {
100 g_child_watch_add (pid, curl_exited, &done);
103 g_main_context_iteration (NULL, TRUE);
105 test_data.succeeded = FALSE;
106 g_ptr_array_free (args, TRUE);
109 if (server_requests_basic != test_data.server_requested_basic) {
111 if (test_data.server_requested_basic)
112 debug_printf (1, " Server sent WWW-Authenticate: Basic, but shouldn't have!\n");
114 debug_printf (1, " Server didn't send WWW-Authenticate: Basic, but should have!\n");
116 if (server_requests_digest != test_data.server_requested_digest) {
118 if (test_data.server_requested_digest)
119 debug_printf (1, " Server sent WWW-Authenticate: Digest, but shouldn't have!\n");
121 debug_printf (1, " Server didn't send WWW-Authenticate: Digest, but should have!\n");
123 if (client_sends_basic != test_data.client_sent_basic) {
125 if (test_data.client_sent_basic)
126 debug_printf (1, " Client sent Authorization: Basic, but shouldn't have!\n");
128 debug_printf (1, " Client didn't send Authorization: Basic, but should have!\n");
130 if (client_sends_digest != test_data.client_sent_digest) {
132 if (test_data.client_sent_digest)
133 debug_printf (1, " Client sent Authorization: Digest, but shouldn't have!\n");
135 debug_printf (1, " Client didn't send Authorization: Digest, but should have!\n");
137 if (success && !test_data.succeeded) {
139 debug_printf (1, " Should have succeeded, but didn't!\n");
140 } else if (!success && test_data.succeeded) {
142 debug_printf (1, " Should not have succeeded, but did!\n");
147 do_auth_tests (SoupURI *base_uri)
150 gboolean use_basic, use_digest, good_user, good_password;
151 gboolean preemptive_basic, good_auth;
153 for (i = 0; i < 16; i++) {
154 use_basic = (i & 1) == 1;
155 use_digest = (i & 2) == 2;
156 good_user = (i & 4) == 4;
157 good_password = (i & 8) == 8;
159 good_auth = good_user && good_password;
161 /* Curl will preemptively send Basic if it's told to
162 * use Basic but not Digest.
164 preemptive_basic = use_basic && !use_digest;
166 /* 1. No auth required. The server will ignore the
167 * Authorization headers completely, and the request
168 * will always succeed.
170 do_test (n++, base_uri, "/foo",
171 good_user, good_password,
173 use_basic, use_digest,
174 /* expected from client */
175 preemptive_basic, FALSE,
176 /* expected from server */
181 /* 2. Basic auth required. The server will send
182 * "WWW-Authenticate: Basic" if the client fails to
183 * send an Authorization: Basic on the first request,
184 * or if it sends a bad password.
186 do_test (n++, base_uri, "/Basic/foo",
187 good_user, good_password,
189 use_basic, use_digest,
190 /* expected from client */
192 /* expected from server */
193 !preemptive_basic || !good_auth, FALSE,
195 use_basic && good_auth);
197 /* 3. Digest auth required. Simpler than the basic
198 * case because the client can't send Digest auth
201 do_test (n++, base_uri, "/Digest/foo",
202 good_user, good_password,
204 use_basic, use_digest,
205 /* expected from client */
206 preemptive_basic, use_digest,
207 /* expected from server */
210 use_digest && good_auth);
212 /* 4. Any auth required. */
213 do_test (n++, base_uri, "/Any/foo",
214 good_user, good_password,
216 use_basic, use_digest,
217 /* expected from client */
218 preemptive_basic, use_digest,
219 /* expected from server */
220 !preemptive_basic || !good_auth, !preemptive_basic || !good_auth,
222 (use_basic || use_digest) && good_auth);
224 /* 5. No auth required again. (Makes sure that
225 * SOUP_AUTH_DOMAIN_REMOVE_PATH works.)
227 do_test (n++, base_uri, "/Any/Not/foo",
228 good_user, good_password,
230 use_basic, use_digest,
231 /* expected from client */
232 preemptive_basic, FALSE,
233 /* expected from server */
241 basic_auth_callback (SoupAuthDomain *auth_domain, SoupMessage *msg,
242 const char *username, const char *password, gpointer data)
244 return !strcmp (username, "user") && !strcmp (password, "password");
248 digest_auth_callback (SoupAuthDomain *auth_domain, SoupMessage *msg,
249 const char *username, gpointer data)
251 if (strcmp (username, "user") != 0)
254 /* Note: this is exactly how you *shouldn't* do it in the real
255 * world; you should have the pre-encoded password stored in a
256 * database of some sort rather than using the cleartext
257 * password in the callback.
259 return soup_auth_domain_digest_encode_password ("user",
265 server_callback (SoupServer *server, SoupMessage *msg,
266 const char *path, GHashTable *query,
267 SoupClientContext *context, gpointer data)
269 if (msg->method != SOUP_METHOD_GET && msg->method != SOUP_METHOD_HEAD) {
270 soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
274 soup_message_set_response (msg, "text/plain",
277 soup_message_set_status (msg, SOUP_STATUS_OK);
281 got_headers_callback (SoupMessage *msg, gpointer data)
285 header = soup_message_headers_get_one (msg->request_headers,
288 if (strstr (header, "Basic "))
289 test_data.client_sent_basic = TRUE;
290 if (strstr (header, "Digest "))
291 test_data.client_sent_digest = TRUE;
296 wrote_headers_callback (SoupMessage *msg, gpointer data)
300 header = soup_message_headers_get_list (msg->response_headers,
303 if (strstr (header, "Basic "))
304 test_data.server_requested_basic = TRUE;
305 if (strstr (header, "Digest "))
306 test_data.server_requested_digest = TRUE;
311 request_started_callback (SoupServer *server, SoupMessage *msg,
312 SoupClientContext *client, gpointer data)
314 g_signal_connect (msg, "got_headers",
315 G_CALLBACK (got_headers_callback), NULL);
316 g_signal_connect (msg, "wrote_headers",
317 G_CALLBACK (wrote_headers_callback), NULL);
320 static gboolean run_tests = TRUE;
322 static GOptionEntry no_test_entry[] = {
323 { "no-tests", 'n', G_OPTION_FLAG_REVERSE,
324 G_OPTION_ARG_NONE, &run_tests,
325 "Don't run tests, just run the test server", NULL },
330 main (int argc, char **argv)
335 SoupAuthDomain *auth_domain;
337 test_init (argc, argv, no_test_entry);
339 server = soup_test_server_new (FALSE);
340 g_signal_connect (server, "request_started",
341 G_CALLBACK (request_started_callback), NULL);
342 soup_server_add_handler (server, NULL,
343 server_callback, NULL, NULL);
345 auth_domain = soup_auth_domain_basic_new (
346 SOUP_AUTH_DOMAIN_REALM, "server-auth-test",
347 SOUP_AUTH_DOMAIN_ADD_PATH, "/Basic",
348 SOUP_AUTH_DOMAIN_ADD_PATH, "/Any",
349 SOUP_AUTH_DOMAIN_REMOVE_PATH, "/Any/Not",
350 SOUP_AUTH_DOMAIN_BASIC_AUTH_CALLBACK, basic_auth_callback,
352 soup_server_add_auth_domain (server, auth_domain);
353 g_object_unref (auth_domain);
355 auth_domain = soup_auth_domain_digest_new (
356 SOUP_AUTH_DOMAIN_REALM, "server-auth-test",
357 SOUP_AUTH_DOMAIN_ADD_PATH, "/Digest",
358 SOUP_AUTH_DOMAIN_ADD_PATH, "/Any",
359 SOUP_AUTH_DOMAIN_REMOVE_PATH, "/Any/Not",
360 SOUP_AUTH_DOMAIN_DIGEST_AUTH_CALLBACK, digest_auth_callback,
362 soup_server_add_auth_domain (server, auth_domain);
363 g_object_unref (auth_domain);
365 loop = g_main_loop_new (NULL, TRUE);
368 uri = soup_uri_new ("http://127.0.0.1");
369 soup_uri_set_port (uri, soup_server_get_port (server));
373 printf ("Listening on port %d\n", soup_server_get_port (server));
374 g_main_loop_run (loop);
377 g_main_loop_unref (loop);
378 soup_test_server_quit_unref (server);