Fix the retry-on-broken-connection codepath for SoupRequest
[platform/upstream/libsoup.git] / tests / test-utils.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
3 #include "test-utils.h"
4
5 #include <glib/gprintf.h>
6
7 #include <locale.h>
8 #include <signal.h>
9
10 #ifdef HAVE_APACHE
11 static gboolean apache_running;
12 #endif
13
14 static SoupLogger *logger;
15
16 int debug_level, errors;
17 gboolean expect_warning, tls_available;
18 static int http_debug_level;
19
20 static gboolean
21 increment_debug_level (const char *option_name, const char *value,
22                        gpointer data, GError **error)
23 {
24         debug_level++;
25         return TRUE;
26 }
27
28 static gboolean
29 increment_http_debug_level (const char *option_name, const char *value,
30                             gpointer data, GError **error)
31 {
32         http_debug_level++;
33         return TRUE;
34 }
35
36 static GOptionEntry debug_entry[] = {
37         { "debug", 'd', G_OPTION_FLAG_NO_ARG,
38           G_OPTION_ARG_CALLBACK, increment_debug_level,
39           "Enable (or increase) test-specific debugging", NULL },
40         { "http-debug", 'h', G_OPTION_FLAG_NO_ARG,
41           G_OPTION_ARG_CALLBACK, increment_http_debug_level,
42           "Enable (or increase) HTTP-level debugging", NULL },
43         { NULL }
44 };
45
46 static void
47 quit (int sig)
48 {
49 #ifdef HAVE_APACHE
50         if (apache_running)
51                 apache_cleanup ();
52 #endif
53
54         exit (1);
55 }
56
57 static void
58 test_log_handler (const char *log_domain, GLogLevelFlags log_level,
59                   const char *message, gpointer user_data)
60 {
61         if (log_level & (G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL)) {
62                 if (expect_warning) {
63                         expect_warning = FALSE;
64                         debug_printf (2, "Got expected warning: %s\n", message);
65                         return;
66                 } else
67                         errors++;
68         }
69         g_log_default_handler (log_domain, log_level, message, user_data);
70 }
71
72 void
73 test_init (int argc, char **argv, GOptionEntry *entries)
74 {
75         GOptionContext *opts;
76         char *name;
77         GError *error = NULL;
78         GTlsBackend *tls_backend;
79
80         setlocale (LC_ALL, "");
81         g_type_init ();
82
83         name = strrchr (argv[0], '/');
84         if (!name++)
85                 name = argv[0];
86         if (!strncmp (name, "lt-", 3))
87                 name += 3;
88         g_set_prgname (name);
89
90         opts = g_option_context_new (NULL);
91         g_option_context_add_main_entries (opts, debug_entry, NULL);
92         if (entries)
93                 g_option_context_add_main_entries (opts, entries, NULL);
94
95         if (!g_option_context_parse (opts, &argc, &argv, &error)) {
96                 g_printerr ("Could not parse arguments: %s\n",
97                             error->message);
98                 g_printerr ("%s",
99                             g_option_context_get_help (opts, TRUE, NULL));
100                 exit (1);
101         }
102         g_option_context_free (opts);
103
104         /* Exit cleanly on ^C in case we're valgrinding. */
105         signal (SIGINT, quit);
106
107         g_log_set_default_handler (test_log_handler, NULL);
108
109         tls_backend = g_tls_backend_get_default ();
110         tls_available = g_tls_backend_supports_tls (tls_backend);
111 }
112
113 void
114 test_cleanup (void)
115 {
116 #ifdef HAVE_APACHE
117         if (apache_running)
118                 apache_cleanup ();
119 #endif
120
121         if (logger)
122                 g_object_unref (logger);
123
124         g_main_context_unref (g_main_context_default ());
125
126         debug_printf (1, "\n");
127         if (errors) {
128                 g_print ("%s: %d error(s).%s\n",
129                          g_get_prgname (), errors,
130                          debug_level == 0 ? " Run with '-d' for details" : "");
131         } else
132                 g_print ("%s: OK\n", g_get_prgname ());
133 }
134
135 void
136 debug_printf (int level, const char *format, ...)
137 {
138         va_list args;
139
140         if (debug_level < level)
141                 return;
142
143         va_start (args, format);
144         g_vprintf (format, args);
145         va_end (args);
146 }
147
148 #ifdef HAVE_APACHE
149
150 static gboolean
151 apache_cmd (const char *cmd)
152 {
153         const char *argv[8];
154         char *cwd, *conf;
155         int status;
156         gboolean ok;
157
158         cwd = g_get_current_dir ();
159         conf = g_build_filename (cwd, "httpd.conf", NULL);
160
161         argv[0] = APACHE_HTTPD;
162         argv[1] = "-d";
163         argv[2] = cwd;
164         argv[3] = "-f";
165         argv[4] = conf;
166         argv[5] = "-k";
167         argv[6] = cmd;
168         argv[7] = NULL;
169
170         ok = g_spawn_sync (cwd, (char **)argv, NULL, 0, NULL, NULL,
171                            NULL, NULL, &status, NULL);
172         if (ok)
173                 ok = (status == 0);
174
175         g_free (cwd);
176         g_free (conf);
177
178         return ok;
179 }
180
181 void
182 apache_init (void)
183 {
184         if (!apache_cmd ("start")) {
185                 g_printerr ("Could not start apache\n");
186                 exit (1);
187         }
188         apache_running = TRUE;
189 }
190
191 void
192 apache_cleanup (void)
193 {
194         pid_t pid;
195         char *contents;
196
197         if (g_file_get_contents ("httpd.pid", &contents, NULL, NULL)) {
198                 pid = strtoul (contents, NULL, 10);
199                 g_free (contents);
200         } else
201                 pid = 0;
202
203         if (!apache_cmd ("graceful-stop"))
204                 return;
205         apache_running = FALSE;
206
207         if (pid) {
208                 while (kill (pid, 0) == 0)
209                         g_usleep (100);
210         }
211 }
212
213 #endif /* HAVE_APACHE */
214
215 SoupSession *
216 soup_test_session_new (GType type, ...)
217 {
218         va_list args;
219         const char *propname;
220         SoupSession *session;
221
222         va_start (args, type);
223         propname = va_arg (args, const char *);
224         session = (SoupSession *)g_object_new_valist (type, propname, args);
225         va_end (args);
226
227         g_object_set (G_OBJECT (session),
228                       SOUP_SESSION_SSL_CA_FILE, SRCDIR "/test-cert.pem",
229                       NULL);
230
231         if (http_debug_level && !logger) {
232                 SoupLoggerLogLevel level = MIN ((SoupLoggerLogLevel)http_debug_level, SOUP_LOGGER_LOG_BODY);
233
234                 logger = soup_logger_new (level, -1);
235         }
236
237         if (logger)
238                 soup_session_add_feature (session, SOUP_SESSION_FEATURE (logger));
239
240         return session;
241 }
242
243 void
244 soup_test_session_abort_unref (SoupSession *session)
245 {
246         g_object_add_weak_pointer (G_OBJECT (session), (gpointer *)&session);
247
248         soup_session_abort (session);
249         g_object_unref (session);
250
251         if (session) {
252                 errors++;
253                 debug_printf (1, "leaked SoupSession!\n");
254                 g_object_remove_weak_pointer (G_OBJECT (session), (gpointer *)&session);
255         }
256 }
257
258 static gpointer run_server_thread (gpointer user_data);
259
260 static SoupServer *
261 test_server_new (gboolean in_own_thread, gboolean ssl)
262 {
263         SoupServer *server;
264         GMainContext *async_context;
265         const char *ssl_cert_file, *ssl_key_file;
266         SoupAddress *addr;
267
268         async_context = in_own_thread ? g_main_context_new () : NULL;
269
270         if (ssl) {
271                 ssl_cert_file = SRCDIR "/test-cert.pem";
272                 ssl_key_file = SRCDIR "/test-key.pem";
273         } else
274                 ssl_cert_file = ssl_key_file = NULL;
275
276         addr = soup_address_new ("127.0.0.1", SOUP_ADDRESS_ANY_PORT);
277         soup_address_resolve_sync (addr, NULL);
278
279         server = soup_server_new (SOUP_SERVER_INTERFACE, addr,
280                                   SOUP_SERVER_ASYNC_CONTEXT, async_context,
281                                   SOUP_SERVER_SSL_CERT_FILE, ssl_cert_file,
282                                   SOUP_SERVER_SSL_KEY_FILE, ssl_key_file,
283                                   NULL);
284         g_object_unref (addr);
285         if (async_context)
286                 g_main_context_unref (async_context);
287
288         if (!server) {
289                 g_printerr ("Unable to create server\n");
290                 exit (1);
291         }
292
293         if (in_own_thread) {
294                 GThread *thread;
295
296                 thread = g_thread_new ("server_thread", run_server_thread, server);
297                 g_object_set_data (G_OBJECT (server), "thread", thread);
298         } else
299                 soup_server_run_async (server);
300
301         return server;
302 }
303
304 SoupServer *
305 soup_test_server_new (gboolean in_own_thread)
306 {
307         return test_server_new (in_own_thread, FALSE);
308 }
309
310 SoupServer *
311 soup_test_server_new_ssl (gboolean in_own_thread)
312 {
313         return test_server_new (in_own_thread, TRUE);
314 }
315
316 static gpointer
317 run_server_thread (gpointer user_data)
318 {
319         SoupServer *server = user_data;
320
321         soup_server_run (server);
322         return NULL;
323 }
324
325 static gboolean
326 idle_quit_server (gpointer server)
327 {
328         soup_server_quit (server);
329         return FALSE;
330 }
331
332 void
333 soup_test_server_quit_unref (SoupServer *server)
334 {
335         GThread *thread;
336
337         g_object_add_weak_pointer (G_OBJECT (server),
338                                    (gpointer *)&server);
339
340         thread = g_object_get_data (G_OBJECT (server), "thread");
341         if (thread) {
342                 soup_add_completion (soup_server_get_async_context (server),
343                                      idle_quit_server, server);
344                 g_thread_join (thread);
345         } else
346                 soup_server_quit (server);
347         g_object_unref (server);
348
349         if (server) {
350                 errors++;
351                 debug_printf (1, "leaked SoupServer!\n");
352                 g_object_remove_weak_pointer (G_OBJECT (server),
353                                               (gpointer *)&server);
354         }
355 }
356
357 typedef struct {
358   GMainLoop *loop;
359   GAsyncResult *result;
360 } AsyncAsSyncData;
361
362 static void
363 async_as_sync_callback (GObject      *object,
364                         GAsyncResult *result,
365                         gpointer      user_data)
366 {
367   AsyncAsSyncData *data = user_data;
368
369   data->result = g_object_ref (result);
370   g_main_loop_quit (data->loop);
371 }
372
373 GInputStream *
374 soup_test_request_send_async_as_sync (SoupRequest   *req,
375                                       GCancellable  *cancellable,
376                                       GError       **error)
377 {
378   AsyncAsSyncData data;
379   GInputStream *stream;
380
381   data.loop = g_main_loop_new (g_main_context_get_thread_default (), FALSE);
382
383   soup_request_send_async (req, cancellable, async_as_sync_callback, &data);
384   g_main_loop_run (data.loop);
385
386   stream = soup_request_send_finish (req, data.result, error);
387
388   g_main_loop_unref (data.loop);
389   g_object_unref (data.result);
390
391   return stream;
392 }
393
394 gboolean
395 soup_test_stream_close_async_as_sync (GInputStream  *stream,
396                                       GCancellable  *cancellable,
397                                       GError       **error)
398 {
399   AsyncAsSyncData data;
400   gboolean ok;
401
402   data.loop = g_main_loop_new (g_main_context_get_thread_default (), FALSE);
403
404   g_input_stream_close_async (stream, G_PRIORITY_DEFAULT, cancellable,
405                               async_as_sync_callback, &data);
406   g_main_loop_run (data.loop);
407
408   ok = g_input_stream_close_finish (stream, data.result, error);
409
410   g_main_loop_unref (data.loop);
411   g_object_unref (data.result);
412
413   return ok;
414 }