1 | /* GIO - GLib Input, Output and Streaming Library |
2 | * |
3 | * Copyright (C) 2018 Igalia S.L. |
4 | * |
5 | * This library is free software; you can redistribute it and/or |
6 | * modify it under the terms of the GNU Lesser General Public |
7 | * License as published by the Free Software Foundation; either |
8 | * version 2.1 of the License, or (at your option) any later version. |
9 | * |
10 | * This library is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | * Lesser General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU Lesser General |
16 | * Public License along with this library; if not, see <http://www.gnu.org/licenses/>. |
17 | */ |
18 | |
19 | #include <gio/gio.h> |
20 | |
21 | static void |
22 | on_connected (GObject *source_object, |
23 | GAsyncResult *result, |
24 | gpointer user_data) |
25 | { |
26 | GSocketConnection *conn; |
27 | GError *error = NULL; |
28 | |
29 | conn = g_socket_client_connect_to_uri_finish (G_SOCKET_CLIENT (source_object), result, error: &error); |
30 | g_assert_no_error (error); |
31 | |
32 | g_object_unref (object: conn); |
33 | g_main_loop_quit (loop: user_data); |
34 | } |
35 | |
36 | static void |
37 | test_happy_eyeballs (void) |
38 | { |
39 | GSocketClient *client; |
40 | GSocketService *service; |
41 | GError *error = NULL; |
42 | guint16 port; |
43 | GMainLoop *loop; |
44 | |
45 | loop = g_main_loop_new (NULL, FALSE); |
46 | |
47 | service = g_socket_service_new (); |
48 | port = g_socket_listener_add_any_inet_port (G_SOCKET_LISTENER (service), NULL, error: &error); |
49 | g_assert_no_error (error); |
50 | g_socket_service_start (service); |
51 | |
52 | /* All of the magic here actually happens in slow-connect-preload.c |
53 | * which as you would guess is preloaded. So this is just making a |
54 | * normal connection that happens to take 600ms each time. This will |
55 | * trigger the logic to make multiple parallel connections. |
56 | */ |
57 | client = g_socket_client_new (); |
58 | g_socket_client_connect_to_host_async (client, host_and_port: "localhost" , default_port: port, NULL, callback: on_connected, user_data: loop); |
59 | g_main_loop_run (loop); |
60 | |
61 | g_main_loop_unref (loop); |
62 | g_object_unref (object: service); |
63 | g_object_unref (object: client); |
64 | } |
65 | |
66 | static void |
67 | on_connected_cancelled (GObject *source_object, |
68 | GAsyncResult *result, |
69 | gpointer user_data) |
70 | { |
71 | GSocketConnection *conn; |
72 | GError *error = NULL; |
73 | |
74 | conn = g_socket_client_connect_to_uri_finish (G_SOCKET_CLIENT (source_object), result, error: &error); |
75 | g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); |
76 | g_assert_null (conn); |
77 | |
78 | g_error_free (error); |
79 | g_main_loop_quit (loop: user_data); |
80 | } |
81 | |
82 | typedef struct |
83 | { |
84 | GCancellable *cancellable; |
85 | gboolean completed; |
86 | } EventCallbackData; |
87 | |
88 | static void |
89 | on_event (GSocketClient *client, |
90 | GSocketClientEvent event, |
91 | GSocketConnectable *connectable, |
92 | GIOStream *connection, |
93 | EventCallbackData *data) |
94 | { |
95 | if (data->cancellable && event == G_SOCKET_CLIENT_CONNECTED) |
96 | { |
97 | g_cancellable_cancel (cancellable: data->cancellable); |
98 | } |
99 | else if (event == G_SOCKET_CLIENT_COMPLETE) |
100 | { |
101 | data->completed = TRUE; |
102 | g_assert_null (connection); |
103 | } |
104 | } |
105 | |
106 | static void |
107 | test_happy_eyeballs_cancel_delayed (void) |
108 | { |
109 | GSocketClient *client; |
110 | GSocketService *service; |
111 | GError *error = NULL; |
112 | guint16 port; |
113 | GMainLoop *loop; |
114 | EventCallbackData data = { NULL, FALSE }; |
115 | |
116 | /* This just tests that cancellation works as expected, still emits the completed signal, |
117 | * and never returns a connection */ |
118 | |
119 | loop = g_main_loop_new (NULL, FALSE); |
120 | |
121 | service = g_socket_service_new (); |
122 | port = g_socket_listener_add_any_inet_port (G_SOCKET_LISTENER (service), NULL, error: &error); |
123 | g_assert_no_error (error); |
124 | g_socket_service_start (service); |
125 | |
126 | client = g_socket_client_new (); |
127 | data.cancellable = g_cancellable_new (); |
128 | g_socket_client_connect_to_host_async (client, host_and_port: "localhost" , default_port: port, cancellable: data.cancellable, callback: on_connected_cancelled, user_data: loop); |
129 | g_signal_connect (client, "event" , G_CALLBACK (on_event), &data); |
130 | g_main_loop_run (loop); |
131 | |
132 | g_assert_true (data.completed); |
133 | g_main_loop_unref (loop); |
134 | g_object_unref (object: service); |
135 | g_object_unref (object: client); |
136 | g_object_unref (object: data.cancellable); |
137 | } |
138 | |
139 | static void |
140 | test_happy_eyeballs_cancel_instant (void) |
141 | { |
142 | GSocketClient *client; |
143 | GSocketService *service; |
144 | GError *error = NULL; |
145 | guint16 port; |
146 | GMainLoop *loop; |
147 | GCancellable *cancel; |
148 | EventCallbackData data = { NULL, FALSE }; |
149 | |
150 | /* This tests the same things as above, test_happy_eyeballs_cancel_delayed(), but |
151 | * with different timing since it sends an already cancelled cancellable */ |
152 | |
153 | loop = g_main_loop_new (NULL, FALSE); |
154 | |
155 | service = g_socket_service_new (); |
156 | port = g_socket_listener_add_any_inet_port (G_SOCKET_LISTENER (service), NULL, error: &error); |
157 | g_assert_no_error (error); |
158 | g_socket_service_start (service); |
159 | |
160 | client = g_socket_client_new (); |
161 | cancel = g_cancellable_new (); |
162 | g_cancellable_cancel (cancellable: cancel); |
163 | g_socket_client_connect_to_host_async (client, host_and_port: "localhost" , default_port: port, cancellable: cancel, callback: on_connected_cancelled, user_data: loop); |
164 | g_signal_connect (client, "event" , G_CALLBACK (on_event), &data); |
165 | g_main_loop_run (loop); |
166 | |
167 | g_assert_true (data.completed); |
168 | g_main_loop_unref (loop); |
169 | g_object_unref (object: service); |
170 | g_object_unref (object: client); |
171 | g_object_unref (object: cancel); |
172 | } |
173 | |
174 | int |
175 | main (int argc, char *argv[]) |
176 | { |
177 | g_test_init (argc: &argc, argv: &argv, NULL); |
178 | |
179 | g_test_add_func (testpath: "/socket-client/happy-eyeballs/slow" , test_func: test_happy_eyeballs); |
180 | g_test_add_func (testpath: "/socket-client/happy-eyeballs/cancellation/instant" , test_func: test_happy_eyeballs_cancel_instant); |
181 | g_test_add_func (testpath: "/socket-client/happy-eyeballs/cancellation/delayed" , test_func: test_happy_eyeballs_cancel_delayed); |
182 | |
183 | |
184 | return g_test_run (); |
185 | } |
186 | |