• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2011 Red Hat, Inc.
4  */
5 
6 /* Kill SoupRequester-related deprecation warnings */
7 #define SOUP_VERSION_MIN_REQUIRED SOUP_VERSION_2_40
8 
9 #include "test-utils.h"
10 
11 SoupServer *server;
12 GMainLoop *loop;
13 char buf[1024];
14 
15 SoupBuffer *response, *auth_response;
16 
17 #define REDIRECT_HTML_BODY "<html><body>Try again</body></html>\r\n"
18 #define AUTH_HTML_BODY "<html><body>Unauthorized</body></html>\r\n"
19 
20 typedef enum {
21 	NO_CANCEL,
22 	SYNC_CANCEL,
23 	PAUSE_AND_CANCEL_ON_IDLE
24 } CancelPolicy;
25 
26 static gboolean
slow_finish_message(gpointer msg)27 slow_finish_message (gpointer msg)
28 {
29 	SoupServer *server = g_object_get_data (G_OBJECT (msg), "server");
30 
31 	soup_server_unpause_message (server, msg);
32 	return FALSE;
33 }
34 
35 static void
slow_pause_message(SoupMessage * msg,gpointer server)36 slow_pause_message (SoupMessage *msg, gpointer server)
37 {
38 	soup_server_pause_message (server, msg);
39 	soup_add_timeout (soup_server_get_async_context (server),
40 			  1000, slow_finish_message, msg);
41 }
42 
43 static void
server_callback(SoupServer * server,SoupMessage * msg,const char * path,GHashTable * query,SoupClientContext * context,gpointer data)44 server_callback (SoupServer *server, SoupMessage *msg,
45 		 const char *path, GHashTable *query,
46 		 SoupClientContext *context, gpointer data)
47 {
48 	gboolean chunked = FALSE;
49 	int i;
50 
51 	if (strcmp (path, "/auth") == 0) {
52 		soup_message_set_status (msg, SOUP_STATUS_UNAUTHORIZED);
53 		soup_message_set_response (msg, "text/html",
54 					   SOUP_MEMORY_STATIC,
55 					   AUTH_HTML_BODY,
56 					   strlen (AUTH_HTML_BODY));
57 		soup_message_headers_append (msg->response_headers,
58 					     "WWW-Authenticate",
59 					     "Basic: realm=\"requester-test\"");
60 		return;
61 	} else if (strcmp (path, "/foo") == 0) {
62 		soup_message_set_redirect (msg, SOUP_STATUS_FOUND, "/");
63 		/* Make the response HTML so if we sniff that instead of the
64 		 * real body, we'll notice.
65 		 */
66 		soup_message_set_response (msg, "text/html",
67 					   SOUP_MEMORY_STATIC,
68 					   REDIRECT_HTML_BODY,
69 					   strlen (REDIRECT_HTML_BODY));
70 		return;
71 	} else if (strcmp (path, "/chunked") == 0) {
72 		chunked = TRUE;
73 	} else if (strcmp (path, "/non-persistent") == 0) {
74 		soup_message_headers_append (msg->response_headers,
75 					     "Connection", "close");
76 	} else if (!strcmp (path, "/slow")) {
77 		g_object_set_data (G_OBJECT (msg), "server", server);
78 		g_signal_connect (msg, "wrote-headers",
79 				  G_CALLBACK (slow_pause_message), server);
80 	}
81 
82 	soup_message_set_status (msg, SOUP_STATUS_OK);
83 
84 	if (chunked) {
85 		soup_message_headers_set_encoding (msg->response_headers,
86 						   SOUP_ENCODING_CHUNKED);
87 
88 		for (i = 0; i < response->length; i += 8192) {
89 			SoupBuffer *tmp;
90 
91 			tmp = soup_buffer_new_subbuffer (response, i,
92 							 MIN (8192, response->length - i));
93 			soup_message_body_append_buffer (msg->response_body, tmp);
94 			soup_buffer_free (tmp);
95 		}
96 		soup_message_body_complete (msg->response_body);
97 	} else
98 		soup_message_body_append_buffer (msg->response_body, response);
99 }
100 
101 typedef struct {
102 	GString *body;
103 	gboolean cancel;
104 } RequestData;
105 
106 static void
stream_closed(GObject * source,GAsyncResult * res,gpointer user_data)107 stream_closed (GObject *source, GAsyncResult *res, gpointer user_data)
108 {
109 	GInputStream *stream = G_INPUT_STREAM (source);
110 	GError *error = NULL;
111 
112 	g_input_stream_close_finish (stream, res, &error);
113 	g_assert_no_error (error);
114 	g_main_loop_quit (loop);
115 	g_object_unref (stream);
116 }
117 
118 static void
test_read_ready(GObject * source,GAsyncResult * res,gpointer user_data)119 test_read_ready (GObject *source, GAsyncResult *res, gpointer user_data)
120 {
121 	GInputStream *stream = G_INPUT_STREAM (source);
122 	RequestData *data = user_data;
123 	GString *body = data->body;
124 	GError *error = NULL;
125 	gsize nread;
126 
127 	nread = g_input_stream_read_finish (stream, res, &error);
128 	if (nread == -1) {
129 		g_assert_no_error (error);
130 		g_error_free (error);
131 		g_input_stream_close (stream, NULL, NULL);
132 		g_object_unref (stream);
133 		g_main_loop_quit (loop);
134 		return;
135 	} else if (nread == 0) {
136 		g_input_stream_close_async (stream,
137 					    G_PRIORITY_DEFAULT, NULL,
138 					    stream_closed, NULL);
139 		return;
140 	}
141 
142 	g_string_append_len (body, buf, nread);
143 	g_input_stream_read_async (stream, buf, sizeof (buf),
144 				   G_PRIORITY_DEFAULT, NULL,
145 				   test_read_ready, data);
146 }
147 
148 static void
auth_test_sent(GObject * source,GAsyncResult * res,gpointer user_data)149 auth_test_sent (GObject *source, GAsyncResult *res, gpointer user_data)
150 {
151 	RequestData *data = user_data;
152 	GInputStream *stream;
153 	GError *error = NULL;
154 	SoupMessage *msg;
155 	const char *content_type;
156 
157 	stream = soup_request_send_finish (SOUP_REQUEST (source), res, &error);
158 	if (!stream) {
159 		g_assert_no_error (error);
160 		g_clear_error (&error);
161 		g_main_loop_quit (loop);
162 		return;
163 	}
164 
165 	msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (source));
166 	soup_test_assert_message_status (msg, SOUP_STATUS_UNAUTHORIZED);
167 	g_object_unref (msg);
168 
169 	content_type = soup_request_get_content_type (SOUP_REQUEST (source));
170 	g_assert_cmpstr (content_type, ==, "text/html");
171 
172 	g_input_stream_read_async (stream, buf, sizeof (buf),
173 				   G_PRIORITY_DEFAULT, NULL,
174 				   test_read_ready, data);
175 }
176 
177 static void
test_sent(GObject * source,GAsyncResult * res,gpointer user_data)178 test_sent (GObject *source, GAsyncResult *res, gpointer user_data)
179 {
180 	RequestData *data = user_data;
181 	GInputStream *stream;
182 	GError *error = NULL;
183 	const char *content_type;
184 
185 	stream = soup_request_send_finish (SOUP_REQUEST (source), res, &error);
186 	if (data->cancel) {
187 		g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
188 		g_clear_error (&error);
189 		g_main_loop_quit (loop);
190 		return;
191 	} else {
192 		g_assert_no_error (error);
193 		if (!stream) {
194 			g_main_loop_quit (loop);
195 			g_clear_error (&error);
196 			return;
197 		}
198 	}
199 
200 	content_type = soup_request_get_content_type (SOUP_REQUEST (source));
201 	g_assert_cmpstr (content_type, ==, "text/plain");
202 
203 	g_input_stream_read_async (stream, buf, sizeof (buf),
204 				   G_PRIORITY_DEFAULT, NULL,
205 				   test_read_ready, data);
206 }
207 
208 static void
cancel_message(SoupMessage * msg,gpointer session)209 cancel_message (SoupMessage *msg, gpointer session)
210 {
211 	soup_session_cancel_message (session, msg, SOUP_STATUS_FORBIDDEN);
212 }
213 
214 typedef struct {
215 	SoupMessage *msg;
216 	SoupSession *session;
217 } CancelData;
218 
219 static gboolean
cancel_message_idle(CancelData * data)220 cancel_message_idle (CancelData *data)
221 {
222 	cancel_message (data->msg, data->session);
223 	return FALSE;
224 }
225 
226 static void
pause_and_cancel_message(SoupMessage * msg,gpointer session)227 pause_and_cancel_message (SoupMessage *msg, gpointer session)
228 {
229 	CancelData *data = g_new (CancelData, 1);
230 	GSource *source = g_idle_source_new ();
231 
232 	soup_session_pause_message (session, msg);
233 	data->msg = msg;
234 	data->session = session;
235 	g_source_set_callback (source, (GSourceFunc)cancel_message_idle, data, g_free);
236 	g_source_attach (source, soup_session_get_async_context (session));
237 	g_source_unref (source);
238 }
239 
240 static void
request_started(SoupSession * session,SoupMessage * msg,SoupSocket * socket,gpointer user_data)241 request_started (SoupSession *session, SoupMessage *msg,
242 		 SoupSocket *socket, gpointer user_data)
243 {
244 	SoupSocket **save_socket = user_data;
245 
246 	g_clear_object (save_socket);
247 	*save_socket = g_object_ref (socket);
248 }
249 
250 static void
do_async_test(SoupSession * session,SoupURI * uri,GAsyncReadyCallback callback,guint expected_status,SoupBuffer * expected_response,gboolean persistent,CancelPolicy cancel_policy)251 do_async_test (SoupSession *session, SoupURI *uri,
252 	       GAsyncReadyCallback callback, guint expected_status,
253 	       SoupBuffer *expected_response,
254 	       gboolean persistent, CancelPolicy cancel_policy)
255 {
256 	SoupRequester *requester;
257 	SoupRequest *request;
258 	guint started_id;
259 	SoupSocket *socket = NULL;
260 	SoupMessage *msg;
261 	RequestData data;
262 
263 	if (SOUP_IS_SESSION_ASYNC (session))
264 		requester = SOUP_REQUESTER (soup_session_get_feature (session, SOUP_TYPE_REQUESTER));
265 	else
266 		requester = NULL;
267 
268 	data.body = g_string_new (NULL);
269 	data.cancel = cancel_policy != NO_CANCEL;
270 	if (requester)
271 		request = soup_requester_request_uri (requester, uri, NULL);
272 	else
273 		request = soup_session_request_uri (session, uri, NULL);
274 	msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (request));
275 
276 	switch (cancel_policy) {
277 	case SYNC_CANCEL:
278 		g_signal_connect (msg, "got-headers",
279 				  G_CALLBACK (cancel_message), session);
280 		break;
281 	case PAUSE_AND_CANCEL_ON_IDLE:
282 		g_signal_connect (msg, "got-headers",
283 				  G_CALLBACK (pause_and_cancel_message), session);
284 		break;
285 	case NO_CANCEL:
286 		break;
287 	}
288 
289 	started_id = g_signal_connect (session, "request-started",
290 				       G_CALLBACK (request_started),
291 				       &socket);
292 
293 	soup_request_send_async (request, NULL, callback, &data);
294 	g_object_unref (request);
295 
296 	loop = g_main_loop_new (soup_session_get_async_context (session), TRUE);
297 	g_main_loop_run (loop);
298 	g_main_loop_unref (loop);
299 
300 	g_signal_handler_disconnect (session, started_id);
301 
302 	soup_test_assert_message_status (msg, expected_status);
303 	g_object_unref (msg);
304 
305 	if (expected_response) {
306 		soup_assert_cmpmem (data.body->str, data.body->len,
307 				    expected_response->data, expected_response->length);
308 	} else
309 		g_assert_cmpint (data.body->len, ==, 0);
310 
311 	if (persistent)
312 		g_assert_true (soup_socket_is_connected (socket));
313 	else
314 		g_assert_false (soup_socket_is_connected (socket));
315 
316 	g_object_unref (socket);
317 
318 	g_string_free (data.body, TRUE);
319 }
320 
321 static void
do_test_for_thread_and_context(SoupSession * session,SoupURI * base_uri)322 do_test_for_thread_and_context (SoupSession *session, SoupURI *base_uri)
323 {
324 	SoupRequester *requester;
325 	SoupURI *uri;
326 
327 	if (SOUP_IS_SESSION_ASYNC (session)) {
328 		requester = soup_requester_new ();
329 		soup_session_add_feature (session, SOUP_SESSION_FEATURE (requester));
330 		g_object_unref (requester);
331 	}
332 	soup_session_add_feature_by_type (session, SOUP_TYPE_CONTENT_SNIFFER);
333 
334 	debug_printf (1, "    basic test\n");
335 	do_async_test (session, base_uri, test_sent,
336 		       SOUP_STATUS_OK, response,
337 		       TRUE, NO_CANCEL);
338 
339 	debug_printf (1, "    chunked test\n");
340 	uri = soup_uri_new_with_base (base_uri, "/chunked");
341 	do_async_test (session, uri, test_sent,
342 		       SOUP_STATUS_OK, response,
343 		       TRUE, NO_CANCEL);
344 	soup_uri_free (uri);
345 
346 	debug_printf (1, "    auth test\n");
347 	uri = soup_uri_new_with_base (base_uri, "/auth");
348 	do_async_test (session, uri, auth_test_sent,
349 		       SOUP_STATUS_UNAUTHORIZED, auth_response,
350 		       TRUE, NO_CANCEL);
351 	soup_uri_free (uri);
352 
353 	debug_printf (1, "    non-persistent test\n");
354 	uri = soup_uri_new_with_base (base_uri, "/non-persistent");
355 	do_async_test (session, uri, test_sent,
356 		       SOUP_STATUS_OK, response,
357 		       FALSE, NO_CANCEL);
358 	soup_uri_free (uri);
359 
360 	debug_printf (1, "    cancellation test\n");
361 	uri = soup_uri_new_with_base (base_uri, "/");
362 	do_async_test (session, uri, test_sent,
363 		       SOUP_STATUS_FORBIDDEN, NULL,
364 		       FALSE, SYNC_CANCEL);
365 	soup_uri_free (uri);
366 
367 	debug_printf (1, "    cancellation after paused test\n");
368 	uri = soup_uri_new_with_base (base_uri, "/");
369 	do_async_test (session, uri, test_sent,
370 		       SOUP_STATUS_FORBIDDEN, NULL,
371 		       FALSE, PAUSE_AND_CANCEL_ON_IDLE);
372 	soup_uri_free (uri);
373 }
374 
375 static void
do_simple_plain_test(gconstpointer data)376 do_simple_plain_test (gconstpointer data)
377 {
378 	SoupURI *uri = (SoupURI *)data;
379 	SoupSession *session;
380 
381 	g_test_bug ("653707");
382 
383 	session = soup_test_session_new (SOUP_TYPE_SESSION, NULL);
384 	do_test_for_thread_and_context (session, uri);
385 	soup_test_session_abort_unref (session);
386 }
387 
388 static void
do_simple_async_test(gconstpointer data)389 do_simple_async_test (gconstpointer data)
390 {
391 	SoupURI *uri = (SoupURI *)data;
392 	SoupSession *session;
393 
394 	g_test_bug ("653707");
395 
396 	session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
397 					 SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
398 					 NULL);
399 	do_test_for_thread_and_context (session, uri);
400 	soup_test_session_abort_unref (session);
401 }
402 
403 static void
do_test_with_context_and_type(SoupURI * uri,gboolean plain_session)404 do_test_with_context_and_type (SoupURI *uri, gboolean plain_session)
405 {
406 	GMainContext *async_context;
407 	SoupSession *session;
408 
409 	g_test_bug ("653707");
410 
411 	async_context = g_main_context_new ();
412 	g_main_context_push_thread_default (async_context);
413 
414 	session = soup_test_session_new (plain_session ? SOUP_TYPE_SESSION : SOUP_TYPE_SESSION_ASYNC,
415 					 SOUP_SESSION_ASYNC_CONTEXT, async_context,
416 					 SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
417 					 NULL);
418 
419 	do_test_for_thread_and_context (session, uri);
420 	soup_test_session_abort_unref (session);
421 
422 	g_main_context_pop_thread_default (async_context);
423 	g_main_context_unref (async_context);
424 }
425 
426 static void
do_async_test_with_context(gconstpointer data)427 do_async_test_with_context (gconstpointer data)
428 {
429 	SoupURI *uri = (SoupURI *)data;
430 
431 	do_test_with_context_and_type (uri, FALSE);
432 }
433 
434 static void
do_plain_test_with_context(gconstpointer data)435 do_plain_test_with_context (gconstpointer data)
436 {
437 	SoupURI *uri = (SoupURI *)data;
438 
439 	do_test_with_context_and_type (uri, TRUE);
440 }
441 
442 static gpointer
async_test_thread(gpointer uri)443 async_test_thread (gpointer uri)
444 {
445 	do_test_with_context_and_type (uri, TRUE);
446 	return NULL;
447 }
448 
449 static gpointer
plain_test_thread(gpointer uri)450 plain_test_thread (gpointer uri)
451 {
452 	do_test_with_context_and_type (uri, FALSE);
453 	return NULL;
454 }
455 
456 static void
do_async_test_in_thread(gconstpointer data)457 do_async_test_in_thread (gconstpointer data)
458 {
459 	SoupURI *uri = (SoupURI *)data;
460 	GThread *thread;
461 
462 	thread = g_thread_new ("do_async_test_in_thread",
463 			       async_test_thread,
464 			       (gpointer)uri);
465 	g_thread_join (thread);
466 }
467 
468 static void
do_plain_test_in_thread(gconstpointer data)469 do_plain_test_in_thread (gconstpointer data)
470 {
471 	SoupURI *uri = (SoupURI *)data;
472 	GThread *thread;
473 
474 	thread = g_thread_new ("do_plain_test_in_thread",
475 			       plain_test_thread,
476 			       (gpointer)uri);
477 	g_thread_join (thread);
478 }
479 
480 static void
do_sync_request(SoupSession * session,SoupRequest * request,guint expected_status,SoupBuffer * expected_response,gboolean persistent,CancelPolicy cancel_policy)481 do_sync_request (SoupSession *session, SoupRequest *request,
482 		 guint expected_status, SoupBuffer *expected_response,
483 		 gboolean persistent, CancelPolicy cancel_policy)
484 {
485 	GInputStream *in;
486 	SoupMessage *msg;
487 	GError *error = NULL;
488 	GString *body;
489 	char buf[1024];
490 	gssize nread;
491 	guint started_id;
492 	SoupSocket *socket = NULL;
493 
494 	msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (request));
495 	if (cancel_policy == SYNC_CANCEL) {
496 		g_signal_connect (msg, "got-headers",
497 				  G_CALLBACK (cancel_message), session);
498 	}
499 
500 	started_id = g_signal_connect (session, "request-started",
501 				       G_CALLBACK (request_started),
502 				       &socket);
503 
504 	in = soup_request_send (request, NULL, &error);
505 	g_signal_handler_disconnect (session, started_id);
506 	if (cancel_policy == SYNC_CANCEL) {
507 		g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
508 		g_clear_error (&error);
509 		g_object_unref (msg);
510 		g_object_unref (socket);
511 		return;
512 	} else if (!in) {
513 		g_assert_no_error (error);
514 		g_clear_error (&error);
515 		g_object_unref (msg);
516 		g_object_unref (socket);
517 		return;
518 	}
519 
520 	soup_test_assert_message_status (msg, expected_status);
521 	g_object_unref (msg);
522 
523 	body = g_string_new (NULL);
524 	do {
525 		nread = g_input_stream_read (in, buf, sizeof (buf),
526 					     NULL, &error);
527 		g_assert_no_error (error);
528 		if (nread == -1) {
529 			g_clear_error (&error);
530 			break;
531 		}
532 		g_string_append_len (body, buf, nread);
533 	} while (nread > 0);
534 
535 	g_input_stream_close (in, NULL, &error);
536 	g_assert_no_error (error);
537 	g_clear_error (&error);
538 	g_object_unref (in);
539 
540 	if (expected_response) {
541 		soup_assert_cmpmem (body->str, body->len,
542 				    expected_response->data, expected_response->length);
543 	} else
544 		g_assert_cmpint (body->len, ==, 0);
545 
546 	if (persistent)
547 		g_assert_true (soup_socket_is_connected (socket));
548 	else
549 		g_assert_false (soup_socket_is_connected (socket));
550 	g_object_unref (socket);
551 
552 	g_string_free (body, TRUE);
553 }
554 
555 static void
do_sync_tests_for_session(SoupSession * session,SoupURI * base_uri)556 do_sync_tests_for_session (SoupSession *session, SoupURI *base_uri)
557 {
558 	SoupRequester *requester;
559 	SoupRequest *request;
560 	SoupURI *uri;
561 
562 	requester = SOUP_REQUESTER (soup_session_get_feature (session, SOUP_TYPE_REQUESTER));
563 
564 	uri = soup_uri_copy (base_uri);
565 
566 	debug_printf (1, "    basic test\n");
567 	if (requester)
568 		request = soup_requester_request_uri (requester, uri, NULL);
569 	else
570 		request = soup_session_request_uri (session, uri, NULL);
571 	do_sync_request (session, request,
572 			 SOUP_STATUS_OK, response,
573 			 TRUE, NO_CANCEL);
574 	g_object_unref (request);
575 
576 	debug_printf (1, "    chunked test\n");
577 	soup_uri_set_path (uri, "/chunked");
578 	if (requester)
579 		request = soup_requester_request_uri (requester, uri, NULL);
580 	else
581 		request = soup_session_request_uri (session, uri, NULL);
582 	do_sync_request (session, request,
583 			 SOUP_STATUS_OK, response,
584 			 TRUE, NO_CANCEL);
585 	g_object_unref (request);
586 
587 	debug_printf (1, "    auth test\n");
588 	soup_uri_set_path (uri, "/auth");
589 	if (requester)
590 		request = soup_requester_request_uri (requester, uri, NULL);
591 	else
592 		request = soup_session_request_uri (session, uri, NULL);
593 	do_sync_request (session, request,
594 			 SOUP_STATUS_UNAUTHORIZED, auth_response,
595 			 TRUE, NO_CANCEL);
596 	g_object_unref (request);
597 
598 	debug_printf (1, "    non-persistent test\n");
599 	soup_uri_set_path (uri, "/non-persistent");
600 	if (requester)
601 		request = soup_requester_request_uri (requester, uri, NULL);
602 	else
603 		request = soup_session_request_uri (session, uri, NULL);
604 	do_sync_request (session, request,
605 			 SOUP_STATUS_OK, response,
606 			 FALSE, NO_CANCEL);
607 	g_object_unref (request);
608 
609 	debug_printf (1, "    cancel test\n");
610 	soup_uri_set_path (uri, "/");
611 	if (requester)
612 		request = soup_requester_request_uri (requester, uri, NULL);
613 	else
614 		request = soup_session_request_uri (session, uri, NULL);
615 	do_sync_request (session, request,
616 			 SOUP_STATUS_FORBIDDEN, NULL,
617 			 TRUE, SYNC_CANCEL);
618 	g_object_unref (request);
619 
620 	soup_uri_free (uri);
621 }
622 
623 static void
do_plain_sync_test(gconstpointer data)624 do_plain_sync_test (gconstpointer data)
625 {
626 	SoupURI *uri = (SoupURI *)data;
627 	SoupSession *session;
628 
629 	session = soup_test_session_new (SOUP_TYPE_SESSION, NULL);
630 	do_sync_tests_for_session (session, uri);
631 	soup_test_session_abort_unref (session);
632 }
633 
634 static void
do_sync_sync_test(gconstpointer data)635 do_sync_sync_test (gconstpointer data)
636 {
637 	SoupURI *uri = (SoupURI *)data;
638 	SoupSession *session;
639 	SoupRequester *requester;
640 
641 	session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL);
642 	requester = soup_requester_new ();
643 	soup_session_add_feature (session, SOUP_SESSION_FEATURE (requester));
644 	g_object_unref (requester);
645 	do_sync_tests_for_session (session, uri);
646 	soup_test_session_abort_unref (session);
647 }
648 
649 static void
do_null_char_request(SoupSession * session,const char * encoded_data,const char * expected_data,int expected_len)650 do_null_char_request (SoupSession *session, const char *encoded_data,
651 		      const char *expected_data, int expected_len)
652 {
653 	GError *error = NULL;
654 	GInputStream *stream;
655 	SoupRequest *request;
656 	SoupURI *uri;
657 	char *uri_string, buf[256];
658 	gsize nread;
659 
660 	uri_string = g_strdup_printf ("data:text/html,%s", encoded_data);
661 	uri = soup_uri_new (uri_string);
662 	g_free (uri_string);
663 
664 	request = soup_session_request_uri (session, uri, NULL);
665 	stream = soup_test_request_send (request, NULL, 0, &error);
666 	g_assert_no_error (error);
667 	if (error) {
668 		g_error_free (error);
669 		g_object_unref (request);
670 		soup_uri_free (uri);
671 		return;
672 	}
673 
674 	g_input_stream_read_all (stream, buf, sizeof (buf), &nread, NULL, &error);
675 	g_assert_no_error (error);
676 	g_clear_error (&error);
677 
678 	soup_test_request_close_stream (request, stream, NULL, &error);
679 	g_assert_no_error (error);
680 	g_clear_error (&error);
681 
682 	soup_assert_cmpmem (buf, nread, expected_data, expected_len);
683 
684 	g_object_unref (stream);
685 	g_object_unref (request);
686 	soup_uri_free (uri);
687 }
688 
689 static void
do_null_char_test_for_session(SoupSession * session)690 do_null_char_test_for_session (SoupSession *session)
691 {
692 	static struct {
693 		const char *encoded_data;
694 		const char *expected_data;
695 		int expected_len;
696 	} test_cases[] = {
697 		{ "%3Cscript%3Ea%3D'%00'%3C%2Fscript%3E", "<script>a='\0'</script>", 22 },
698 		{ "%00%3Cscript%3Ea%3D42%3C%2Fscript%3E", "\0<script>a=42</script>", 22 },
699 		{ "%3Cscript%3E%00%3Cbr%2F%3E%3C%2Fscript%3E%00", "<script>\0<br/></script>\0", 24 },
700 	};
701 	static int num_test_cases = G_N_ELEMENTS(test_cases);
702 	int i;
703 
704 	for (i = 0; i < num_test_cases; i++) {
705 		do_null_char_request (session, test_cases[i].encoded_data,
706 				      test_cases[i].expected_data, test_cases[i].expected_len);
707 	}
708 }
709 
710 static void
do_plain_null_char_test(void)711 do_plain_null_char_test (void)
712 {
713 	SoupSession *session;
714 
715 	session = soup_test_session_new (SOUP_TYPE_SESSION, NULL);
716 	do_null_char_test_for_session (session);
717 	soup_test_session_abort_unref (session);
718 }
719 
720 static void
do_async_null_char_test(void)721 do_async_null_char_test (void)
722 {
723 	SoupSession *session;
724 
725 	session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
726 					 SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
727 					 NULL);
728 	do_null_char_test_for_session (session);
729 	soup_test_session_abort_unref (session);
730 }
731 
732 static void
close_test_msg_finished(SoupMessage * msg,gpointer user_data)733 close_test_msg_finished (SoupMessage *msg,
734 			 gpointer     user_data)
735 {
736 	gboolean *finished = user_data;
737 
738 	*finished = TRUE;
739 }
740 
741 static void
do_close_test_for_session(SoupSession * session,SoupURI * uri)742 do_close_test_for_session (SoupSession *session,
743 			   SoupURI     *uri)
744 {
745 	GError *error = NULL;
746 	GInputStream *stream;
747 	SoupRequest *request;
748 	guint64 start, end;
749 	GCancellable *cancellable;
750 	SoupMessage *msg;
751 	gboolean finished = FALSE;
752 
753 	debug_printf (1, "    normal close\n");
754 
755 	request = soup_session_request_uri (session, uri, NULL);
756 	stream = soup_test_request_send (request, NULL, 0, &error);
757 	g_assert_no_error (error);
758 	if (error) {
759 		g_error_free (error);
760 		g_object_unref (request);
761 		return;
762 	}
763 
764 	start = g_get_monotonic_time ();
765 	soup_test_request_close_stream (request, stream, NULL, &error);
766 	g_assert_no_error (error);
767 	g_clear_error (&error);
768 	end = g_get_monotonic_time ();
769 
770 	g_assert_cmpint (end - start, <=, 500000);
771 
772 	g_object_unref (stream);
773 	g_object_unref (request);
774 
775 
776 	debug_printf (1, "    error close\n");
777 
778 	request = soup_session_request_uri (session, uri, NULL);
779 	msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (request));
780 	g_signal_connect (msg, "finished", G_CALLBACK (close_test_msg_finished), &finished);
781 	g_object_unref (msg);
782 
783 	stream = soup_test_request_send (request, NULL, 0, &error);
784 	g_assert_no_error (error);
785 	if (error) {
786 		g_error_free (error);
787 		g_object_unref (request);
788 		return;
789 	}
790 
791 	cancellable = g_cancellable_new ();
792 	g_cancellable_cancel (cancellable);
793 	soup_test_request_close_stream (request, stream, cancellable, &error);
794 	if (error)
795 		g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
796 	g_clear_error (&error);
797 	g_object_unref (cancellable);
798 
799 	g_assert_true (finished);
800 
801 	g_object_unref (stream);
802 	g_object_unref (request);
803 }
804 
805 static void
do_async_close_test(gconstpointer data)806 do_async_close_test (gconstpointer data)
807 {
808 	SoupURI *uri = (SoupURI *)data;
809 	SoupSession *session;
810 	SoupURI *slow_uri;
811 
812 	g_test_bug ("695652");
813 	g_test_bug ("711260");
814 
815 	slow_uri = soup_uri_new_with_base ((SoupURI *)uri, "/slow");
816 
817 	session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
818 					 SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
819 					 NULL);
820 	do_close_test_for_session (session, slow_uri);
821 	soup_test_session_abort_unref (session);
822 
823 	soup_uri_free (slow_uri);
824 }
825 
826 static void
do_sync_close_test(gconstpointer data)827 do_sync_close_test (gconstpointer data)
828 {
829 	SoupURI *uri = (SoupURI *)data;
830 	SoupSession *session;
831 	SoupURI *slow_uri;
832 
833 	g_test_bug ("695652");
834 	g_test_bug ("711260");
835 
836 	slow_uri = soup_uri_new_with_base ((SoupURI *)uri, "/slow");
837 
838 	session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC,
839 					 SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
840 					 NULL);
841 	do_close_test_for_session (session, slow_uri);
842 	soup_test_session_abort_unref (session);
843 
844 	soup_uri_free (slow_uri);
845 }
846 
847 int
main(int argc,char ** argv)848 main (int argc, char **argv)
849 {
850 	SoupURI *uri;
851 	int ret;
852 
853 	test_init (argc, argv, NULL);
854 
855 	response = soup_test_get_index ();
856 	auth_response = soup_buffer_new (SOUP_MEMORY_STATIC,
857 					 AUTH_HTML_BODY,
858 					 strlen (AUTH_HTML_BODY));
859 
860 	server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD);
861 	soup_server_add_handler (server, NULL, server_callback, NULL, NULL);
862 
863 	uri = soup_test_server_get_uri (server, "http", NULL);
864 	soup_uri_set_path (uri, "/foo");
865 
866 	g_test_add_data_func ("/requester/simple/SoupSession", uri, do_simple_plain_test);
867 	g_test_add_data_func ("/requester/simple/SoupSessionAsync", uri, do_simple_async_test);
868 	g_test_add_data_func ("/requester/threaded/SoupSession", uri, do_plain_test_in_thread);
869 	g_test_add_data_func ("/requester/threaded/SoupSessionAsync", uri, do_async_test_in_thread);
870 	g_test_add_data_func ("/requester/context/SoupSession", uri, do_plain_test_with_context);
871 	g_test_add_data_func ("/requester/context/SoupSessionAsync", uri, do_async_test_with_context);
872 	g_test_add_data_func ("/requester/sync/SoupSession", uri, do_plain_sync_test);
873 	g_test_add_data_func ("/requester/sync/SoupSessionSync", uri, do_sync_sync_test);
874 	g_test_add_func ("/requester/null-char/SoupSession", do_plain_null_char_test);
875 	g_test_add_func ("/requester/null-char/SoupSessionAsync", do_async_null_char_test);
876 	g_test_add_data_func ("/requester/close/SoupSessionAsync", uri, do_async_close_test);
877 	g_test_add_data_func ("/requester/close/SoupSessionSync", uri, do_sync_close_test);
878 
879 	ret = g_test_run ();
880 
881 	soup_uri_free (uri);
882 	soup_buffer_free (auth_response);
883 	soup_test_server_quit_unref (server);
884 
885 	test_cleanup ();
886 	return ret;
887 }
888