• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright 2007-2012 Red Hat, Inc.
4  */
5 
6 #include "test-utils.h"
7 
8 #include <gio/gnetworking.h>
9 
10 SoupServer *server;
11 SoupURI *base_uri;
12 GMutex server_mutex;
13 
14 static void
forget_close(SoupMessage * msg,gpointer user_data)15 forget_close (SoupMessage *msg, gpointer user_data)
16 {
17 	soup_message_headers_remove (msg->response_headers, "Connection");
18 }
19 
20 static void
close_socket(SoupMessage * msg,gpointer user_data)21 close_socket (SoupMessage *msg, gpointer user_data)
22 {
23 	SoupSocket *sock = user_data;
24 	int sockfd;
25 
26 	/* Actually calling soup_socket_disconnect() here would cause
27 	 * us to leak memory, so just shutdown the socket instead.
28 	 */
29 	sockfd = soup_socket_get_fd (sock);
30 #ifdef G_OS_WIN32
31 	shutdown (sockfd, SD_SEND);
32 #else
33 	shutdown (sockfd, SHUT_WR);
34 #endif
35 
36 	/* Then add the missing data to the message now, so SoupServer
37 	 * can clean up after itself properly.
38 	 */
39 	soup_message_body_append (msg->response_body, SOUP_MEMORY_STATIC,
40 				  "foo", 3);
41 }
42 
43 static void
timeout_socket(SoupSocket * sock,gpointer user_data)44 timeout_socket (SoupSocket *sock, gpointer user_data)
45 {
46 	soup_socket_disconnect (sock);
47 }
48 
49 static void
timeout_request_started(SoupServer * server,SoupMessage * msg,SoupClientContext * client,gpointer user_data)50 timeout_request_started (SoupServer *server, SoupMessage *msg,
51 			 SoupClientContext *client, gpointer user_data)
52 {
53 	SoupSocket *sock;
54 	GMainContext *context = g_main_context_get_thread_default ();
55 	guint readable;
56 
57 	g_signal_handlers_disconnect_by_func (server, timeout_request_started, NULL);
58 
59 	G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
60 	sock = soup_client_context_get_socket (client);
61 	G_GNUC_END_IGNORE_DEPRECATIONS;
62 	readable = g_signal_connect (sock, "readable",
63 				    G_CALLBACK (timeout_socket), NULL);
64 
65 	g_mutex_unlock (&server_mutex);
66 	while (soup_socket_is_connected (sock))
67 		g_main_context_iteration (context, TRUE);
68 	g_signal_handler_disconnect (sock, readable);
69 }
70 
71 static void
setup_timeout_persistent(SoupServer * server,SoupSocket * sock)72 setup_timeout_persistent (SoupServer *server, SoupSocket *sock)
73 {
74 	char buf[1];
75 	gsize nread;
76 
77 	/* In order for the test to work correctly, we have to
78 	 * close the connection *after* the client side writes
79 	 * the request. To ensure that this happens reliably,
80 	 * regardless of thread scheduling, we:
81 	 *
82 	 *   1. Try to read off the socket now, knowing it will
83 	 *      fail (since the client is waiting for us to
84 	 *      return a response). This will cause it to
85 	 *      emit "readable" later.
86 	 *   2. Wait for the server to finish this request and
87 	 *      start reading the next one (and lock server_mutex
88 	 *      to interlock with the client and ensure that it
89 	 *      doesn't start writing its next request until
90 	 *      that point).
91 	 *   3. Block until "readable" is emitted, meaning the
92 	 *      client has written its request.
93 	 *   4. Close the socket.
94 	 */
95 
96 	soup_socket_read (sock, buf, 1, &nread, NULL, NULL);
97 	g_mutex_lock (&server_mutex);
98 	g_signal_connect (server, "request-started",
99 			  G_CALLBACK (timeout_request_started), NULL);
100 }
101 
102 static void
server_callback(SoupServer * server,SoupMessage * msg,const char * path,GHashTable * query,SoupClientContext * context,gpointer data)103 server_callback (SoupServer *server, SoupMessage *msg,
104 		 const char *path, GHashTable *query,
105 		 SoupClientContext *context, gpointer data)
106 {
107 	/* The way this gets used in the tests, we don't actually
108 	 * need to hold it through the whole function, so it's simpler
109 	 * to just release it right away.
110 	 */
111 	g_mutex_lock (&server_mutex);
112 	g_mutex_unlock (&server_mutex);
113 
114 	if (msg->method != SOUP_METHOD_GET && msg->method != SOUP_METHOD_POST) {
115 		soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
116 		return;
117 	}
118 
119 	if (g_str_has_prefix (path, "/content-length/")) {
120 		gboolean too_long = strcmp (path, "/content-length/long") == 0;
121 		gboolean no_close = strcmp (path, "/content-length/noclose") == 0;
122 
123 		soup_message_set_status (msg, SOUP_STATUS_OK);
124 		soup_message_set_response (msg, "text/plain",
125 					   SOUP_MEMORY_STATIC, "foobar", 6);
126 		if (too_long)
127 			soup_message_headers_set_content_length (msg->response_headers, 9);
128 		soup_message_headers_append (msg->response_headers,
129 					     "Connection", "close");
130 
131 		if (too_long) {
132 			SoupSocket *sock;
133 
134 			/* soup-message-io will wait for us to add
135 			 * another chunk after the first, to fill out
136 			 * the declared Content-Length. Instead, we
137 			 * forcibly close the socket at that point.
138 			 */
139 			G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
140 			sock = soup_client_context_get_socket (context);
141 			G_GNUC_END_IGNORE_DEPRECATIONS;
142 			g_signal_connect (msg, "wrote-chunk",
143 					  G_CALLBACK (close_socket), sock);
144 		} else if (no_close) {
145 			/* Remove the 'Connection: close' after writing
146 			 * the headers, so that when we check it after
147 			 * writing the body, we'll think we aren't
148 			 * supposed to close it.
149 			 */
150 			g_signal_connect (msg, "wrote-headers",
151 					  G_CALLBACK (forget_close), NULL);
152 		}
153 		return;
154 	}
155 
156 	if (!strcmp (path, "/timeout-persistent")) {
157 		SoupSocket *sock;
158 
159 		G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
160 		sock = soup_client_context_get_socket (context);
161 		G_GNUC_END_IGNORE_DEPRECATIONS;
162 		setup_timeout_persistent (server, sock);
163 	}
164 
165 	soup_message_set_status (msg, SOUP_STATUS_OK);
166 	soup_message_set_response (msg, "text/plain",
167 				   SOUP_MEMORY_STATIC, "index", 5);
168 	return;
169 }
170 
171 static void
do_content_length_framing_test(void)172 do_content_length_framing_test (void)
173 {
174 	SoupSession *session;
175 	SoupMessage *msg;
176 	SoupURI *request_uri;
177 	goffset declared_length;
178 
179 	g_test_bug ("611481");
180 
181 	session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
182 
183 	debug_printf (1, "  Content-Length larger than message body length\n");
184 	request_uri = soup_uri_new_with_base (base_uri, "/content-length/long");
185 	msg = soup_message_new_from_uri ("GET", request_uri);
186 	soup_session_send_message (session, msg);
187 
188 	soup_test_assert_message_status (msg, SOUP_STATUS_OK);
189 
190 	declared_length = soup_message_headers_get_content_length (msg->response_headers);
191 	debug_printf (2, "    Content-Length: %lu, body: %s\n",
192 		      (gulong)declared_length, msg->response_body->data);
193 	g_assert_cmpint (msg->response_body->length, <, declared_length);
194 
195 	soup_uri_free (request_uri);
196 	g_object_unref (msg);
197 
198 	debug_printf (1, "  Server claims 'Connection: close' but doesn't\n");
199 	request_uri = soup_uri_new_with_base (base_uri, "/content-length/noclose");
200 	msg = soup_message_new_from_uri ("GET", request_uri);
201 	soup_session_send_message (session, msg);
202 
203 	soup_test_assert_message_status (msg, SOUP_STATUS_OK);
204 
205 	declared_length = soup_message_headers_get_content_length (msg->response_headers);
206 	g_assert_cmpint (msg->response_body->length, ==, declared_length);
207 
208 	soup_uri_free (request_uri);
209 	g_object_unref (msg);
210 
211 	soup_test_session_abort_unref (session);
212 }
213 
214 static void
request_started_socket_collector(SoupSession * session,SoupMessage * msg,SoupSocket * socket,gpointer user_data)215 request_started_socket_collector (SoupSession *session, SoupMessage *msg,
216 				  SoupSocket *socket, gpointer user_data)
217 {
218 	SoupSocket **sockets = user_data;
219 	int i;
220 
221 	debug_printf (2, "      msg %p => socket %p\n", msg, socket);
222 	for (i = 0; i < 4; i++) {
223 		if (!sockets[i]) {
224 			/* We ref the socket to make sure that even if
225 			 * it gets disconnected, it doesn't get freed,
226 			 * since our checks would get messed up if the
227 			 * slice allocator reused the same address for
228 			 * two consecutive sockets.
229 			 */
230 			sockets[i] = g_object_ref (socket);
231 			break;
232 		}
233 	}
234 
235 	soup_test_assert (i < 4, "socket queue overflowed");
236 }
237 
238 static void
do_timeout_test_for_session(SoupSession * session)239 do_timeout_test_for_session (SoupSession *session)
240 {
241 	SoupMessage *msg;
242 	SoupSocket *sockets[4] = { NULL, NULL, NULL, NULL };
243 	SoupURI *timeout_uri;
244 	int i;
245 
246 	g_signal_connect (session, "request-started",
247 			  G_CALLBACK (request_started_socket_collector),
248 			  &sockets);
249 
250 	debug_printf (1, "    First message\n");
251 	timeout_uri = soup_uri_new_with_base (base_uri, "/timeout-persistent");
252 	msg = soup_message_new_from_uri ("GET", timeout_uri);
253 	soup_uri_free (timeout_uri);
254 	soup_session_send_message (session, msg);
255 	soup_test_assert_message_status (msg, SOUP_STATUS_OK);
256 
257 	if (sockets[1]) {
258 		soup_test_assert (sockets[1] == NULL, "Message was retried");
259 		sockets[1] = sockets[2] = sockets[3] = NULL;
260 	}
261 	g_object_unref (msg);
262 
263 	/* The server will grab server_mutex before returning the response,
264 	 * and release it when it's ready for us to send the second request.
265 	 */
266 	g_mutex_lock (&server_mutex);
267 	g_mutex_unlock (&server_mutex);
268 
269 	debug_printf (1, "    Second message\n");
270 	msg = soup_message_new_from_uri ("GET", base_uri);
271 	soup_session_send_message (session, msg);
272 	soup_test_assert_message_status (msg, SOUP_STATUS_OK);
273 
274 	soup_test_assert (sockets[1] == sockets[0],
275 			  "Message was not retried on existing connection");
276 	soup_test_assert (sockets[2] != NULL,
277 			  "Message was not retried after disconnect");
278 	soup_test_assert (sockets[2] != sockets[1],
279 			  "Message was retried on closed connection");
280 	soup_test_assert (sockets[3] == NULL,
281 			  "Message was retried again");
282 	g_object_unref (msg);
283 
284 	for (i = 0; sockets[i]; i++)
285 		g_object_unref (sockets[i]);
286 }
287 
288 static void
do_timeout_req_test_for_session(SoupSession * session)289 do_timeout_req_test_for_session (SoupSession *session)
290 {
291 	SoupRequest *req;
292 	SoupMessage *msg;
293 	GInputStream *stream;
294 	SoupSocket *sockets[4] = { NULL, NULL, NULL, NULL };
295 	SoupURI *timeout_uri;
296 	GError *error = NULL;
297 	int i;
298 
299 	g_signal_connect (session, "request-started",
300 			  G_CALLBACK (request_started_socket_collector),
301 			  &sockets);
302 
303 	debug_printf (1, "    First request\n");
304 	timeout_uri = soup_uri_new_with_base (base_uri, "/timeout-persistent");
305 	req = soup_session_request_uri (session, timeout_uri, NULL);
306 	soup_uri_free (timeout_uri);
307 
308 	stream = soup_test_request_send (req, NULL, 0, &error);
309 	if (error) {
310 		g_assert_no_error (error);
311 		g_clear_error (&error);
312 	} else {
313 		soup_test_request_read_all (req, stream, NULL, &error);
314 		if (error) {
315 			g_assert_no_error (error);
316 			g_clear_error (&error);
317 		}
318 
319 		soup_test_request_close_stream (req, stream, NULL, &error);
320 		if (error) {
321 			g_assert_no_error (error);
322 			g_clear_error (&error);
323 		}
324 		g_object_unref (stream);
325 	}
326 
327 	if (sockets[1]) {
328 		soup_test_assert (sockets[1] == NULL, "Message was retried");
329 		sockets[1] = sockets[2] = sockets[3] = NULL;
330 	}
331 	g_object_unref (req);
332 
333 	/* The server will grab server_mutex before returning the response,
334 	 * and release it when it's ready for us to send the second request.
335 	 */
336 	g_mutex_lock (&server_mutex);
337 	g_mutex_unlock (&server_mutex);
338 
339 	debug_printf (1, "    Second request\n");
340 	req = soup_session_request_uri (session, base_uri, NULL);
341 
342 	stream = soup_test_request_send (req, NULL, 0, &error);
343 	if (error) {
344 		g_assert_no_error (error);
345 		g_clear_error (&error);
346 	} else {
347 		soup_test_request_close_stream (req, stream, NULL, &error);
348 		if (error) {
349 			g_assert_no_error (error);
350 			g_clear_error (&error);
351 		}
352 		g_object_unref (stream);
353 	}
354 
355 	msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (req));
356 	soup_test_assert_message_status (msg, SOUP_STATUS_OK);
357 
358 	soup_test_assert (sockets[1] == sockets[0],
359 			  "Message was not retried on existing connection");
360 	soup_test_assert (sockets[2] != NULL,
361 			  "Message was not retried after disconnect");
362 	soup_test_assert (sockets[2] != sockets[1],
363 			  "Message was retried on closed connection");
364 	soup_test_assert (sockets[3] == NULL,
365 			  "Message was retried again");
366 	g_object_unref (msg);
367 	g_object_unref (req);
368 
369 	for (i = 0; sockets[i]; i++)
370 		g_object_unref (sockets[i]);
371 }
372 
373 static void
do_persistent_connection_timeout_test(void)374 do_persistent_connection_timeout_test (void)
375 {
376 	SoupSession *session;
377 
378 	g_test_bug ("631525");
379 
380 	debug_printf (1, "  Async session, message API\n");
381 	session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
382 	do_timeout_test_for_session (session);
383 	soup_test_session_abort_unref (session);
384 
385 	debug_printf (1, "  Async session, request API\n");
386 	session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
387 					 SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
388 					 NULL);
389 	do_timeout_req_test_for_session (session);
390 	soup_test_session_abort_unref (session);
391 
392 	debug_printf (1, "  Sync session, message API\n");
393 	session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL);
394 	do_timeout_test_for_session (session);
395 	soup_test_session_abort_unref (session);
396 
397 	debug_printf (1, "  Sync session, request API\n");
398 	session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL);
399 	do_timeout_req_test_for_session (session);
400 	soup_test_session_abort_unref (session);
401 }
402 
403 static void
cancel_cancellable_handler(SoupSession * session,SoupMessage * msg,SoupSocket * socket,gpointer user_data)404 cancel_cancellable_handler (SoupSession *session, SoupMessage *msg,
405 			    SoupSocket *socket, gpointer user_data)
406 {
407 	g_cancellable_cancel (user_data);
408 }
409 
410 static void
do_persistent_connection_timeout_test_with_cancellation(void)411 do_persistent_connection_timeout_test_with_cancellation (void)
412 {
413 	SoupSession *session;
414 	SoupMessage *msg;
415 	SoupSocket *sockets[4] = { NULL, NULL, NULL, NULL };
416 	SoupURI *timeout_uri;
417 	GCancellable *cancellable;
418 	GInputStream *response;
419 	int i;
420 	char buf[8192];
421 
422 	session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL);
423 
424 	g_signal_connect (session, "request-started",
425 			  G_CALLBACK (request_started_socket_collector),
426 			  &sockets);
427 
428 	debug_printf (1, "    First message\n");
429 	timeout_uri = soup_uri_new_with_base (base_uri, "/timeout-persistent");
430 	msg = soup_message_new_from_uri ("GET", timeout_uri);
431 	cancellable = g_cancellable_new ();
432 	soup_uri_free (timeout_uri);
433 	response = soup_session_send (session, msg, cancellable, NULL);
434 	soup_test_assert_message_status (msg, SOUP_STATUS_OK);
435 
436 	if (sockets[1]) {
437 		soup_test_assert (sockets[1] == NULL, "Message was retried");
438 		sockets[1] = sockets[2] = sockets[3] = NULL;
439 	}
440 	g_object_unref (msg);
441 
442 	soup_test_assert (response, "No response received");
443 
444 	while (g_input_stream_read (response, buf, sizeof (buf), NULL, NULL))
445 		debug_printf (1, "Reading response\n");
446 
447 	soup_test_assert (!g_cancellable_is_cancelled (cancellable),
448 			  "User-supplied cancellable was cancelled");
449 
450 	g_object_unref (response);
451 
452 	/* The server will grab server_mutex before returning the response,
453 	 * and release it when it's ready for us to send the second request.
454 	 */
455 	g_mutex_lock (&server_mutex);
456 	g_mutex_unlock (&server_mutex);
457 
458 	debug_printf (1, "    Second message\n");
459 	msg = soup_message_new_from_uri ("GET", base_uri);
460 
461 	/* Cancel the cancellable in the signal handler, and then check that it
462 	 * was not reset below */
463 	g_signal_connect (session, "request-started",
464 			  G_CALLBACK (cancel_cancellable_handler),
465 			  cancellable);
466 
467 	response = soup_session_send (session, msg, cancellable, NULL);
468 
469 	soup_test_assert (response == NULL, "Unexpected response");
470 
471 	soup_test_assert_message_status (msg, SOUP_STATUS_NONE);
472 
473 	soup_test_assert (sockets[1] == sockets[0],
474 			  "Message was not retried on existing connection");
475 	soup_test_assert (sockets[2] != sockets[1],
476 			  "Message was retried on closed connection");
477 	soup_test_assert (sockets[3] == NULL,
478 			  "Message was retried again");
479 	g_object_unref (msg);
480 
481 	/* cancellable should not have been reset, it should still be in the
482 	 * cancelled state */
483 	soup_test_assert (g_cancellable_is_cancelled (cancellable),
484 			  "User-supplied cancellable was reset");
485 
486 	for (i = 0; sockets[i]; i++)
487 		g_object_unref (sockets[i]);
488 
489 	g_object_unref (cancellable);
490 
491 	soup_test_session_abort_unref (session);
492 }
493 
494 static GMainLoop *max_conns_loop;
495 static int msgs_done;
496 static guint quit_loop_timeout;
497 #define MAX_CONNS 2
498 #define TEST_CONNS (MAX_CONNS * 2) + 1
499 
500 static gboolean
idle_start_server(gpointer data)501 idle_start_server (gpointer data)
502 {
503 	g_mutex_unlock (&server_mutex);
504 	return FALSE;
505 }
506 
507 static gboolean
quit_loop(gpointer data)508 quit_loop (gpointer data)
509 {
510 	quit_loop_timeout = 0;
511 	g_main_loop_quit (max_conns_loop);
512 	return FALSE;
513 }
514 
515 static void
max_conns_request_started(SoupSession * session,SoupMessage * msg,SoupSocket * socket,gpointer user_data)516 max_conns_request_started (SoupSession *session, SoupMessage *msg,
517 			   SoupSocket *socket, gpointer user_data)
518 {
519 	if (++msgs_done >= MAX_CONNS) {
520 		if (quit_loop_timeout)
521 			g_source_remove (quit_loop_timeout);
522 		quit_loop_timeout = g_timeout_add (100, quit_loop, NULL);
523 	}
524 }
525 
526 static void
max_conns_message_complete(SoupSession * session,SoupMessage * msg,gpointer user_data)527 max_conns_message_complete (SoupSession *session, SoupMessage *msg, gpointer user_data)
528 {
529 	if (++msgs_done == TEST_CONNS)
530 		g_main_loop_quit (max_conns_loop);
531 }
532 
533 static void
do_max_conns_test_for_session(SoupSession * session)534 do_max_conns_test_for_session (SoupSession *session)
535 {
536 	SoupMessage *msgs[TEST_CONNS + 1];
537 	SoupMessageFlags flags;
538 	int i;
539 
540 	max_conns_loop = g_main_loop_new (NULL, TRUE);
541 
542 	g_mutex_lock (&server_mutex);
543 
544 	g_signal_connect (session, "request-started",
545 			  G_CALLBACK (max_conns_request_started), NULL);
546 	msgs_done = 0;
547 	for (i = 0; i < TEST_CONNS - 1; i++) {
548 		msgs[i] = soup_message_new_from_uri ("GET", base_uri);
549 		g_object_ref (msgs[i]);
550 		soup_session_queue_message (session, msgs[i],
551 					    max_conns_message_complete, NULL);
552 	}
553 
554 	g_main_loop_run (max_conns_loop);
555 	g_assert_cmpint (msgs_done, ==, MAX_CONNS);
556 
557 	if (quit_loop_timeout)
558 		g_source_remove (quit_loop_timeout);
559 	quit_loop_timeout = g_timeout_add (1000, quit_loop, NULL);
560 
561 	/* Message with SOUP_MESSAGE_IGNORE_CONNECTION_LIMITS should start */
562 	msgs[i] = soup_message_new_from_uri ("GET", base_uri);
563 	flags = soup_message_get_flags (msgs[i]);
564 	soup_message_set_flags (msgs[i], flags | SOUP_MESSAGE_IGNORE_CONNECTION_LIMITS);
565 	g_object_ref (msgs[i]);
566 	soup_session_queue_message (session, msgs[i],
567 				    max_conns_message_complete, NULL);
568 
569 	g_main_loop_run (max_conns_loop);
570 	g_assert_cmpint (msgs_done, ==, MAX_CONNS + 1);
571 	g_signal_handlers_disconnect_by_func (session, max_conns_request_started, NULL);
572 
573 	msgs_done = 0;
574 	g_idle_add (idle_start_server, NULL);
575 	if (quit_loop_timeout)
576 		g_source_remove (quit_loop_timeout);
577 	quit_loop_timeout = g_timeout_add (1000, quit_loop, NULL);
578 	g_main_loop_run (max_conns_loop);
579 
580 	for (i = 0; i < TEST_CONNS; i++)
581 		soup_test_assert_message_status (msgs[i], SOUP_STATUS_OK);
582 
583 	if (msgs_done != TEST_CONNS) {
584 		/* Clean up so we don't get a spurious "Leaked
585 		 * session" error.
586 		 */
587 		for (i = 0; i < TEST_CONNS; i++)
588 			soup_session_cancel_message (session, msgs[i], SOUP_STATUS_CANCELLED);
589 		g_main_loop_run (max_conns_loop);
590 	}
591 
592 	g_main_loop_unref (max_conns_loop);
593 	if (quit_loop_timeout) {
594 		g_source_remove (quit_loop_timeout);
595 		quit_loop_timeout = 0;
596 	}
597 
598 	for (i = 0; i < TEST_CONNS; i++)
599 		g_object_unref (msgs[i]);
600 }
601 
602 static void
do_max_conns_test(void)603 do_max_conns_test (void)
604 {
605 	SoupSession *session;
606 
607 	g_test_bug ("634422");
608 
609 	debug_printf (1, "  Async session\n");
610 	session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
611 					 SOUP_SESSION_MAX_CONNS, MAX_CONNS,
612 					 NULL);
613 	do_max_conns_test_for_session (session);
614 	soup_test_session_abort_unref (session);
615 
616 	debug_printf (1, "  Sync session\n");
617 	session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC,
618 					 SOUP_SESSION_MAX_CONNS, MAX_CONNS,
619 					 NULL);
620 	do_max_conns_test_for_session (session);
621 	soup_test_session_abort_unref (session);
622 }
623 
624 static void
np_request_started(SoupSession * session,SoupMessage * msg,SoupSocket * socket,gpointer user_data)625 np_request_started (SoupSession *session, SoupMessage *msg,
626 		    SoupSocket *socket, gpointer user_data)
627 {
628 	SoupSocket **save_socket = user_data;
629 
630 	*save_socket = g_object_ref (socket);
631 }
632 
633 static void
np_request_unqueued(SoupSession * session,SoupMessage * msg,gpointer user_data)634 np_request_unqueued (SoupSession *session, SoupMessage *msg,
635 		     gpointer user_data)
636 {
637 	SoupSocket *socket = *(SoupSocket **)user_data;
638 
639 	g_assert_false (soup_socket_is_connected (socket));
640 }
641 
642 static void
np_request_finished(SoupSession * session,SoupMessage * msg,gpointer user_data)643 np_request_finished (SoupSession *session, SoupMessage *msg,
644 		     gpointer user_data)
645 {
646 	GMainLoop *loop = user_data;
647 
648 	g_main_loop_quit (loop);
649 }
650 
651 static void
do_non_persistent_test_for_session(SoupSession * session)652 do_non_persistent_test_for_session (SoupSession *session)
653 {
654 	SoupMessage *msg;
655 	SoupSocket *socket = NULL;
656 	GMainLoop *loop;
657 
658 	loop = g_main_loop_new (NULL, FALSE);
659 
660 	g_signal_connect (session, "request-started",
661 			  G_CALLBACK (np_request_started),
662 			  &socket);
663 	g_signal_connect (session, "request-unqueued",
664 			  G_CALLBACK (np_request_unqueued),
665 			  &socket);
666 
667 	msg = soup_message_new_from_uri ("GET", base_uri);
668 	soup_message_headers_append (msg->request_headers, "Connection", "close");
669 	g_object_ref (msg);
670 	soup_session_queue_message (session, msg,
671 				    np_request_finished, loop);
672 	g_main_loop_run (loop);
673 	g_main_loop_unref (loop);
674 
675 	soup_test_assert_message_status (msg, SOUP_STATUS_OK);
676 
677 	g_object_unref (msg);
678 	g_object_unref (socket);
679 }
680 
681 static void
do_non_persistent_connection_test(void)682 do_non_persistent_connection_test (void)
683 {
684 	SoupSession *session;
685 
686 	g_test_bug ("578990");
687 
688 	debug_printf (1, "  Async session\n");
689 	session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
690 	do_non_persistent_test_for_session (session);
691 	soup_test_session_abort_unref (session);
692 
693 	debug_printf (1, "  Sync session\n");
694 	session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL);
695 	do_non_persistent_test_for_session (session);
696 	soup_test_session_abort_unref (session);
697 }
698 
699 static void
do_non_idempotent_test_for_session(SoupSession * session)700 do_non_idempotent_test_for_session (SoupSession *session)
701 {
702 	SoupMessage *msg;
703 	SoupSocket *sockets[4] = { NULL, NULL, NULL, NULL };
704 	int i;
705 
706 	g_signal_connect (session, "request-started",
707 			  G_CALLBACK (request_started_socket_collector),
708 			  &sockets);
709 
710 	debug_printf (2, "    GET\n");
711 	msg = soup_message_new_from_uri ("GET", base_uri);
712 	soup_session_send_message (session, msg);
713 	soup_test_assert_message_status (msg, SOUP_STATUS_OK);
714 	if (sockets[1]) {
715 		soup_test_assert (sockets[1] == NULL, "Message was retried");
716 		sockets[1] = sockets[2] = sockets[3] = NULL;
717 	}
718 	g_object_unref (msg);
719 
720 	debug_printf (2, "    POST\n");
721 	msg = soup_message_new_from_uri ("POST", base_uri);
722 	soup_session_send_message (session, msg);
723 	soup_test_assert_message_status (msg, SOUP_STATUS_OK);
724 	soup_test_assert (sockets[1] != sockets[0],
725 			  "Message was sent on existing connection");
726 	soup_test_assert (sockets[2] == NULL,
727 			  "Too many connections used");
728 
729 	g_object_unref (msg);
730 
731 	for (i = 0; sockets[i]; i++)
732 		g_object_unref (sockets[i]);
733 }
734 
735 static void
do_non_idempotent_connection_test(void)736 do_non_idempotent_connection_test (void)
737 {
738 	SoupSession *session;
739 
740 	debug_printf (1, "  Async session\n");
741 	session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
742 	do_non_idempotent_test_for_session (session);
743 	soup_test_session_abort_unref (session);
744 
745 	debug_printf (1, "  Sync session\n");
746 	session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL);
747 	do_non_idempotent_test_for_session (session);
748 	soup_test_session_abort_unref (session);
749 }
750 
751 #define HTTP_SERVER  "http://127.0.0.1:47524"
752 #define HTTPS_SERVER "https://127.0.0.1:47525"
753 #define HTTP_PROXY   "http://127.0.0.1:47526"
754 
755 static SoupConnectionState state_transitions[] = {
756 	/* NEW -> */        SOUP_CONNECTION_CONNECTING,
757 	/* CONNECTING -> */ SOUP_CONNECTION_IN_USE,
758 	/* IDLE -> */       SOUP_CONNECTION_DISCONNECTED,
759 	/* IN_USE -> */     SOUP_CONNECTION_IDLE,
760 
761 	/* REMOTE_DISCONNECTED */ -1,
762 	/* DISCONNECTED */        -1,
763 };
764 
765 static const char *state_names[] = {
766 	"NEW", "CONNECTING", "IDLE", "IN_USE",
767 	"REMOTE_DISCONNECTED", "DISCONNECTED"
768 };
769 
770 static void
connection_state_changed(GObject * object,GParamSpec * param,gpointer user_data)771 connection_state_changed (GObject *object, GParamSpec *param,
772 			  gpointer user_data)
773 {
774 	SoupConnectionState *state = user_data;
775 	SoupConnectionState new_state;
776 
777 	g_object_get (object, "state", &new_state, NULL);
778 	debug_printf (2, "      %s -> %s\n",
779 		      state_names[*state], state_names[new_state]);
780 	soup_test_assert (state_transitions[*state] == new_state,
781 			  "Unexpected transition: %s -> %s\n",
782 			  state_names[*state], state_names[new_state]);
783 	*state = new_state;
784 }
785 
786 static void
connection_created(SoupSession * session,GObject * conn,gpointer user_data)787 connection_created (SoupSession *session, GObject *conn,
788 		    gpointer user_data)
789 {
790 	SoupConnectionState *state = user_data;
791 
792 	g_object_get (conn, "state", state, NULL);
793 	g_assert_cmpint (*state, ==, SOUP_CONNECTION_NEW);
794 
795 	g_signal_connect (conn, "notify::state",
796 			  G_CALLBACK (connection_state_changed),
797 			  state);
798 }
799 
800 static void
do_one_connection_state_test(SoupSession * session,const char * uri)801 do_one_connection_state_test (SoupSession *session, const char *uri)
802 {
803 	SoupMessage *msg;
804 
805 	msg = soup_message_new ("GET", uri);
806 	soup_session_send_message (session, msg);
807 	soup_test_assert_message_status (msg, SOUP_STATUS_OK);
808 	g_object_unref (msg);
809 	soup_session_abort (session);
810 }
811 
812 static void
do_connection_state_test_for_session(SoupSession * session)813 do_connection_state_test_for_session (SoupSession *session)
814 {
815 	SoupConnectionState state;
816 	SoupURI *proxy_uri;
817 
818 	g_signal_connect (session, "connection-created",
819 			  G_CALLBACK (connection_created),
820 			  &state);
821 
822 	debug_printf (1, "    http\n");
823 	do_one_connection_state_test (session, HTTP_SERVER);
824 
825 	if (tls_available) {
826 		debug_printf (1, "    https\n");
827 		do_one_connection_state_test (session, HTTPS_SERVER);
828 	} else
829 		debug_printf (1, "    https -- SKIPPING\n");
830 
831 	proxy_uri = soup_uri_new (HTTP_PROXY);
832 	g_object_set (G_OBJECT (session),
833 		      SOUP_SESSION_PROXY_URI, proxy_uri,
834 		      NULL);
835 	soup_uri_free (proxy_uri);
836 
837 	debug_printf (1, "    http with proxy\n");
838 	do_one_connection_state_test (session, HTTP_SERVER);
839 
840 	if (tls_available) {
841 		debug_printf (1, "    https with proxy\n");
842 		do_one_connection_state_test (session, HTTPS_SERVER);
843 	} else
844 		debug_printf (1, "    https with proxy -- SKIPPING\n");
845 }
846 
847 static void
do_connection_state_test(void)848 do_connection_state_test (void)
849 {
850 	SoupSession *session;
851 
852 	SOUP_TEST_SKIP_IF_NO_APACHE;
853 
854 	debug_printf (1, "  Async session\n");
855 	session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
856 	do_connection_state_test_for_session (session);
857 	soup_test_session_abort_unref (session);
858 
859 	debug_printf (1, "  Sync session\n");
860 	session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL);
861 	do_connection_state_test_for_session (session);
862 	soup_test_session_abort_unref (session);
863 }
864 
865 
866 static const char *event_names[] = {
867 	"RESOLVING", "RESOLVED", "CONNECTING", "CONNECTED",
868 	"PROXY_NEGOTIATING", "PROXY_NEGOTIATED",
869 	"TLS_HANDSHAKING", "TLS_HANDSHAKED", "COMPLETE"
870 };
871 
872 static const char event_abbrevs[] = {
873 	'r', 'R', 'c', 'C', 'p', 'P', 't', 'T', 'x', '\0'
874 };
875 
876 static const char *
event_name_from_abbrev(char abbrev)877 event_name_from_abbrev (char abbrev)
878 {
879 	int evt;
880 
881 	for (evt = 0; event_abbrevs[evt]; evt++) {
882 		if (event_abbrevs[evt] == abbrev)
883 			return event_names[evt];
884 	}
885 	return "???";
886 }
887 
888 static void
network_event(SoupMessage * msg,GSocketClientEvent event,GIOStream * connection,gpointer user_data)889 network_event (SoupMessage *msg, GSocketClientEvent event,
890 	       GIOStream *connection, gpointer user_data)
891 {
892 	const char **events = user_data;
893 
894 	debug_printf (2, "      %s\n", event_names[event]);
895 	soup_test_assert (**events == event_abbrevs[event],
896 			  "Unexpected event: %s (expected %s)",
897 			  event_names[event],
898 			  event_name_from_abbrev (**events));
899 
900 	if (**events == event_abbrevs[event]) {
901 		if (event == G_SOCKET_CLIENT_RESOLVING ||
902 		    event == G_SOCKET_CLIENT_RESOLVED) {
903 			soup_test_assert (connection == NULL,
904 					  "Unexpectedly got connection (%s) with '%s' event",
905 					  G_OBJECT_TYPE_NAME (connection),
906 					  event_names[event]);
907 		} else if (event < G_SOCKET_CLIENT_TLS_HANDSHAKING) {
908 			soup_test_assert (G_IS_SOCKET_CONNECTION (connection),
909 					  "Unexpectedly got %s with '%s' event",
910 					  G_OBJECT_TYPE_NAME (connection),
911 					  event_names[event]);
912 		} else if (event == G_SOCKET_CLIENT_TLS_HANDSHAKING ||
913 			   event == G_SOCKET_CLIENT_TLS_HANDSHAKED) {
914 			soup_test_assert (G_IS_TLS_CLIENT_CONNECTION (connection),
915 					  "Unexpectedly got %s with '%s' event",
916 					  G_OBJECT_TYPE_NAME (connection),
917 					  event_names[event]);
918 		} else if (event == G_SOCKET_CLIENT_COMPLETE) {
919 			/* See if the previous expected event was TLS_HANDSHAKED */
920 			if ((*events)[-1] == 'T') {
921 				soup_test_assert (G_IS_TLS_CLIENT_CONNECTION (connection),
922 						  "Unexpectedly got %s with '%s' event",
923 						  G_OBJECT_TYPE_NAME (connection),
924 						  event_names[event]);
925 			} else {
926 				soup_test_assert (G_IS_SOCKET_CONNECTION (connection),
927 						  "Unexpectedly got %s with '%s' event",
928 						  G_OBJECT_TYPE_NAME (connection),
929 						  event_names[event]);
930 			}
931 		}
932 	}
933 
934 	*events = *events + 1;
935 }
936 
937 static void
do_one_connection_event_test(SoupSession * session,const char * uri,const char * events)938 do_one_connection_event_test (SoupSession *session, const char *uri,
939 			      const char *events)
940 {
941 	SoupMessage *msg;
942 
943 	msg = soup_message_new ("GET", uri);
944 	g_signal_connect (msg, "network-event",
945 			  G_CALLBACK (network_event),
946 			  &events);
947 	soup_session_send_message (session, msg);
948 	soup_test_assert_message_status (msg, SOUP_STATUS_OK);
949 	while (*events) {
950 		soup_test_assert (!*events,
951 				  "Expected %s",
952 				  event_name_from_abbrev (*events));
953 		events++;
954 	}
955 
956 	g_object_unref (msg);
957 	soup_session_abort (session);
958 }
959 
960 static void
do_connection_event_test_for_session(SoupSession * session)961 do_connection_event_test_for_session (SoupSession *session)
962 {
963 	SoupURI *proxy_uri;
964 
965 	debug_printf (1, "    http\n");
966 	do_one_connection_event_test (session, HTTP_SERVER, "rRcCx");
967 
968 	if (tls_available) {
969 		debug_printf (1, "    https\n");
970 		do_one_connection_event_test (session, HTTPS_SERVER, "rRcCtTx");
971 	} else
972 		debug_printf (1, "    https -- SKIPPING\n");
973 
974 	proxy_uri = soup_uri_new (HTTP_PROXY);
975 	g_object_set (G_OBJECT (session),
976 		      SOUP_SESSION_PROXY_URI, proxy_uri,
977 		      NULL);
978 	soup_uri_free (proxy_uri);
979 
980 	debug_printf (1, "    http with proxy\n");
981 	do_one_connection_event_test (session, HTTP_SERVER, "rRcCx");
982 
983 	if (tls_available) {
984 		debug_printf (1, "    https with proxy\n");
985 		do_one_connection_event_test (session, HTTPS_SERVER, "rRcCpPtTx");
986 	} else
987 		debug_printf (1, "    https with proxy -- SKIPPING\n");
988 }
989 
990 static void
do_connection_event_test(void)991 do_connection_event_test (void)
992 {
993 	SoupSession *session;
994 
995 	SOUP_TEST_SKIP_IF_NO_APACHE;
996 
997 	debug_printf (1, "  Async session\n");
998 	session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
999 	do_connection_event_test_for_session (session);
1000 	soup_test_session_abort_unref (session);
1001 
1002 	debug_printf (1, "  Sync session\n");
1003 	session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL);
1004 	do_connection_event_test_for_session (session);
1005 	soup_test_session_abort_unref (session);
1006 }
1007 
1008 typedef struct {
1009         GMainLoop *loop;
1010         GIOStream *stream;
1011         GError *error;
1012         const char *events;
1013 } ConnectTestData;
1014 
1015 static void
connect_progress(SoupSession * session,GSocketClientEvent event,GIOStream * connection,ConnectTestData * data)1016 connect_progress (SoupSession *session, GSocketClientEvent event, GIOStream *connection, ConnectTestData *data)
1017 {
1018         soup_test_assert (*data->events == event_abbrevs[event],
1019                           "Unexpected event: %s (expected %s)",
1020                           event_names[event],
1021                           event_name_from_abbrev (*data->events));
1022         data->events = data->events + 1;
1023 }
1024 
1025 static void
connect_finished(SoupSession * session,GAsyncResult * result,ConnectTestData * data)1026 connect_finished (SoupSession *session, GAsyncResult *result, ConnectTestData *data)
1027 {
1028         data->stream = soup_session_connect_finish (session, result, &data->error);
1029         g_main_loop_quit (data->loop);
1030 }
1031 
1032 static void
do_one_connection_connect_test(SoupSession * session,SoupURI * uri,const char * response,const char * events)1033 do_one_connection_connect_test (SoupSession *session, SoupURI *uri, const char *response, const char *events)
1034 {
1035         ConnectTestData data = { NULL, NULL, NULL, events };
1036         static const char *request = "GET / HTTP/1.1\r\nHost: localhost\r\n\r\n";
1037         gsize bytes = 0;
1038         char buffer[128];
1039 
1040         data.loop = g_main_loop_new (NULL, FALSE);
1041         soup_session_connect_async (session, uri, NULL,
1042                                     (SoupSessionConnectProgressCallback)connect_progress,
1043                                     (GAsyncReadyCallback)connect_finished,
1044                                     &data);
1045         g_main_loop_run (data.loop);
1046 
1047         g_assert (G_IS_IO_STREAM (data.stream));
1048         g_assert_no_error (data.error);
1049         g_assert (g_output_stream_write_all (g_io_stream_get_output_stream (data.stream),
1050                                              request, strlen (request), &bytes, NULL, NULL));
1051         g_assert (g_input_stream_read_all (g_io_stream_get_input_stream (data.stream),
1052                                            buffer, sizeof (buffer), &bytes, NULL, NULL));
1053         buffer[strlen (response)] = '\0';
1054         g_assert_cmpstr (buffer, ==, response);
1055 
1056         while (*data.events) {
1057                 soup_test_assert (!*data.events,
1058                                   "Expected %s",
1059                                   event_name_from_abbrev (*data.events));
1060                 data.events++;
1061         }
1062 
1063         g_object_unref (data.stream);
1064         g_main_loop_unref (data.loop);
1065 }
1066 
1067 static void
do_one_connection_connect_fail_test(SoupSession * session,SoupURI * uri,GQuark domain,gint code,const char * events)1068 do_one_connection_connect_fail_test (SoupSession *session, SoupURI *uri, GQuark domain, gint code, const char *events)
1069 {
1070         ConnectTestData data = { NULL, NULL, NULL, events };
1071 
1072         data.loop = g_main_loop_new (NULL, FALSE);
1073         soup_session_connect_async (session, uri, NULL,
1074                                     (SoupSessionConnectProgressCallback)connect_progress,
1075                                     (GAsyncReadyCallback)connect_finished,
1076                                     &data);
1077         g_main_loop_run (data.loop);
1078         g_main_loop_unref (data.loop);
1079 
1080         g_assert (!data.stream);
1081         g_assert_error (data.error, domain, code);
1082         g_clear_error (&data.error);
1083 
1084         while (*data.events) {
1085                 soup_test_assert (!*data.events,
1086                                   "Expected %s",
1087                                   event_name_from_abbrev (*data.events));
1088                 data.events++;
1089         }
1090 }
1091 
1092 static void
do_connection_connect_test(void)1093 do_connection_connect_test (void)
1094 {
1095         SoupSession *session;
1096         SoupURI *http_uri;
1097         SoupURI *https_uri = NULL;
1098         SoupURI *ws_uri;
1099         SoupURI *wss_uri = NULL;
1100         SoupURI *file_uri;
1101         SoupURI *wrong_http_uri;
1102         SoupURI *proxy_uri;
1103 
1104         SOUP_TEST_SKIP_IF_NO_APACHE;
1105 
1106         session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
1107                                          SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
1108                                          NULL);
1109 
1110         debug_printf (1, "    http\n");
1111         http_uri = soup_uri_new (HTTP_SERVER);
1112         do_one_connection_connect_test (session, http_uri,
1113                                         "HTTP/1.1 200 OK", "rRcCx");
1114 
1115         if (tls_available) {
1116                 debug_printf (1, "    https\n");
1117                 https_uri = soup_uri_new (HTTPS_SERVER);
1118                 do_one_connection_connect_test (session, https_uri,
1119                                                 "HTTP/1.1 200 OK", "rRcCtTx");
1120         } else
1121                 debug_printf (1, "    https -- SKIPPING\n");
1122 
1123         debug_printf (1, "    ws\n");
1124         ws_uri = soup_uri_new (HTTP_SERVER);
1125         ws_uri->scheme = SOUP_URI_SCHEME_WS;
1126         do_one_connection_connect_test (session, ws_uri,
1127                                         "HTTP/1.1 200 OK", "rRcCx");
1128 
1129         if (tls_available) {
1130                 debug_printf (1, "    wss\n");
1131                 wss_uri = soup_uri_new (HTTPS_SERVER);
1132                 do_one_connection_connect_test (session, wss_uri,
1133                                                 "HTTP/1.1 200 OK", "rRcCtTx");
1134         } else
1135                 debug_printf (1, "    wss -- SKIPPING\n");
1136 
1137         debug_printf (1, "    file\n");
1138         file_uri = soup_uri_new ("file:///foo/bar");
1139         do_one_connection_connect_fail_test (session, file_uri,
1140                                              G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND,
1141                                              "r");
1142 
1143         debug_printf (1, "    wrong http (invalid port)\n");
1144         wrong_http_uri = soup_uri_new (HTTP_SERVER);
1145         wrong_http_uri->port = 1234;
1146         do_one_connection_connect_fail_test (session, wrong_http_uri,
1147                                              G_IO_ERROR, G_IO_ERROR_CONNECTION_REFUSED,
1148                                              "rRcr"); /* FIXME: why r again? GLib bug? */
1149 
1150         proxy_uri = soup_uri_new (HTTP_PROXY);
1151         g_object_set (G_OBJECT (session),
1152                       SOUP_SESSION_PROXY_URI, proxy_uri,
1153                       NULL);
1154 
1155         debug_printf (1, "    http with proxy\n");
1156         do_one_connection_connect_test (session, http_uri,
1157                                         "HTTP/1.1 403 Forbidden", "rRcCx");
1158 
1159         if (tls_available) {
1160                 debug_printf (1, "    https with proxy\n");
1161                 do_one_connection_connect_test (session, https_uri,
1162                                                 "HTTP/1.1 200 OK", "rRcCpPtTx");
1163         } else
1164                 debug_printf (1, "    https with proxy -- SKIPPING\n");
1165 
1166         debug_printf (1, "    ws with proxy\n");
1167         do_one_connection_connect_test (session, ws_uri,
1168                                         "HTTP/1.1 403 Forbidden", "rRcCx");
1169 
1170         if (tls_available) {
1171                 debug_printf (1, "    wss with proxy\n");
1172                 do_one_connection_connect_test (session, wss_uri,
1173                                                 "HTTP/1.1 200 OK", "rRcCpPtTx");
1174         } else
1175                 debug_printf (1, "    wss with proxy -- SKIPPING\n");
1176 
1177         soup_uri_free (http_uri);
1178         if (https_uri)
1179                 soup_uri_free (https_uri);
1180         soup_uri_free (ws_uri);
1181         if (wss_uri)
1182                 soup_uri_free (wss_uri);
1183         soup_uri_free (file_uri);
1184         soup_uri_free (wrong_http_uri);
1185         soup_uri_free (proxy_uri);
1186 
1187         soup_test_session_abort_unref (session);
1188 }
1189 
1190 int
main(int argc,char ** argv)1191 main (int argc, char **argv)
1192 {
1193 	int ret;
1194 
1195 	test_init (argc, argv, NULL);
1196 	apache_init ();
1197 
1198 	server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD);
1199 	soup_server_add_handler (server, NULL, server_callback, "http", NULL);
1200 	base_uri = soup_test_server_get_uri (server, "http", NULL);
1201 
1202 	g_test_add_func ("/connection/content-length-framing", do_content_length_framing_test);
1203 	g_test_add_func ("/connection/persistent-connection-timeout", do_persistent_connection_timeout_test);
1204 	g_test_add_func ("/connection/persistent-connection-timeout-with-cancellable",
1205 			 do_persistent_connection_timeout_test_with_cancellation);
1206 	g_test_add_func ("/connection/max-conns", do_max_conns_test);
1207 	g_test_add_func ("/connection/non-persistent", do_non_persistent_connection_test);
1208 	g_test_add_func ("/connection/non-idempotent", do_non_idempotent_connection_test);
1209 	g_test_add_func ("/connection/state", do_connection_state_test);
1210 	g_test_add_func ("/connection/event", do_connection_event_test);
1211 	g_test_add_func ("/connection/connect", do_connection_connect_test);
1212 
1213 	ret = g_test_run ();
1214 
1215 	soup_uri_free (base_uri);
1216 	soup_test_server_quit_unref (server);
1217 
1218 	test_cleanup ();
1219 	return ret;
1220 }
1221