• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
message_finished(SoupMessage * msg,gpointer user_data)8 message_finished (SoupMessage *msg, gpointer user_data)
9 {
10 	gboolean *finished = user_data;
11 
12 	*finished = TRUE;
13 }
14 
15 static void
request_started_cb(SoupSession * session,SoupMessage * msg,SoupSocket * socket,gpointer user_data)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
do_message_to_session(SoupSession * session,SoupURI * uri,const char * comment,guint expected_status)25 do_message_to_session (SoupSession *session, SoupURI *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_from_uri ("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
do_msg_tests_for_session(SoupSession * timeout_session,SoupSession * idle_session,SoupSession * plain_session,SoupURI * fast_uri,SoupURI * slow_uri)51 do_msg_tests_for_session (SoupSession *timeout_session,
52 			  SoupSession *idle_session,
53 			  SoupSession *plain_session,
54 			  SoupURI *fast_uri,
55 			  SoupURI *slow_uri)
56 {
57 	SoupSocket *ret, *idle_first = NULL, *idle_second;
58 	SoupSocket *plain_first = NULL, *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
do_request_to_session(SoupSession * session,SoupURI * uri,const char * comment,gboolean expect_timeout)103 do_request_to_session (SoupSession *session, SoupURI *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_uri (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
do_req_tests_for_session(SoupSession * timeout_session,SoupSession * idle_session,SoupSession * plain_session,SoupURI * fast_uri,SoupURI * slow_uri)149 do_req_tests_for_session (SoupSession *timeout_session,
150 			  SoupSession *idle_session,
151 			  SoupSession *plain_session,
152 			  SoupURI *fast_uri,
153 			  SoupURI *slow_uri)
154 {
155 	SoupSocket *ret, *idle_first = NULL, *idle_second;
156 	SoupSocket *plain_first = NULL, *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
do_async_timeout_tests(gconstpointer data)201 do_async_timeout_tests (gconstpointer data)
202 {
203 	SoupSession *timeout_session, *idle_session, *plain_session;
204 	SoupURI *fast_uri = (SoupURI *)data;
205 	SoupURI *slow_uri = soup_uri_new_with_base (fast_uri, "/slow");
206 	gboolean extra_slow;
207 
208 	if (fast_uri->scheme == SOUP_URI_SCHEME_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 	soup_uri_free (slow_uri);
241 }
242 
243 static void
do_sync_timeout_tests(gconstpointer data)244 do_sync_timeout_tests (gconstpointer data)
245 {
246 	SoupSession *timeout_session, *plain_session;
247 	SoupURI *fast_uri = (SoupURI *)data;
248 	SoupURI *slow_uri = soup_uri_new_with_base (fast_uri, "/slow");
249 	gboolean extra_slow;
250 
251 	if (fast_uri->scheme == SOUP_URI_SCHEME_HTTPS) {
252 		SOUP_TEST_SKIP_IF_NO_TLS;
253 
254 		extra_slow = slow_https;
255 	} else
256 		extra_slow = FALSE;
257 
258 	timeout_session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC,
259 						 SOUP_SESSION_TIMEOUT, extra_slow ? 3 : 1,
260 						 NULL);
261 	/* SOUP_SESSION_TIMEOUT doesn't work with sync sessions */
262 	plain_session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC,
263 					       NULL);
264 	do_msg_tests_for_session (timeout_session, NULL, plain_session, fast_uri, slow_uri);
265 	do_req_tests_for_session (timeout_session, NULL, plain_session, fast_uri, slow_uri);
266 	soup_test_session_abort_unref (timeout_session);
267 	soup_test_session_abort_unref (plain_session);
268 
269 	soup_uri_free (slow_uri);
270 }
271 
272 static gboolean
timeout_finish_message(gpointer msg)273 timeout_finish_message (gpointer msg)
274 {
275 	SoupServer *server = g_object_get_data (G_OBJECT (msg), "server");
276 
277 	soup_server_unpause_message (server, msg);
278 	return FALSE;
279 }
280 
281 static void
server_handler(SoupServer * server,SoupMessage * msg,const char * path,GHashTable * query,SoupClientContext * client,gpointer user_data)282 server_handler (SoupServer        *server,
283 		SoupMessage       *msg,
284 		const char        *path,
285 		GHashTable        *query,
286 		SoupClientContext *client,
287 		gpointer           user_data)
288 {
289 	soup_message_set_status (msg, SOUP_STATUS_OK);
290 	soup_message_set_response (msg, "text/plain",
291 				   SOUP_MEMORY_STATIC,
292 				   "ok\r\n", 4);
293 
294 	if (!strcmp (path, "/slow")) {
295 		soup_server_pause_message (server, msg);
296 		g_object_set_data (G_OBJECT (msg), "server", server);
297 		soup_add_timeout (g_main_context_get_thread_default (),
298 				  4000, timeout_finish_message, msg);
299 	}
300 }
301 
302 int
main(int argc,char ** argv)303 main (int argc, char **argv)
304 {
305 	SoupServer *server, *https_server = NULL;
306 	SoupURI *uri, *https_uri = NULL;
307 	int ret;
308 
309 	test_init (argc, argv, NULL);
310 
311 	server = soup_test_server_new (TRUE);
312 	soup_server_add_handler (server, NULL, server_handler, NULL, NULL);
313 	uri = soup_test_server_get_uri (server, "http", NULL);
314 
315 	if (tls_available) {
316 		SoupSession *test_session;
317 		gint64 start, end;
318 
319 		https_server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD);
320 		soup_server_add_handler (https_server, NULL, server_handler, NULL, NULL);
321 		https_uri = soup_test_server_get_uri (server, "https", "127.0.0.1");
322 
323 		/* The 1-second timeouts are too fast for some machines... */
324 		test_session = soup_test_session_new (SOUP_TYPE_SESSION, NULL);
325 		start = g_get_monotonic_time ();
326 		do_message_to_session (test_session, uri, NULL, SOUP_STATUS_OK);
327 		end = g_get_monotonic_time ();
328 		soup_test_session_abort_unref (test_session);
329 		debug_printf (2, "  (https request took %0.3fs)\n", (end - start) / 1000000.0);
330 		if (end - start > 750000) {
331 			debug_printf (1, "  (using extra-slow mode)\n\n");
332 			slow_https = TRUE;
333 		} else {
334 			debug_printf (2, "\n");
335 			slow_https = FALSE;
336 		}
337 	} else
338 		https_uri = soup_uri_new ("https://fail.");
339 
340 	g_test_add_data_func ("/timeout/http/async", uri, do_async_timeout_tests);
341 	g_test_add_data_func ("/timeout/http/sync", uri, do_sync_timeout_tests);
342 	g_test_add_data_func ("/timeout/https/async", https_uri, do_async_timeout_tests);
343 	g_test_add_data_func ("/timeout/https/sync", https_uri, do_sync_timeout_tests);
344 
345 	ret = g_test_run ();
346 
347 	soup_uri_free (uri);
348 	soup_uri_free (https_uri);
349 	soup_test_server_quit_unref (server);
350 	if (https_server)
351 		soup_test_server_quit_unref (https_server);
352 
353 	test_cleanup ();
354 	return ret;
355 }
356