Fix the retry-on-broken-connection codepath for SoupRequest
[platform/upstream/libsoup.git] / tests / xmlrpc-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 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
9 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
10 #endif
11
12 static SoupSession *session;
13 static const char *default_uri = "http://127.0.0.1:47524/xmlrpc-server.php";
14 static const char *uri = NULL;
15 static gboolean server_test = FALSE;
16
17 static const char *const value_type[] = {
18         "BAD",
19         "int",
20         "boolean",
21         "string",
22         "double",
23         "datetime",
24         "base64",
25         "struct",
26         "array"
27 };
28
29 static gboolean
30 do_xmlrpc (const char *method, GValue *retval, ...)
31 {
32         SoupMessage *msg;
33         va_list args;
34         GValueArray *params;
35         GError *err = NULL;
36         char *body;
37
38         va_start (args, retval);
39         params = soup_value_array_from_args (args);
40         va_end (args);
41
42         body = soup_xmlrpc_build_method_call (method, params->values,
43                                               params->n_values);
44         g_value_array_free (params);
45         if (!body)
46                 return FALSE;
47
48         msg = soup_message_new ("POST", uri);
49         soup_message_set_request (msg, "text/xml", SOUP_MEMORY_TAKE,
50                                   body, strlen (body));
51         soup_session_send_message (session, msg);
52
53         if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
54                 debug_printf (1, "ERROR: %d %s\n", msg->status_code,
55                               msg->reason_phrase);
56                 g_object_unref (msg);
57                 return FALSE;
58         }
59
60         if (!soup_xmlrpc_parse_method_response (msg->response_body->data,
61                                                 msg->response_body->length,
62                                                 retval, &err)) {
63                 if (err) {
64                         debug_printf (1, "FAULT: %d %s\n", err->code, err->message);
65                         g_error_free (err);
66                 } else
67                         debug_printf (1, "ERROR: could not parse response\n");
68                 g_object_unref (msg);
69                 return FALSE;
70         }
71         g_object_unref (msg);
72
73         return TRUE;
74 }
75
76 static gboolean
77 check_xmlrpc (GValue *value, GType type, ...)
78 {
79         va_list args;
80
81         if (!G_VALUE_HOLDS (value, type)) {
82                 debug_printf (1, "ERROR: could not parse response\n");
83                 g_value_unset (value);
84                 return FALSE;
85         }
86
87         va_start (args, type);
88         SOUP_VALUE_GETV (value, type, args);
89         va_end (args);
90         return TRUE;
91 }
92
93 static gboolean
94 test_sum (void)
95 {
96         GValueArray *ints;
97         int i, val, sum, result;
98         GValue retval;
99         gboolean ok;
100
101         debug_printf (1, "sum (array of int -> int): ");
102
103         ints = g_value_array_new (10);
104         for (i = sum = 0; i < 10; i++) {
105                 val = g_random_int_range (0, 100);
106                 debug_printf (2, "%s%d", i == 0 ? "[" : ", ", val);
107                 soup_value_array_append (ints, G_TYPE_INT, val);
108                 sum += val;
109         }
110         debug_printf (2, "] -> ");
111
112         ok = (do_xmlrpc ("sum", &retval,
113                         G_TYPE_VALUE_ARRAY, ints,
114                         G_TYPE_INVALID) &&
115               check_xmlrpc (&retval, G_TYPE_INT, &result));
116         g_value_array_free (ints);
117
118         if (!ok)
119                 return FALSE;
120
121         debug_printf (2, "%d: ", result);
122         debug_printf (1, "%s\n", result == sum ? "OK!" : "WRONG!");
123         return result == sum;
124 }
125
126 static gboolean
127 test_countBools (void)
128 {
129         GValueArray *bools;
130         int i, trues, falses;
131         GValue retval;
132         int ret_trues, ret_falses;
133         gboolean val, ok;
134         GHashTable *result;
135
136         debug_printf (1, "countBools (array of boolean -> struct of ints): ");
137
138         bools = g_value_array_new (10);
139         for (i = trues = falses = 0; i < 10; i++) {
140                 val = g_random_boolean ();
141                 debug_printf (2, "%s%c", i == 0 ? "[" : ", ", val ? 'T' : 'F');
142                 soup_value_array_append (bools, G_TYPE_BOOLEAN, val);
143                 if (val)
144                         trues++;
145                 else
146                         falses++;
147         }
148         debug_printf (2, "] -> ");
149
150         ok = (do_xmlrpc ("countBools", &retval,
151                          G_TYPE_VALUE_ARRAY, bools,
152                          G_TYPE_INVALID) &&
153               check_xmlrpc (&retval, G_TYPE_HASH_TABLE, &result));
154         g_value_array_free (bools);
155         if (!ok)
156                 return FALSE;
157
158         if (!soup_value_hash_lookup (result, "true", G_TYPE_INT, &ret_trues)) {
159                 debug_printf (1, "NO 'true' value in response\n");
160                 return FALSE;
161         }
162         if (!soup_value_hash_lookup (result, "false", G_TYPE_INT, &ret_falses)) {
163                 debug_printf (1, "NO 'false' value in response\n");
164                 return FALSE;
165         }
166         g_hash_table_destroy (result);
167
168         debug_printf (2, "{ true: %d, false: %d } ", ret_trues, ret_falses);
169         ok = (trues == ret_trues) && (falses == ret_falses);
170         debug_printf (1, "%s\n", ok ? "OK!" : "WRONG!");
171         return ok;
172 }
173
174 static gboolean
175 test_md5sum (void)
176 {
177         GByteArray *data, *result;
178         int i;
179         GChecksum *checksum;
180         guchar digest[16];
181         gsize digest_len = sizeof (digest);
182         GValue retval;
183         gboolean ok;
184
185         debug_printf (1, "md5sum (base64 -> base64): ");
186
187         data = g_byte_array_new ();
188         g_byte_array_set_size (data, 256);
189         for (i = 0; i < data->len; i++)
190                 data->data[i] = (char)(g_random_int_range (0, 256));
191
192         checksum = g_checksum_new (G_CHECKSUM_MD5);
193         g_checksum_update (checksum, data->data, data->len);
194         g_checksum_get_digest (checksum, digest, &digest_len);
195         g_checksum_free (checksum);
196
197         ok = (do_xmlrpc ("md5sum", &retval,
198                          SOUP_TYPE_BYTE_ARRAY, data,
199                          G_TYPE_INVALID) &&
200               check_xmlrpc (&retval, SOUP_TYPE_BYTE_ARRAY, &result));
201         g_byte_array_free (data, TRUE);
202         if (!ok)
203                 return FALSE;
204
205         if (result->len != digest_len) {
206                 debug_printf (1, "result has WRONG length (%d)\n", result->len);
207                 g_byte_array_free (result, TRUE);
208                 return FALSE;
209         }
210
211         ok = (memcmp (digest, result->data, digest_len) == 0);
212         debug_printf (1, "%s\n", ok ? "OK!" : "WRONG!");
213         g_byte_array_free (result, TRUE);
214         return ok;
215 }
216
217 static gboolean
218 test_dateChange (void)
219 {
220         GHashTable *structval;
221         SoupDate *date, *result;
222         char *timestamp;
223         GValue retval;
224         gboolean ok;
225
226         debug_printf (1, "dateChange (date, struct of ints -> time): ");
227
228         date = soup_date_new (1970 + (g_random_int_range (0, 50)),
229                               1 + g_random_int_range (0, 12),
230                               1 + g_random_int_range (0, 28),
231                               g_random_int_range (0, 24),
232                               g_random_int_range (0, 60),
233                               g_random_int_range (0, 60));
234         if (debug_level >= 2) {
235                 timestamp = soup_date_to_string (date, SOUP_DATE_ISO8601_XMLRPC);
236                 debug_printf (2, "date: %s, {", timestamp);
237                 g_free (timestamp);
238         }
239
240         structval = soup_value_hash_new ();
241
242 #define MAYBE (g_random_int_range (0, 3) != 0)
243
244         if (MAYBE) {
245                 date->year = 1970 + (g_random_int_range (0, 50));
246                 debug_printf (2, "tm_year: %d, ", date->year - 1900);
247                 soup_value_hash_insert (structval, "tm_year",
248                                         G_TYPE_INT, date->year - 1900);
249         }
250         if (MAYBE) {
251                 date->month = 1 + g_random_int_range (0, 12);
252                 debug_printf (2, "tm_mon: %d, ", date->month - 1);
253                 soup_value_hash_insert (structval, "tm_mon",
254                                         G_TYPE_INT, date->month - 1);
255         }
256         if (MAYBE) {
257                 date->day = 1 + g_random_int_range (0, 28);
258                 debug_printf (2, "tm_mday: %d, ", date->day);
259                 soup_value_hash_insert (structval, "tm_mday",
260                                         G_TYPE_INT, date->day);
261         }
262         if (MAYBE) {
263                 date->hour = g_random_int_range (0, 24);
264                 debug_printf (2, "tm_hour: %d, ", date->hour);
265                 soup_value_hash_insert (structval, "tm_hour",
266                                         G_TYPE_INT, date->hour);
267         }
268         if (MAYBE) {
269                 date->minute = g_random_int_range (0, 60);
270                 debug_printf (2, "tm_min: %d, ", date->minute);
271                 soup_value_hash_insert (structval, "tm_min",
272                                         G_TYPE_INT, date->minute);
273         }
274         if (MAYBE) {
275                 date->second = g_random_int_range (0, 60);
276                 debug_printf (2, "tm_sec: %d, ", date->second);
277                 soup_value_hash_insert (structval, "tm_sec",
278                                         G_TYPE_INT, date->second);
279         }
280
281         debug_printf (2, "} -> ");
282
283         ok = (do_xmlrpc ("dateChange", &retval,
284                          SOUP_TYPE_DATE, date,
285                          G_TYPE_HASH_TABLE, structval,
286                          G_TYPE_INVALID) &&
287               check_xmlrpc (&retval, SOUP_TYPE_DATE, &result));
288         g_hash_table_destroy (structval);
289         if (!ok) {
290                 soup_date_free (date);
291                 return FALSE;
292         }
293
294         if (debug_level >= 2) {
295                 timestamp = soup_date_to_string (result, SOUP_DATE_ISO8601_XMLRPC);
296                 debug_printf (2, "%s: ", timestamp);
297                 g_free (timestamp);
298         }
299
300         ok = ((date->year   == result->year) &&
301               (date->month  == result->month) &&
302               (date->day    == result->day) &&
303               (date->hour   == result->hour) &&
304               (date->minute == result->minute) &&
305               (date->second == result->second));
306         soup_date_free (date);
307         soup_date_free (result);
308
309         debug_printf (1, "%s\n", ok ? "OK!" : "WRONG!");
310         return ok;
311 }
312
313 static const char *const echo_strings[] = {
314         "This is a test",
315         "& so is this",
316         "and so is <this>",
317         "&amp; so is &lt;this&gt;"
318 };
319 #define N_ECHO_STRINGS G_N_ELEMENTS (echo_strings)
320
321 static const char *const echo_strings_broken[] = {
322         "This is a test",
323         " so is this",
324         "and so is this",
325         "amp; so is lt;thisgt;"
326 };
327
328 static gboolean
329 test_echo (void)
330 {
331         GValueArray *originals, *echoes;
332         GValue retval;
333         int i;
334         gboolean php_bug = FALSE;
335
336         debug_printf (1, "echo (array of string -> array of string): ");
337
338         originals = g_value_array_new (N_ECHO_STRINGS);
339         for (i = 0; i < N_ECHO_STRINGS; i++) {
340                 soup_value_array_append (originals, G_TYPE_STRING, echo_strings[i]);
341                 debug_printf (2, "%s\"%s\"", i == 0 ? "[" : ", ", echo_strings[i]);
342         }
343         debug_printf (2, "] -> ");
344
345         if (!(do_xmlrpc ("echo", &retval,
346                          G_TYPE_VALUE_ARRAY, originals,
347                          G_TYPE_INVALID) &&
348               check_xmlrpc (&retval, G_TYPE_VALUE_ARRAY, &echoes))) {
349                 g_value_array_free (originals);
350                 return FALSE;
351         }
352         g_value_array_free (originals);
353
354         if (debug_level >= 2) {
355                 for (i = 0; i < echoes->n_values; i++) {
356                         debug_printf (2, "%s\"%s\"", i == 0 ? "[" : ", ",
357                                       g_value_get_string (&echoes->values[i]));
358                 }
359                 debug_printf (2, "] -> ");
360         }
361
362         if (echoes->n_values != N_ECHO_STRINGS) {
363                 debug_printf (1, " WRONG! Wrong number of return strings");
364                 g_value_array_free (echoes);
365                 return FALSE;
366         }
367
368         for (i = 0; i < echoes->n_values; i++) {
369                 if (strcmp (echo_strings[i], g_value_get_string (&echoes->values[i])) != 0) {
370                         if (!server_test && strcmp (echo_strings_broken[i], g_value_get_string (&echoes->values[i])) == 0)
371                                 php_bug = TRUE;
372                         else {
373                                 debug_printf (1, " WRONG! Mismatch at %d\n", i + 1);
374                                 g_value_array_free (echoes);
375                                 return FALSE;
376                         }
377                 }
378         }
379
380         if (php_bug)
381                 debug_printf (1, "WRONG, but it's php's fault\n");
382         else
383                 debug_printf (1, "OK!\n");
384         g_value_array_free (echoes);
385         return TRUE;
386 }
387
388 static gboolean
389 do_bad_xmlrpc (const char *body)
390 {
391         SoupMessage *msg;
392         GError *err = NULL;
393         GValue retval;
394
395         msg = soup_message_new ("POST", uri);
396         soup_message_set_request (msg, "text/xml", SOUP_MEMORY_COPY,
397                                   body, strlen (body));
398         soup_session_send_message (session, msg);
399
400         if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
401                 debug_printf (1, "ERROR: %d %s\n", msg->status_code,
402                               msg->reason_phrase);
403                 g_object_unref (msg);
404                 return FALSE;
405         }
406
407         if (!soup_xmlrpc_parse_method_response (msg->response_body->data,
408                                                 msg->response_body->length,
409                                                 &retval, &err)) {
410                 if (err) {
411                         debug_printf (1, "FAULT: %d %s (OK!)\n",
412                                       err->code, err->message);
413                         g_error_free (err);
414                         g_object_unref (msg);
415                         return TRUE;
416                 } else
417                         debug_printf (1, "ERROR: could not parse response\n");
418         } else
419                 debug_printf (1, "Unexpectedly got successful response!\n");
420
421         g_object_unref (msg);
422         return FALSE;
423 }
424
425 static gboolean
426 test_fault_malformed (void)
427 {
428         debug_printf (1, "malformed request: ");
429
430         return do_bad_xmlrpc ("<methodCall/>");
431 }
432
433 static gboolean
434 test_fault_method (void)
435 {
436         debug_printf (1, "request to non-existent method: ");
437
438         return do_bad_xmlrpc ("<methodCall><methodName>no_such_method</methodName><params><param><value><int>1</int></value></param></params></methodCall>");
439 }
440
441 static gboolean
442 test_fault_args (void)
443 {
444         debug_printf (1, "request with invalid args: ");
445
446         return do_bad_xmlrpc ("<methodCall><methodName>sum</methodName><params><param><value><int>1</int></value></param></params></methodCall>");
447 }
448
449 static GOptionEntry xmlrpc_entries[] = {
450         { "uri", 'u', 0, G_OPTION_ARG_STRING, &uri,
451           "Alternate URI for server", NULL },
452         { "server-test", 's', 0, G_OPTION_ARG_NONE, &server_test,
453           "If this is being run from xmlrpc-server-test", NULL },
454         { NULL }
455 };
456
457 int
458 main (int argc, char **argv)
459 {
460         test_init (argc, argv, xmlrpc_entries);
461
462         if (!uri) {
463                 apache_init ();
464                 uri = default_uri;
465         }
466
467         session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL);
468
469         if (!test_sum ())
470                 errors++;
471         if (!test_countBools ())
472                 errors++;
473         if (!test_md5sum ())
474                 errors++;
475         if (!test_dateChange ())
476                 errors++;
477         if (!test_echo ())
478                 errors++;
479         if (!test_fault_malformed ())
480                 errors++;
481         if (!test_fault_method ())
482                 errors++;
483         if (!test_fault_args ())
484                 errors++;
485
486         soup_test_session_abort_unref (session);
487
488         test_cleanup ();
489         return errors != 0;
490 }