add missing slash in %configure
[platform/upstream/libsoup.git] / tests / timeout-test.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
3 #include "test-utils.h"
4
5 static gboolean slow_https;
6
7 static void
8 message_finished (SoupMessage *msg, gpointer user_data)
9 {
10         gboolean *finished = user_data;
11
12         *finished = TRUE;
13 }
14
15 static void
16 request_started_cb (SoupSession *session, SoupMessage *msg,
17                     SoupSocket *socket, gpointer user_data)
18 {
19         SoupSocket **ret = user_data;
20
21         *ret = socket;
22 }
23
24 static void
25 do_message_to_session (SoupSession *session, const char *uri,
26                        const char *comment, guint expected_status)
27 {
28         SoupMessage *msg;
29         gboolean finished = FALSE;
30
31         if (comment)
32                 debug_printf (1, "    msg %s\n", comment);
33         msg = soup_message_new ("GET", uri);
34
35         g_signal_connect (msg, "finished",
36                           G_CALLBACK (message_finished), &finished);
37         soup_session_send_message (session, msg);
38
39         soup_test_assert_message_status (msg, expected_status);
40         if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code))
41                 g_assert_true (soup_message_is_keepalive (msg));
42         g_assert_true (finished);
43
44         g_signal_handlers_disconnect_by_func (msg,
45                                               G_CALLBACK (message_finished),
46                                               &finished);
47         g_object_unref (msg);
48 }
49
50 static void
51 do_msg_tests_for_session (SoupSession *timeout_session,
52                           SoupSession *idle_session,
53                           SoupSession *plain_session,
54                           const char *fast_uri,
55                           const char *slow_uri)
56 {
57         SoupSocket *ret, *idle_first, *idle_second;
58         SoupSocket *plain_first, *plain_second;
59
60         if (idle_session) {
61                 g_signal_connect (idle_session, "request-started",
62                                   G_CALLBACK (request_started_cb), &ret);
63                 do_message_to_session (idle_session, fast_uri, "fast to idle", SOUP_STATUS_OK);
64                 idle_first = g_object_ref (ret);
65         }
66
67         if (plain_session) {
68                 g_signal_connect (plain_session, "request-started",
69                                   G_CALLBACK (request_started_cb), &ret);
70                 do_message_to_session (plain_session, fast_uri, "fast to plain", SOUP_STATUS_OK);
71                 plain_first = g_object_ref (ret);
72         }
73
74         do_message_to_session (timeout_session, fast_uri, "fast to timeout", SOUP_STATUS_OK);
75         do_message_to_session (timeout_session, slow_uri, "slow to timeout", SOUP_STATUS_IO_ERROR);
76
77         if (idle_session) {
78                 do_message_to_session (idle_session, fast_uri, "fast to idle", SOUP_STATUS_OK);
79                 idle_second = ret;
80                 g_signal_handlers_disconnect_by_func (idle_session,
81                                                       (gpointer)request_started_cb,
82                                                       &ret);
83
84                 soup_test_assert (idle_first != idle_second,
85                                   "idle_session did not close first connection");
86                 g_object_unref (idle_first);
87         }
88
89         if (plain_session) {
90                 do_message_to_session (plain_session, fast_uri, "fast to plain", SOUP_STATUS_OK);
91                 plain_second = ret;
92                 g_signal_handlers_disconnect_by_func (plain_session,
93                                                       (gpointer)request_started_cb,
94                                                       &ret);
95
96                 soup_test_assert (plain_first == plain_second,
97                                   "plain_session closed connection");
98                 g_object_unref (plain_first);
99         }
100 }
101
102 static void
103 do_request_to_session (SoupSession *session, const char *uri,
104                        const char *comment, gboolean expect_timeout)
105 {
106         SoupRequest *req;
107         SoupMessage *msg;
108         GInputStream *stream;
109         GError *error = NULL;
110         gboolean finished = FALSE;
111
112         debug_printf (1, "    req %s\n", comment);
113         req = soup_session_request (session, uri, NULL);
114         msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (req));
115
116         g_signal_connect (msg, "finished",
117                           G_CALLBACK (message_finished), &finished);
118         stream = soup_test_request_send (req, NULL, 0, &error);
119
120         if (expect_timeout)
121                 g_assert_error (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT);
122         else
123                 g_assert_no_error (error);
124         g_clear_error (&error);
125
126         if (stream) {
127                 soup_test_request_read_all (req, stream, NULL, &error);
128                 g_assert_no_error (error);
129         }
130
131         if (stream) {
132                 soup_test_request_close_stream (req, stream, NULL, &error);
133                 g_assert_no_error (error);
134                 g_object_unref (stream);
135         }
136
137         if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code))
138                 g_assert_true (soup_message_is_keepalive (msg));
139         g_assert_true (finished);
140
141         g_signal_handlers_disconnect_by_func (msg,
142                                               G_CALLBACK (message_finished),
143                                               &finished);
144         g_object_unref (msg);
145         g_object_unref (req);
146 }
147
148 static void
149 do_req_tests_for_session (SoupSession *timeout_session,
150                           SoupSession *idle_session,
151                           SoupSession *plain_session,
152                           const char *fast_uri,
153                           const char *slow_uri)
154 {
155         SoupSocket *ret, *idle_first, *idle_second;
156         SoupSocket *plain_first, *plain_second;
157
158         if (idle_session) {
159                 g_signal_connect (idle_session, "request-started",
160                                   G_CALLBACK (request_started_cb), &ret);
161                 do_request_to_session (idle_session, fast_uri, "fast to idle", FALSE);
162                 idle_first = g_object_ref (ret);
163         }
164
165         if (plain_session) {
166                 g_signal_connect (plain_session, "request-started",
167                                   G_CALLBACK (request_started_cb), &ret);
168                 do_request_to_session (plain_session, fast_uri, "fast to plain", FALSE);
169                 plain_first = g_object_ref (ret);
170         }
171
172         do_request_to_session (timeout_session, fast_uri, "fast to timeout", FALSE);
173         do_request_to_session (timeout_session, slow_uri, "slow to timeout", TRUE);
174
175         if (idle_session) {
176                 do_request_to_session (idle_session, fast_uri, "fast to idle", FALSE);
177                 idle_second = ret;
178                 g_signal_handlers_disconnect_by_func (idle_session,
179                                                       (gpointer)request_started_cb,
180                                                       &ret);
181
182                 soup_test_assert (idle_first != idle_second,
183                                   "idle_session did not close first connection");
184                 g_object_unref (idle_first);
185         }
186
187         if (plain_session) {
188                 do_request_to_session (plain_session, fast_uri, "fast to plain", FALSE);
189                 plain_second = ret;
190                 g_signal_handlers_disconnect_by_func (plain_session,
191                                                       (gpointer)request_started_cb,
192                                                       &ret);
193
194                 soup_test_assert (plain_first == plain_second,
195                                   "plain_session closed connection");
196                 g_object_unref (plain_first);
197         }
198 }
199
200 static void
201 do_async_timeout_tests (gconstpointer data)
202 {
203         SoupSession *timeout_session, *idle_session, *plain_session;
204         const char *fast_uri = data;
205         const char *slow_uri = g_build_path ("/", fast_uri, "slow", NULL);
206         gboolean extra_slow;
207
208         if (g_str_has_prefix (fast_uri, "https")) {
209                 SOUP_TEST_SKIP_IF_NO_TLS;
210
211                 extra_slow = slow_https;
212         } else
213                 extra_slow = FALSE;
214
215         timeout_session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
216                                                  SOUP_SESSION_TIMEOUT, extra_slow ? 3 : 1,
217                                                  SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
218                                                  NULL);
219         idle_session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
220                                               SOUP_SESSION_IDLE_TIMEOUT, extra_slow ? 2 : 1,
221                                               SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
222                                               NULL);
223         /* The "plain" session also has an idle timeout, but it's longer
224          * than the test takes, so for our purposes it should behave like
225          * it has no timeout.
226          */
227         plain_session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
228                                                SOUP_SESSION_IDLE_TIMEOUT, 20,
229                                                SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
230                                                NULL);
231
232         do_msg_tests_for_session (timeout_session, idle_session, plain_session,
233                                   fast_uri, slow_uri);
234         do_req_tests_for_session (timeout_session, idle_session, plain_session,
235                                   fast_uri, slow_uri);
236         soup_test_session_abort_unref (timeout_session);
237         soup_test_session_abort_unref (idle_session);
238         soup_test_session_abort_unref (plain_session);
239 }
240
241 static void
242 do_sync_timeout_tests (gconstpointer data)
243 {
244         SoupSession *timeout_session, *plain_session;
245         const char *fast_uri = data;
246         const char *slow_uri = g_build_path ("/", fast_uri, "slow", NULL);
247         gboolean extra_slow;
248
249         if (g_str_has_prefix (fast_uri, "https")) {
250                 SOUP_TEST_SKIP_IF_NO_TLS;
251
252                 extra_slow = slow_https;
253         } else
254                 extra_slow = FALSE;
255
256         timeout_session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC,
257                                                  SOUP_SESSION_TIMEOUT, extra_slow ? 3 : 1,
258                                                  NULL);
259         /* SOUP_SESSION_TIMEOUT doesn't work with sync sessions */
260         plain_session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC,
261                                                NULL);
262         do_msg_tests_for_session (timeout_session, NULL, plain_session, fast_uri, slow_uri);
263         do_req_tests_for_session (timeout_session, NULL, plain_session, fast_uri, slow_uri);
264         soup_test_session_abort_unref (timeout_session);
265         soup_test_session_abort_unref (plain_session);
266 }
267
268 static gboolean
269 timeout_finish_message (gpointer msg)
270 {
271         SoupServer *server = g_object_get_data (G_OBJECT (msg), "server");
272
273         soup_server_unpause_message (server, msg);
274         return FALSE;
275 }
276
277 static void
278 server_handler (SoupServer        *server,
279                 SoupMessage       *msg, 
280                 const char        *path,
281                 GHashTable        *query,
282                 SoupClientContext *client,
283                 gpointer           user_data)
284 {
285         soup_message_set_status (msg, SOUP_STATUS_OK);
286         soup_message_set_response (msg, "text/plain",
287                                    SOUP_MEMORY_STATIC,
288                                    "ok\r\n", 4);
289
290         if (!strcmp (path, "/slow")) {
291                 soup_server_pause_message (server, msg);
292                 g_object_set_data (G_OBJECT (msg), "server", server);
293                 soup_add_timeout (soup_server_get_async_context (server),
294                                   4000, timeout_finish_message, msg);
295         }
296 }
297
298 int
299 main (int argc, char **argv)
300 {
301         SoupServer *server, *https_server = NULL;
302         char *uri, *https_uri = NULL;
303         int ret;
304
305         test_init (argc, argv, NULL);
306
307         server = soup_test_server_new (TRUE);
308         soup_server_add_handler (server, NULL, server_handler, NULL, NULL);
309         uri = g_strdup_printf ("http://127.0.0.1:%u/",
310                                soup_server_get_port (server));
311
312         if (tls_available) {
313                 SoupSession *test_session;
314                 gint64 start, end;
315
316                 https_server = soup_test_server_new_ssl (TRUE);
317                 soup_server_add_handler (https_server, NULL, server_handler, NULL, NULL);
318                 https_uri = g_strdup_printf ("https://127.0.0.1:%u/",
319                                              soup_server_get_port (https_server));
320
321                 /* The 1-second timeouts are too fast for some machines... */
322                 test_session = soup_test_session_new (SOUP_TYPE_SESSION, NULL);
323                 start = g_get_monotonic_time ();
324                 do_message_to_session (test_session, uri, NULL, SOUP_STATUS_OK);
325                 end = g_get_monotonic_time ();
326                 soup_test_session_abort_unref (test_session);
327                 debug_printf (2, "  (https request took %0.3fs)\n", (end - start) / 1000000.0);
328                 if (end - start > 750000) {
329                         debug_printf (1, "  (using extra-slow mode)\n\n");
330                         slow_https = TRUE;
331                 } else {
332                         debug_printf (2, "\n");
333                         slow_https = FALSE;
334                 }
335         } else
336                 https_uri = g_strdup ("https://fail.");
337
338         g_test_add_data_func ("/timeout/http/async", uri, do_async_timeout_tests);
339         g_test_add_data_func ("/timeout/http/sync", uri, do_sync_timeout_tests);
340         g_test_add_data_func ("/timeout/https/async", https_uri, do_async_timeout_tests);
341         g_test_add_data_func ("/timeout/https/sync", https_uri, do_sync_timeout_tests);
342
343         ret = g_test_run ();
344
345         g_free (uri);
346         g_free (https_uri);
347         soup_test_server_quit_unref (server);
348         if (https_server)
349                 soup_test_server_quit_unref (https_server);
350
351         test_cleanup ();
352         return ret;
353 }