1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright 2007-2012 Red Hat, Inc.
8 #define LIBSOUP_USE_UNSTABLE_REQUEST_API
9 #include <libsoup/soup.h>
10 #include <libsoup/soup-requester.h>
12 #include "test-utils.h"
14 SoupServer *server, *ssl_server;
15 SoupURI *base_uri, *ssl_base_uri;
18 auth_callback (SoupAuthDomain *auth_domain, SoupMessage *msg,
19 const char *username, const char *password, gpointer data)
21 return !strcmp (username, "user") && !strcmp (password, "password");
25 timeout_finish_message (gpointer msg)
27 SoupServer *server = g_object_get_data (G_OBJECT (msg), "server");
29 soup_server_unpause_message (server, msg);
34 server_callback (SoupServer *server, SoupMessage *msg,
35 const char *path, GHashTable *query,
36 SoupClientContext *context, gpointer data)
38 SoupURI *uri = soup_message_get_uri (msg);
39 const char *server_protocol = data;
41 soup_message_headers_append (msg->response_headers,
42 "X-Handled-By", "server_callback");
44 if (!strcmp (path, "*")) {
45 debug_printf (1, " default server_callback got request for '*'!\n");
47 soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
51 if (msg->method != SOUP_METHOD_GET && msg->method != SOUP_METHOD_POST) {
52 soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
56 if (!strcmp (path, "/redirect")) {
57 soup_message_set_redirect (msg, SOUP_STATUS_FOUND, "/");
61 if (!strcmp (path, "/alias-redirect")) {
62 SoupURI *redirect_uri;
63 char *redirect_string;
64 const char *redirect_protocol;
66 redirect_protocol = soup_message_headers_get_one (msg->request_headers, "X-Redirect-Protocol");
68 redirect_uri = soup_uri_copy (uri);
69 soup_uri_set_scheme (redirect_uri, "foo");
70 if (!g_strcmp0 (redirect_protocol, "https"))
71 soup_uri_set_port (redirect_uri, ssl_base_uri->port);
73 soup_uri_set_port (redirect_uri, base_uri->port);
74 soup_uri_set_path (redirect_uri, "/alias-redirected");
75 redirect_string = soup_uri_to_string (redirect_uri, FALSE);
77 soup_message_set_redirect (msg, SOUP_STATUS_FOUND, redirect_string);
78 g_free (redirect_string);
79 soup_uri_free (redirect_uri);
81 } else if (!strcmp (path, "/alias-redirected")) {
82 soup_message_set_status (msg, SOUP_STATUS_OK);
83 soup_message_headers_append (msg->response_headers,
84 "X-Redirected-Protocol",
89 if (!strcmp (path, "/slow")) {
90 soup_server_pause_message (server, msg);
91 g_object_set_data (G_OBJECT (msg), "server", server);
92 soup_add_timeout (soup_server_get_async_context (server),
93 1000, timeout_finish_message, msg);
96 soup_message_set_status (msg, SOUP_STATUS_OK);
97 if (!strcmp (uri->host, "foo")) {
98 soup_message_set_response (msg, "text/plain",
99 SOUP_MEMORY_STATIC, "foo-index", 9);
102 soup_message_set_response (msg, "text/plain",
103 SOUP_MEMORY_STATIC, "index", 5);
109 server_star_callback (SoupServer *server, SoupMessage *msg,
110 const char *path, GHashTable *query,
111 SoupClientContext *context, gpointer data)
113 soup_message_headers_append (msg->response_headers,
114 "X-Handled-By", "star_callback");
116 if (strcmp (path, "*") != 0) {
117 debug_printf (1, " server_star_callback got request for '%s'!\n", path);
119 soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
123 if (msg->method != SOUP_METHOD_OPTIONS) {
124 soup_message_set_status (msg, SOUP_STATUS_METHOD_NOT_ALLOWED);
128 soup_message_set_status (msg, SOUP_STATUS_OK);
131 /* Host header handling: client must be able to override the default
132 * value, server must be able to recognize different Host values.
138 SoupSession *session;
139 SoupMessage *one, *two;
141 debug_printf (1, "Host handling\n");
143 session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL);
145 one = soup_message_new_from_uri ("GET", base_uri);
146 two = soup_message_new_from_uri ("GET", base_uri);
147 soup_message_headers_replace (two->request_headers, "Host", "foo");
149 soup_session_send_message (session, one);
150 soup_session_send_message (session, two);
152 soup_test_session_abort_unref (session);
154 if (!SOUP_STATUS_IS_SUCCESSFUL (one->status_code)) {
155 debug_printf (1, " Message 1 failed: %d %s\n",
156 one->status_code, one->reason_phrase);
158 } else if (strcmp (one->response_body->data, "index") != 0) {
159 debug_printf (1, " Unexpected response to message 1: '%s'\n",
160 one->response_body->data);
163 g_object_unref (one);
165 if (!SOUP_STATUS_IS_SUCCESSFUL (two->status_code)) {
166 debug_printf (1, " Message 2 failed: %d %s\n",
167 two->status_code, two->reason_phrase);
169 } else if (strcmp (two->response_body->data, "foo-index") != 0) {
170 debug_printf (1, " Unexpected response to message 2: '%s'\n",
171 two->response_body->data);
174 g_object_unref (two);
177 /* Dropping the application's ref on the session from a callback
178 * should not cause the session to be freed at an incorrect time.
179 * (This test will crash if it fails.) #533473
182 cu_one_completed (SoupSession *session, SoupMessage *msg, gpointer loop)
184 debug_printf (2, " Message 1 completed\n");
185 if (msg->status_code != SOUP_STATUS_CANT_CONNECT) {
186 debug_printf (1, " Unexpected status on Message 1: %d %s\n",
187 msg->status_code, msg->reason_phrase);
190 g_object_unref (session);
194 cu_idle_quit (gpointer loop)
196 g_main_loop_quit (loop);
201 cu_two_completed (SoupSession *session, SoupMessage *msg, gpointer loop)
203 debug_printf (2, " Message 2 completed\n");
204 if (msg->status_code != SOUP_STATUS_CANT_CONNECT) {
205 debug_printf (1, " Unexpected status on Message 2: %d %s\n",
206 msg->status_code, msg->reason_phrase);
209 g_idle_add (cu_idle_quit, loop);
213 do_callback_unref_test (void)
215 SoupServer *bad_server;
217 SoupSession *session;
218 SoupMessage *one, *two;
222 debug_printf (1, "\nCallback unref handling (msg api)\n");
224 /* Get a guaranteed-bad URI */
225 addr = soup_address_new ("127.0.0.1", SOUP_ADDRESS_ANY_PORT);
226 soup_address_resolve_sync (addr, NULL);
227 bad_server = soup_server_new (SOUP_SERVER_INTERFACE, addr,
229 g_object_unref (addr);
231 bad_uri = g_strdup_printf ("http://127.0.0.1:%u/",
232 soup_server_get_port (bad_server));
233 g_object_unref (bad_server);
235 session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
236 g_object_add_weak_pointer (G_OBJECT (session), (gpointer *)&session);
238 loop = g_main_loop_new (NULL, TRUE);
240 one = soup_message_new ("GET", bad_uri);
241 g_object_add_weak_pointer (G_OBJECT (one), (gpointer *)&one);
242 two = soup_message_new ("GET", bad_uri);
243 g_object_add_weak_pointer (G_OBJECT (two), (gpointer *)&two);
246 soup_session_queue_message (session, one, cu_one_completed, loop);
247 soup_session_queue_message (session, two, cu_two_completed, loop);
249 g_main_loop_run (loop);
250 g_main_loop_unref (loop);
253 g_object_remove_weak_pointer (G_OBJECT (session), (gpointer *)&session);
254 debug_printf (1, " Session not destroyed?\n");
256 g_object_unref (session);
259 g_object_remove_weak_pointer (G_OBJECT (one), (gpointer *)&one);
260 debug_printf (1, " Message 1 not destroyed?\n");
262 g_object_unref (one);
265 g_object_remove_weak_pointer (G_OBJECT (two), (gpointer *)&two);
266 debug_printf (1, " Message 2 not destroyed?\n");
268 g_object_unref (two);
271 /* Otherwise, if we haven't crashed, we're ok. */
275 cur_one_completed (GObject *source, GAsyncResult *result, gpointer session)
277 SoupRequest *one = SOUP_REQUEST (source);
278 GError *error = NULL;
280 debug_printf (2, " Request 1 completed\n");
281 if (soup_request_send_finish (one, result, &error)) {
282 debug_printf (1, " Request 1 succeeded?\n");
284 } else if (!g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_CANT_CONNECT)) {
285 debug_printf (1, " Unexpected error on Request 1: %s\n",
289 g_clear_error (&error);
291 g_object_unref (session);
295 cur_idle_quit (gpointer loop)
297 g_main_loop_quit (loop);
302 cur_two_completed (GObject *source, GAsyncResult *result, gpointer loop)
304 SoupRequest *two = SOUP_REQUEST (source);
305 GError *error = NULL;
307 debug_printf (2, " Request 2 completed\n");
308 if (soup_request_send_finish (two, result, &error)) {
309 debug_printf (1, " Request 2 succeeded?\n");
311 } else if (!g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_CANT_CONNECT)) {
312 debug_printf (1, " Unexpected error on Request 2: %s\n",
316 g_clear_error (&error);
318 g_idle_add (cur_idle_quit, loop);
322 do_callback_unref_req_test (void)
324 SoupServer *bad_server;
326 SoupSession *session;
327 SoupRequester *requester;
328 SoupRequest *one, *two;
332 debug_printf (1, "\nCallback unref handling (request api)\n");
334 /* Get a guaranteed-bad URI */
335 addr = soup_address_new ("127.0.0.1", SOUP_ADDRESS_ANY_PORT);
336 soup_address_resolve_sync (addr, NULL);
337 bad_server = soup_server_new (SOUP_SERVER_INTERFACE, addr,
339 g_object_unref (addr);
341 bad_uri = g_strdup_printf ("http://127.0.0.1:%u/",
342 soup_server_get_port (bad_server));
343 g_object_unref (bad_server);
345 session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
346 SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_REQUESTER,
347 SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
349 g_object_add_weak_pointer (G_OBJECT (session), (gpointer *)&session);
351 requester = (SoupRequester *)soup_session_get_feature (session, SOUP_TYPE_REQUESTER);
353 loop = g_main_loop_new (NULL, TRUE);
355 one = soup_requester_request (requester, bad_uri, NULL);
356 g_object_add_weak_pointer (G_OBJECT (one), (gpointer *)&one);
357 two = soup_requester_request (requester, bad_uri, NULL);
358 g_object_add_weak_pointer (G_OBJECT (two), (gpointer *)&two);
361 soup_request_send_async (one, NULL, cur_one_completed, session);
362 g_object_unref (one);
363 soup_request_send_async (two, NULL, cur_two_completed, loop);
364 g_object_unref (two);
366 g_main_loop_run (loop);
367 g_main_loop_unref (loop);
370 g_object_remove_weak_pointer (G_OBJECT (session), (gpointer *)&session);
371 debug_printf (1, " Session not destroyed?\n");
373 g_object_unref (session);
376 g_object_remove_weak_pointer (G_OBJECT (one), (gpointer *)&one);
377 debug_printf (1, " Request 1 not destroyed?\n");
379 g_object_unref (one);
382 g_object_remove_weak_pointer (G_OBJECT (two), (gpointer *)&two);
383 debug_printf (1, " Request 2 not destroyed?\n");
385 g_object_unref (two);
388 /* Otherwise, if we haven't crashed, we're ok. */
391 /* SoupSession should clean up all signal handlers on a message after
392 * it is finished, allowing the message to be reused if desired.
396 ensure_no_signal_handlers (SoupMessage *msg, guint *signal_ids, guint n_signal_ids)
400 for (i = 0; i < n_signal_ids; i++) {
401 if (g_signal_handler_find (msg, G_SIGNAL_MATCH_ID, signal_ids[i],
402 0, NULL, NULL, NULL)) {
403 debug_printf (1, " Message has handler for '%s'\n",
404 g_signal_name (signal_ids[i]));
411 reuse_test_authenticate (SoupSession *session, SoupMessage *msg,
412 SoupAuth *auth, gboolean retrying)
414 /* Get it wrong the first time, then succeed */
416 soup_auth_authenticate (auth, "user", "wrong password");
418 soup_auth_authenticate (auth, "user", "password");
422 do_msg_reuse_test (void)
424 SoupSession *session;
427 guint *signal_ids, n_signal_ids;
429 debug_printf (1, "\nSoupMessage reuse\n");
431 signal_ids = g_signal_list_ids (SOUP_TYPE_MESSAGE, &n_signal_ids);
433 session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
434 g_signal_connect (session, "authenticate",
435 G_CALLBACK (reuse_test_authenticate), NULL);
437 debug_printf (1, " First message\n");
438 msg = soup_message_new_from_uri ("GET", base_uri);
439 soup_session_send_message (session, msg);
440 ensure_no_signal_handlers (msg, signal_ids, n_signal_ids);
442 debug_printf (1, " Redirect message\n");
443 uri = soup_uri_new_with_base (base_uri, "/redirect");
444 soup_message_set_uri (msg, uri);
446 soup_session_send_message (session, msg);
447 if (!soup_uri_equal (soup_message_get_uri (msg), base_uri)) {
448 debug_printf (1, " Message did not get redirected!\n");
451 ensure_no_signal_handlers (msg, signal_ids, n_signal_ids);
453 debug_printf (1, " Auth message\n");
454 uri = soup_uri_new_with_base (base_uri, "/auth");
455 soup_message_set_uri (msg, uri);
457 soup_session_send_message (session, msg);
458 if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
459 debug_printf (1, " Message did not get authenticated!\n");
462 ensure_no_signal_handlers (msg, signal_ids, n_signal_ids);
464 /* One last try to make sure the auth stuff got cleaned up */
465 debug_printf (1, " Last message\n");
466 soup_message_set_uri (msg, base_uri);
467 soup_session_send_message (session, msg);
468 ensure_no_signal_handlers (msg, signal_ids, n_signal_ids);
470 soup_test_session_abort_unref (session);
471 g_object_unref (msg);
475 /* Server handlers for "*" work but are separate from handlers for
476 * all other URIs. #590751
481 SoupSession *session;
484 const char *handled_by;
486 debug_printf (1, "\nOPTIONS *\n");
488 session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL);
489 star_uri = soup_uri_copy (base_uri);
490 soup_uri_set_path (star_uri, "*");
492 debug_printf (1, " Testing with no handler\n");
493 msg = soup_message_new_from_uri ("OPTIONS", star_uri);
494 soup_session_send_message (session, msg);
496 if (msg->status_code != SOUP_STATUS_NOT_FOUND) {
497 debug_printf (1, " Unexpected response: %d %s\n",
498 msg->status_code, msg->reason_phrase);
501 handled_by = soup_message_headers_get_one (msg->response_headers,
504 /* Should have been rejected by SoupServer directly */
505 debug_printf (1, " Message reached handler '%s'\n",
509 g_object_unref (msg);
511 soup_server_add_handler (server, "*", server_star_callback, NULL, NULL);
513 debug_printf (1, " Testing with handler\n");
514 msg = soup_message_new_from_uri ("OPTIONS", star_uri);
515 soup_session_send_message (session, msg);
517 if (msg->status_code != SOUP_STATUS_OK) {
518 debug_printf (1, " Unexpected response: %d %s\n",
519 msg->status_code, msg->reason_phrase);
522 handled_by = soup_message_headers_get_one (msg->response_headers,
525 debug_printf (1, " Message did not reach handler!\n");
527 } else if (strcmp (handled_by, "star_callback") != 0) {
528 debug_printf (1, " Message reached incorrect handler '%s'\n",
532 g_object_unref (msg);
534 soup_test_session_abort_unref (session);
535 soup_uri_free (star_uri);
538 /* Handle unexpectedly-early aborts. #596074, #618641 */
540 ea_msg_completed_one (SoupSession *session, SoupMessage *msg, gpointer loop)
542 debug_printf (2, " Message 1 completed\n");
543 if (msg->status_code != SOUP_STATUS_CANCELLED) {
544 debug_printf (1, " Unexpected status on Message 1: %d %s\n",
545 msg->status_code, msg->reason_phrase);
548 g_main_loop_quit (loop);
552 ea_abort_session (gpointer session)
554 soup_session_abort (session);
559 ea_connection_state_changed (GObject *conn, GParamSpec *pspec, gpointer session)
561 SoupConnectionState state;
563 g_object_get (conn, "state", &state, NULL);
564 if (state == SOUP_CONNECTION_CONNECTING) {
565 g_idle_add_full (G_PRIORITY_HIGH,
568 g_signal_handlers_disconnect_by_func (conn, ea_connection_state_changed, session);
573 ea_connection_created (SoupSession *session, GObject *conn, gpointer user_data)
575 g_signal_connect (conn, "notify::state",
576 G_CALLBACK (ea_connection_state_changed), session);
577 g_signal_handlers_disconnect_by_func (session, ea_connection_created, user_data);
581 ea_request_started (SoupSession *session, SoupMessage *msg, SoupSocket *socket, gpointer user_data)
583 soup_session_cancel_message (session, msg, SOUP_STATUS_CANCELLED);
587 do_early_abort_test (void)
589 SoupSession *session;
591 GMainContext *context;
594 debug_printf (1, "\nAbort with pending connection (msg api)\n");
596 session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
597 msg = soup_message_new_from_uri ("GET", base_uri);
599 context = g_main_context_default ();
600 loop = g_main_loop_new (context, TRUE);
601 soup_session_queue_message (session, msg, ea_msg_completed_one, loop);
602 g_main_context_iteration (context, FALSE);
604 soup_session_abort (session);
605 while (g_main_context_pending (context))
606 g_main_context_iteration (context, FALSE);
607 g_main_loop_unref (loop);
608 soup_test_session_abort_unref (session);
610 session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
611 msg = soup_message_new_from_uri ("GET", base_uri);
613 g_signal_connect (session, "connection-created",
614 G_CALLBACK (ea_connection_created), NULL);
615 soup_session_send_message (session, msg);
616 debug_printf (2, " Message 2 completed\n");
618 if (msg->status_code != SOUP_STATUS_CANCELLED) {
619 debug_printf (1, " Unexpected response: %d %s\n",
620 msg->status_code, msg->reason_phrase);
623 g_object_unref (msg);
625 while (g_main_context_pending (context))
626 g_main_context_iteration (context, FALSE);
628 soup_test_session_abort_unref (session);
630 session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
631 msg = soup_message_new_from_uri ("GET", base_uri);
633 g_signal_connect (session, "request-started",
634 G_CALLBACK (ea_request_started), NULL);
635 soup_session_send_message (session, msg);
636 debug_printf (2, " Message 3 completed\n");
638 if (msg->status_code != SOUP_STATUS_CANCELLED) {
639 debug_printf (1, " Unexpected response: %d %s\n",
640 msg->status_code, msg->reason_phrase);
643 g_object_unref (msg);
645 while (g_main_context_pending (context))
646 g_main_context_iteration (context, FALSE);
648 soup_test_session_abort_unref (session);
652 ear_one_completed (GObject *source, GAsyncResult *result, gpointer user_data)
654 GError *error = NULL;
656 debug_printf (2, " Request 1 completed\n");
657 if (soup_request_send_finish (SOUP_REQUEST (source), result, &error)) {
658 debug_printf (1, " Request 1 succeeded?\n");
660 } else if (!g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_CANCELLED)) {
661 debug_printf (1, " Unexpected error on Request 1: %s\n",
665 g_clear_error (&error);
669 ear_two_completed (GObject *source, GAsyncResult *result, gpointer loop)
671 GError *error = NULL;
673 debug_printf (2, " Request 2 completed\n");
674 if (soup_request_send_finish (SOUP_REQUEST (source), result, &error)) {
675 debug_printf (1, " Request 2 succeeded?\n");
677 } else if (!g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_CANCELLED)) {
678 debug_printf (1, " Unexpected error on Request 2: %s\n",
682 g_clear_error (&error);
684 g_main_loop_quit (loop);
688 ear_three_completed (GObject *source, GAsyncResult *result, gpointer loop)
690 GError *error = NULL;
692 debug_printf (2, " Request 3 completed\n");
693 if (soup_request_send_finish (SOUP_REQUEST (source), result, &error)) {
694 debug_printf (1, " Request 3 succeeded?\n");
696 } else if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
697 debug_printf (1, " Unexpected error on Request 3: %s\n",
701 g_clear_error (&error);
703 g_main_loop_quit (loop);
707 ear_request_started (SoupSession *session, SoupMessage *msg,
708 SoupSocket *socket, gpointer cancellable)
710 g_cancellable_cancel (cancellable);
714 do_early_abort_req_test (void)
716 SoupSession *session;
717 SoupRequester *requester;
719 GMainContext *context;
721 GCancellable *cancellable;
723 debug_printf (1, "\nAbort with pending connection (request api)\n");
725 session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
726 SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_REQUESTER,
727 SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
729 requester = (SoupRequester *)soup_session_get_feature (session, SOUP_TYPE_REQUESTER);
730 req = soup_requester_request_uri (requester, base_uri, NULL);
732 context = g_main_context_default ();
733 loop = g_main_loop_new (context, TRUE);
734 soup_request_send_async (req, NULL, ear_one_completed, NULL);
735 g_object_unref (req);
736 g_main_context_iteration (context, FALSE);
738 soup_session_abort (session);
739 while (g_main_context_pending (context))
740 g_main_context_iteration (context, FALSE);
741 soup_test_session_abort_unref (session);
743 session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
744 SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_REQUESTER,
745 SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
747 requester = (SoupRequester *)soup_session_get_feature (session, SOUP_TYPE_REQUESTER);
748 req = soup_requester_request_uri (requester, base_uri, NULL);
750 g_signal_connect (session, "connection-created",
751 G_CALLBACK (ea_connection_created), NULL);
752 soup_request_send_async (req, NULL, ear_two_completed, loop);
753 g_main_loop_run (loop);
754 g_object_unref (req);
756 while (g_main_context_pending (context))
757 g_main_context_iteration (context, FALSE);
759 soup_test_session_abort_unref (session);
761 session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
762 SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_REQUESTER,
763 SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
765 requester = (SoupRequester *)soup_session_get_feature (session, SOUP_TYPE_REQUESTER);
766 req = soup_requester_request_uri (requester, base_uri, NULL);
768 cancellable = g_cancellable_new ();
769 g_signal_connect (session, "request-started",
770 G_CALLBACK (ear_request_started), cancellable);
771 soup_request_send_async (req, cancellable, ear_three_completed, loop);
772 g_main_loop_run (loop);
773 g_object_unref (req);
774 g_object_unref (cancellable);
776 while (g_main_context_pending (context))
777 g_main_context_iteration (context, FALSE);
779 soup_test_session_abort_unref (session);
780 g_main_loop_unref (loop);
784 do_one_accept_language_test (const char *language, const char *expected_header)
786 SoupSession *session;
790 debug_printf (1, " LANGUAGE=%s\n", language);
791 g_setenv ("LANGUAGE", language, TRUE);
792 session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC,
793 SOUP_SESSION_ACCEPT_LANGUAGE_AUTO, TRUE,
795 msg = soup_message_new_from_uri ("GET", base_uri);
796 soup_session_send_message (session, msg);
797 soup_test_session_abort_unref (session);
799 if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
800 debug_printf (1, " Message failed? %d %s\n",
801 msg->status_code, msg->reason_phrase);
804 val = soup_message_headers_get_list (msg->request_headers,
807 debug_printf (1, " No Accept-Language set!\n");
809 } else if (strcmp (val, expected_header) != 0) {
810 debug_printf (1, " Wrong Accept-Language: expected '%s', got '%s'\n",
811 expected_header, val);
815 g_object_unref (msg);
819 do_accept_language_test (void)
821 const char *orig_language;
823 debug_printf (1, "\nAutomatic Accept-Language processing\n");
825 orig_language = g_getenv ("LANGUAGE");
826 do_one_accept_language_test ("C", "en");
827 do_one_accept_language_test ("fr_FR", "fr-fr, fr;q=0.9");
828 do_one_accept_language_test ("fr_FR:de:en_US", "fr-fr, fr;q=0.9, de;q=0.8, en-us;q=0.7, en;q=0.6");
831 g_setenv ("LANGUAGE", orig_language, TRUE);
833 g_unsetenv ("LANGUAGE");
837 cancel_message_timeout (gpointer msg)
839 SoupSession *session = g_object_get_data (G_OBJECT (msg), "session");
841 soup_session_cancel_message (session, msg, SOUP_STATUS_CANCELLED);
842 g_object_unref (msg);
843 g_object_unref (session);
848 cancel_message_thread (gpointer msg)
850 SoupSession *session = g_object_get_data (G_OBJECT (msg), "session");
852 g_usleep (100000); /* .1s */
853 soup_session_cancel_message (session, msg, SOUP_STATUS_CANCELLED);
854 g_object_unref (msg);
855 g_object_unref (session);
860 set_done (SoupSession *session, SoupMessage *msg, gpointer user_data)
862 gboolean *done = user_data;
868 do_cancel_while_reading_test_for_session (SoupSession *session)
871 GThread *thread = NULL;
873 gboolean done = FALSE;
875 uri = soup_uri_new_with_base (base_uri, "/slow");
876 msg = soup_message_new_from_uri ("GET", uri);
879 g_object_set_data (G_OBJECT (msg), "session", session);
881 g_object_ref (session);
882 if (SOUP_IS_SESSION_ASYNC (session))
883 g_timeout_add (100, cancel_message_timeout, msg);
885 thread = g_thread_new ("cancel_message_thread", cancel_message_thread, msg);
887 /* We intentionally don't use soup_session_send_message() here,
888 * because it holds an extra ref on the SoupMessageQueueItem
889 * relative to soup_session_queue_message().
892 soup_session_queue_message (session, msg, set_done, &done);
894 g_main_context_iteration (NULL, TRUE);
896 if (msg->status_code != SOUP_STATUS_CANCELLED) {
897 debug_printf (1, " FAILED: %d %s (expected Cancelled)\n",
898 msg->status_code, msg->reason_phrase);
901 g_object_unref (msg);
904 g_thread_join (thread);
908 do_cancel_while_reading_test (void)
910 SoupSession *session;
912 debug_printf (1, "\nCancelling message while reading response (msg api)\n");
914 debug_printf (1, " Async session\n");
915 session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
916 do_cancel_while_reading_test_for_session (session);
917 soup_test_session_abort_unref (session);
919 debug_printf (1, " Sync session\n");
920 session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL);
921 do_cancel_while_reading_test_for_session (session);
922 soup_test_session_abort_unref (session);
926 cancel_request_timeout (gpointer cancellable)
928 g_cancellable_cancel (cancellable);
933 cancel_request_thread (gpointer cancellable)
935 g_usleep (100000); /* .1s */
936 g_cancellable_cancel (cancellable);
941 cancel_request_finished (GObject *source, GAsyncResult *result, gpointer loop)
943 GError *error = NULL;
945 if (soup_request_send_finish (SOUP_REQUEST (source), result, &error)) {
946 debug_printf (1, " Request succeeded?\n");
948 } else if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
949 debug_printf (1, " Unexpected error: %s\n",
953 g_clear_error (&error);
955 g_main_loop_quit (loop);
959 do_cancel_while_reading_req_test_for_session (SoupRequester *requester)
963 GCancellable *cancellable;
965 uri = soup_uri_new_with_base (base_uri, "/slow");
966 req = soup_requester_request_uri (requester, uri, NULL);
969 cancellable = g_cancellable_new ();
971 if (SOUP_IS_SESSION_ASYNC (soup_request_get_session (req))) {
974 loop = g_main_loop_new (NULL, FALSE);
975 g_timeout_add (100, cancel_request_timeout, cancellable);
976 soup_request_send_async (req, cancellable,
977 cancel_request_finished, loop);
978 g_main_loop_run (loop);
979 g_main_loop_unref (loop);
982 GError *error = NULL;
984 thread = g_thread_new ("cancel_request_thread", cancel_request_thread, cancellable);
985 soup_request_send (req, cancellable, &error);
987 debug_printf (1, " Request succeeded?\n");
989 } else if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
990 debug_printf (1, " Unexpected error: %s\n",
994 g_thread_unref (thread);
997 g_object_unref (req);
998 g_object_unref (cancellable);
1002 do_cancel_while_reading_req_test (void)
1004 SoupSession *session;
1005 SoupRequester *requester;
1007 debug_printf (1, "\nCancelling message while reading response (request api)\n");
1009 debug_printf (1, " Async session\n");
1010 session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
1011 SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_REQUESTER,
1012 SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
1014 requester = (SoupRequester *)soup_session_get_feature (session, SOUP_TYPE_REQUESTER);
1015 do_cancel_while_reading_req_test_for_session (requester);
1016 soup_test_session_abort_unref (session);
1018 debug_printf (1, " Sync session\n");
1019 session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC,
1020 SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_REQUESTER,
1022 requester = (SoupRequester *)soup_session_get_feature (session, SOUP_TYPE_REQUESTER);
1023 do_cancel_while_reading_req_test_for_session (requester);
1024 soup_test_session_abort_unref (session);
1028 do_aliases_test_for_session (SoupSession *session,
1029 const char *redirect_protocol)
1033 const char *redirected_protocol;
1035 uri = soup_uri_new_with_base (base_uri, "/alias-redirect");
1036 msg = soup_message_new_from_uri ("GET", uri);
1037 if (redirect_protocol)
1038 soup_message_headers_append (msg->request_headers, "X-Redirect-Protocol", redirect_protocol);
1039 soup_uri_free (uri);
1040 soup_session_send_message (session, msg);
1042 redirected_protocol = soup_message_headers_get_one (msg->response_headers, "X-Redirected-Protocol");
1044 if (g_strcmp0 (redirect_protocol, redirected_protocol)) {
1045 debug_printf (1, " redirect went to %s, should have gone to %s!\n",
1046 redirected_protocol ? redirected_protocol : "(none)",
1047 redirect_protocol ? redirect_protocol : "(none)");
1049 } else if (redirect_protocol && !SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
1050 debug_printf (1, " msg failed? (%d %s)\n",
1051 msg->status_code, msg->reason_phrase);
1053 } else if (!redirect_protocol && SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
1054 debug_printf (1, " msg succeeded? (%d %s)\n",
1055 msg->status_code, msg->reason_phrase);
1059 g_object_unref (msg);
1063 do_aliases_test (void)
1065 SoupSession *session;
1066 char *aliases[] = { "foo", NULL };
1068 debug_printf (1, "\nhttp-aliases / https-aliases\n");
1070 debug_printf (1, " Default behavior\n");
1071 session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
1072 do_aliases_test_for_session (session, "http");
1073 soup_test_session_abort_unref (session);
1075 debug_printf (1, " foo-means-https\n");
1076 session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
1077 SOUP_SESSION_HTTPS_ALIASES, aliases,
1079 do_aliases_test_for_session (session, "https");
1080 soup_test_session_abort_unref (session);
1082 debug_printf (1, " foo-means-nothing\n");
1083 session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
1084 SOUP_SESSION_HTTP_ALIASES, NULL,
1086 do_aliases_test_for_session (session, NULL);
1087 soup_test_session_abort_unref (session);
1091 do_dot_dot_test (void)
1093 SoupSession *session;
1097 debug_printf (1, "\n'..' smuggling test\n");
1099 session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL);
1101 uri = soup_uri_new_with_base (base_uri, "/..%2ftest");
1102 msg = soup_message_new_from_uri ("GET", uri);
1103 soup_uri_free (uri);
1105 soup_session_send_message (session, msg);
1107 if (msg->status_code != SOUP_STATUS_BAD_REQUEST) {
1108 debug_printf (1, " FAILED: %d %s (expected Bad Request)\n",
1109 msg->status_code, msg->reason_phrase);
1112 g_object_unref (msg);
1114 soup_test_session_abort_unref (session);
1120 SoupServer *ipv6_server;
1122 SoupAddress *ipv6_addr;
1123 SoupSession *session;
1126 debug_printf (1, "\nIPv6 server test\n");
1128 ipv6_addr = soup_address_new ("::1", SOUP_ADDRESS_ANY_PORT);
1129 soup_address_resolve_sync (ipv6_addr, NULL);
1130 ipv6_server = soup_server_new (SOUP_SERVER_INTERFACE, ipv6_addr,
1132 g_object_unref (ipv6_addr);
1133 soup_server_add_handler (ipv6_server, NULL, server_callback, NULL, NULL);
1134 soup_server_run_async (ipv6_server);
1136 ipv6_uri = soup_uri_new ("http://[::1]/");
1137 soup_uri_set_port (ipv6_uri, soup_server_get_port (ipv6_server));
1139 session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
1141 debug_printf (1, " HTTP/1.1\n");
1142 msg = soup_message_new_from_uri ("GET", ipv6_uri);
1143 soup_session_send_message (session, msg);
1144 if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
1145 debug_printf (1, " request failed: %d %s\n",
1146 msg->status_code, msg->reason_phrase);
1149 g_object_unref (msg);
1151 debug_printf (1, " HTTP/1.0\n");
1152 msg = soup_message_new_from_uri ("GET", ipv6_uri);
1153 soup_message_set_http_version (msg, SOUP_HTTP_1_0);
1154 soup_session_send_message (session, msg);
1155 if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
1156 debug_printf (1, " request failed: %d %s\n",
1157 msg->status_code, msg->reason_phrase);
1160 g_object_unref (msg);
1162 soup_uri_free (ipv6_uri);
1163 soup_test_session_abort_unref (session);
1164 soup_test_server_quit_unref (ipv6_server);
1168 do_idle_on_dispose_test (void)
1170 SoupSession *session;
1172 GMainContext *async_context;
1174 debug_printf (1, "\nTesting SoupSessionAsync dispose behavior\n");
1176 async_context = g_main_context_new ();
1177 session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
1178 SOUP_SESSION_ASYNC_CONTEXT, async_context,
1181 msg = soup_message_new_from_uri ("GET", base_uri);
1182 soup_session_send_message (session, msg);
1183 g_object_unref (msg);
1185 while (g_main_context_iteration (async_context, FALSE))
1188 g_object_run_dispose (G_OBJECT (session));
1190 if (g_main_context_iteration (async_context, FALSE)) {
1191 debug_printf (1, " idle was queued!\n");
1195 g_object_unref (session);
1196 g_main_context_unref (async_context);
1200 do_pause_abort_test (void)
1202 SoupSession *session;
1206 debug_printf (1, "\nTesting paused messages don't get leaked on abort\n");
1208 session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
1210 msg = soup_message_new_from_uri ("GET", base_uri);
1211 soup_session_queue_message (session, msg, NULL, NULL);
1212 soup_session_pause_message (session, msg);
1214 g_object_add_weak_pointer (G_OBJECT (msg), &ptr);
1215 soup_test_session_abort_unref (session);
1218 debug_printf (1, " msg was leaked\n");
1224 main (int argc, char **argv)
1226 SoupAuthDomain *auth_domain;
1228 test_init (argc, argv, NULL);
1230 server = soup_test_server_new (TRUE);
1231 soup_server_add_handler (server, NULL, server_callback, "http", NULL);
1232 base_uri = soup_uri_new ("http://127.0.0.1/");
1233 soup_uri_set_port (base_uri, soup_server_get_port (server));
1235 auth_domain = soup_auth_domain_basic_new (
1236 SOUP_AUTH_DOMAIN_REALM, "misc-test",
1237 SOUP_AUTH_DOMAIN_ADD_PATH, "/auth",
1238 SOUP_AUTH_DOMAIN_BASIC_AUTH_CALLBACK, auth_callback,
1240 soup_server_add_auth_domain (server, auth_domain);
1241 g_object_unref (auth_domain);
1243 ssl_server = soup_test_server_new_ssl (TRUE);
1244 soup_server_add_handler (ssl_server, NULL, server_callback, "https", NULL);
1245 ssl_base_uri = soup_uri_new ("https://127.0.0.1/");
1246 soup_uri_set_port (ssl_base_uri, soup_server_get_port (ssl_server));
1249 do_callback_unref_test ();
1250 do_callback_unref_req_test ();
1251 do_msg_reuse_test ();
1253 do_early_abort_test ();
1254 do_early_abort_req_test ();
1255 do_accept_language_test ();
1256 do_cancel_while_reading_test ();
1257 do_cancel_while_reading_req_test ();
1261 do_idle_on_dispose_test ();
1262 do_pause_abort_test ();
1264 soup_uri_free (base_uri);
1265 soup_uri_free (ssl_base_uri);
1266 soup_test_server_quit_unref (server);
1267 soup_test_server_quit_unref (ssl_server);