tests: do skipped tests properly
[platform/upstream/libsoup.git] / tests / server-auth-test.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2001-2003, Ximian, Inc.
4  */
5
6 #include "test-utils.h"
7
8 #ifdef HAVE_CURL
9
10 static struct {
11         gboolean client_sent_basic, client_sent_digest;
12         gboolean server_requested_basic, server_requested_digest;
13         gboolean succeeded;
14 } test_data;
15
16 static void
17 curl_exited (GPid pid, int status, gpointer data)
18 {
19         gboolean *done = data;
20
21         *done = TRUE;
22         test_data.succeeded = (status == 0);
23 }
24
25 static void
26 do_test (int n, 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,
31          gboolean success)
32 {
33         SoupURI *uri;
34         char *uri_str;
35         GPtrArray *args;
36         GPid pid;
37         gboolean done;
38
39         debug_printf (1, "%2d. %s, %soffer Basic, %soffer Digest, %s user, %s password\n",
40                       n, path, offer_basic ? "" : "don't ",
41                       offer_digest ? "" : "don't ",
42                       good_user ? "good" : "bad",
43                       good_password ? "good" : "bad");
44
45         uri = soup_uri_new_with_base (base_uri, path);
46         uri_str = soup_uri_to_string (uri, FALSE);
47         soup_uri_free (uri);
48
49         args = g_ptr_array_new ();
50         g_ptr_array_add (args, "curl");
51         g_ptr_array_add (args, "-f");
52         g_ptr_array_add (args, "-s");
53         if (offer_basic || offer_digest) {
54                 g_ptr_array_add (args, "-u");
55                 if (good_user) {
56                         if (good_password)
57                                 g_ptr_array_add (args, "user:password");
58                         else
59                                 g_ptr_array_add (args, "user:badpassword");
60                 } else {
61                         if (good_password)
62                                 g_ptr_array_add (args, "baduser:password");
63                         else
64                                 g_ptr_array_add (args, "baduser:badpassword");
65                 }
66
67                 if (offer_basic && offer_digest)
68                         g_ptr_array_add (args, "--anyauth");
69                 else if (offer_basic)
70                         g_ptr_array_add (args, "--basic");
71                 else
72                         g_ptr_array_add (args, "--digest");
73         }
74         g_ptr_array_add (args, uri_str);
75         g_ptr_array_add (args, NULL);
76
77         memset (&test_data, 0, sizeof (test_data));
78         if (g_spawn_async (NULL, (char **)args->pdata, NULL,
79                            G_SPAWN_SEARCH_PATH | G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL | G_SPAWN_DO_NOT_REAP_CHILD,
80                            NULL, NULL, &pid, NULL)) {
81                 done = FALSE;
82                 g_child_watch_add (pid, curl_exited, &done);
83
84                 while (!done)
85                         g_main_context_iteration (NULL, TRUE);
86         } else
87                 test_data.succeeded = FALSE;
88         g_ptr_array_free (args, TRUE);
89         g_free (uri_str);
90
91         if (server_requests_basic != test_data.server_requested_basic) {
92                 errors++;
93                 if (test_data.server_requested_basic)
94                         debug_printf (1, "  Server sent WWW-Authenticate: Basic, but shouldn't have!\n");
95                 else
96                         debug_printf (1, "  Server didn't send WWW-Authenticate: Basic, but should have!\n");
97         }
98         if (server_requests_digest != test_data.server_requested_digest) {
99                 errors++;
100                 if (test_data.server_requested_digest)
101                         debug_printf (1, "  Server sent WWW-Authenticate: Digest, but shouldn't have!\n");
102                 else
103                         debug_printf (1, "  Server didn't send WWW-Authenticate: Digest, but should have!\n");
104         }
105         if (client_sends_basic != test_data.client_sent_basic) {
106                 errors++;
107                 if (test_data.client_sent_basic)
108                         debug_printf (1, "  Client sent Authorization: Basic, but shouldn't have!\n");
109                 else
110                         debug_printf (1, "  Client didn't send Authorization: Basic, but should have!\n");
111         }
112         if (client_sends_digest != test_data.client_sent_digest) {
113                 errors++;
114                 if (test_data.client_sent_digest)
115                         debug_printf (1, "  Client sent Authorization: Digest, but shouldn't have!\n");
116                 else
117                         debug_printf (1, "  Client didn't send Authorization: Digest, but should have!\n");
118         }
119         if (success && !test_data.succeeded) {
120                 errors++;
121                 debug_printf (1, "  Should have succeeded, but didn't!\n");
122         } else if (!success && test_data.succeeded) {
123                 errors++;
124                 debug_printf (1, "  Should not have succeeded, but did!\n");
125         }
126 }
127
128 static void
129 do_auth_tests (SoupURI *base_uri)
130 {
131         int i, n = 1;
132         gboolean use_basic, use_digest, good_user, good_password;
133         gboolean preemptive_basic, good_auth;
134
135         for (i = 0; i < 16; i++) {
136                 use_basic     = (i & 1) == 1;
137                 use_digest    = (i & 2) == 2;
138                 good_user     = (i & 4) == 4;
139                 good_password = (i & 8) == 8;
140
141                 good_auth = good_user && good_password;
142
143                 /* Curl will preemptively send Basic if it's told to
144                  * use Basic but not Digest.
145                  */
146                 preemptive_basic = use_basic && !use_digest;
147
148                 /* 1. No auth required. The server will ignore the
149                  * Authorization headers completely, and the request
150                  * will always succeed.
151                  */
152                 do_test (n++, base_uri, "/foo",
153                          good_user, good_password,
154                          /* request */
155                          use_basic, use_digest,
156                          /* expected from client */
157                          preemptive_basic, FALSE,
158                          /* expected from server */
159                          FALSE, FALSE,
160                          /* success? */
161                          TRUE);
162
163                 /* 2. Basic auth required. The server will send
164                  * "WWW-Authenticate: Basic" if the client fails to
165                  * send an Authorization: Basic on the first request,
166                  * or if it sends a bad password.
167                  */
168                 do_test (n++, base_uri, "/Basic/foo",
169                          good_user, good_password,
170                          /* request */
171                          use_basic, use_digest,
172                          /* expected from client */
173                          use_basic, FALSE,
174                          /* expected from server */
175                          !preemptive_basic || !good_auth, FALSE,
176                          /* success? */
177                          use_basic && good_auth);
178
179                 /* 3. Digest auth required. Simpler than the basic
180                  * case because the client can't send Digest auth
181                  * premptively.
182                  */
183                 do_test (n++, base_uri, "/Digest/foo",
184                          good_user, good_password,
185                          /* request */
186                          use_basic, use_digest,
187                          /* expected from client */
188                          preemptive_basic, use_digest,
189                          /* expected from server */
190                          FALSE, TRUE,
191                          /* success? */
192                          use_digest && good_auth);
193
194                 /* 4. Any auth required. */
195                 do_test (n++, base_uri, "/Any/foo",
196                          good_user, good_password,
197                          /* request */
198                          use_basic, use_digest,
199                          /* expected from client */
200                          preemptive_basic, use_digest,
201                          /* expected from server */
202                          !preemptive_basic || !good_auth, !preemptive_basic || !good_auth,
203                          /* success? */
204                          (use_basic || use_digest) && good_auth);
205
206                 /* 5. No auth required again. (Makes sure that
207                  * SOUP_AUTH_DOMAIN_REMOVE_PATH works.)
208                  */
209                 do_test (n++, base_uri, "/Any/Not/foo",
210                          good_user, good_password,
211                          /* request */
212                          use_basic, use_digest,
213                          /* expected from client */
214                          preemptive_basic, FALSE,
215                          /* expected from server */
216                          FALSE, FALSE,
217                          /* success? */
218                          TRUE);
219         }
220 }
221
222 static gboolean
223 basic_auth_callback (SoupAuthDomain *auth_domain, SoupMessage *msg,
224                      const char *username, const char *password, gpointer data)
225 {
226         return !strcmp (username, "user") && !strcmp (password, "password");
227 }
228
229 static char *
230 digest_auth_callback (SoupAuthDomain *auth_domain, SoupMessage *msg,
231                       const char *username, gpointer data)
232 {
233         if (strcmp (username, "user") != 0)
234                 return NULL;
235
236         /* Note: this is exactly how you *shouldn't* do it in the real
237          * world; you should have the pre-encoded password stored in a
238          * database of some sort rather than using the cleartext
239          * password in the callback.
240          */
241         return soup_auth_domain_digest_encode_password ("user",
242                                                         "server-auth-test",
243                                                         "password");
244 }
245
246 static void
247 server_callback (SoupServer *server, SoupMessage *msg,
248                  const char *path, GHashTable *query,
249                  SoupClientContext *context, gpointer data)
250 {
251         if (msg->method != SOUP_METHOD_GET && msg->method != SOUP_METHOD_HEAD) {
252                 soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
253                 return;
254         }
255
256         soup_message_set_response (msg, "text/plain",
257                                    SOUP_MEMORY_STATIC,
258                                    "OK\r\n", 4);
259         soup_message_set_status (msg, SOUP_STATUS_OK);
260 }
261
262 static void
263 got_headers_callback (SoupMessage *msg, gpointer data)
264 {
265         const char *header;
266
267         header = soup_message_headers_get_one (msg->request_headers,
268                                                "Authorization");
269         if (header) {
270                 if (strstr (header, "Basic "))
271                         test_data.client_sent_basic = TRUE;
272                 if (strstr (header, "Digest "))
273                         test_data.client_sent_digest = TRUE;
274         }
275 }
276
277 static void
278 wrote_headers_callback (SoupMessage *msg, gpointer data)
279 {
280         const char *header;
281
282         header = soup_message_headers_get_list (msg->response_headers,
283                                                 "WWW-Authenticate");
284         if (header) {
285                 if (strstr (header, "Basic "))
286                         test_data.server_requested_basic = TRUE;
287                 if (strstr (header, "Digest "))
288                         test_data.server_requested_digest = TRUE;
289         }
290 }
291
292 static void
293 request_started_callback (SoupServer *server, SoupMessage *msg,
294                           SoupClientContext *client, gpointer data)
295 {
296         g_signal_connect (msg, "got_headers",
297                           G_CALLBACK (got_headers_callback), NULL);
298         g_signal_connect (msg, "wrote_headers",
299                           G_CALLBACK (wrote_headers_callback), NULL);
300 }
301
302 static gboolean run_tests = TRUE;
303
304 static GOptionEntry no_test_entry[] = {
305         { "no-tests", 'n', G_OPTION_FLAG_REVERSE,
306           G_OPTION_ARG_NONE, &run_tests,
307           "Don't run tests, just run the test server", NULL },
308         { NULL }
309 };
310
311 int
312 main (int argc, char **argv)
313 {
314         GMainLoop *loop;
315         SoupServer *server;
316         SoupURI *uri;
317         SoupAuthDomain *auth_domain;
318
319         test_init (argc, argv, no_test_entry);
320
321         server = soup_test_server_new (FALSE);
322         g_signal_connect (server, "request_started",
323                           G_CALLBACK (request_started_callback), NULL);
324         soup_server_add_handler (server, NULL,
325                                  server_callback, NULL, NULL);
326
327         auth_domain = soup_auth_domain_basic_new (
328                 SOUP_AUTH_DOMAIN_REALM, "server-auth-test",
329                 SOUP_AUTH_DOMAIN_ADD_PATH, "/Basic",
330                 SOUP_AUTH_DOMAIN_ADD_PATH, "/Any",
331                 SOUP_AUTH_DOMAIN_REMOVE_PATH, "/Any/Not",
332                 SOUP_AUTH_DOMAIN_BASIC_AUTH_CALLBACK, basic_auth_callback,
333                 NULL);
334         soup_server_add_auth_domain (server, auth_domain);
335         g_object_unref (auth_domain);
336
337         auth_domain = soup_auth_domain_digest_new (
338                 SOUP_AUTH_DOMAIN_REALM, "server-auth-test",
339                 SOUP_AUTH_DOMAIN_ADD_PATH, "/Digest",
340                 SOUP_AUTH_DOMAIN_ADD_PATH, "/Any",
341                 SOUP_AUTH_DOMAIN_REMOVE_PATH, "/Any/Not",
342                 SOUP_AUTH_DOMAIN_DIGEST_AUTH_CALLBACK, digest_auth_callback,
343                 NULL);
344         soup_server_add_auth_domain (server, auth_domain);
345         g_object_unref (auth_domain);
346
347         loop = g_main_loop_new (NULL, TRUE);
348
349         if (run_tests) {
350                 uri = soup_uri_new ("http://127.0.0.1");
351                 soup_uri_set_port (uri, soup_server_get_port (server));
352                 do_auth_tests (uri);
353                 soup_uri_free (uri);
354         } else {
355                 g_print ("Listening on port %d\n", soup_server_get_port (server));
356                 g_main_loop_run (loop);
357         }
358
359         g_main_loop_unref (loop);
360         soup_test_server_quit_unref (server);
361
362         if (run_tests)
363                 test_cleanup ();
364         return errors != 0;
365 }
366
367 #else /* HAVE_CURL */
368
369 int
370 main (int argc, char **argv)
371 {
372         return 77; /* SKIP */
373 }
374
375 #endif