Branch data Line data Source code
1 : : /* GLib testing framework examples and tests
2 : : *
3 : : * Copyright 2012 Red Hat, Inc.
4 : : *
5 : : * SPDX-License-Identifier: LGPL-2.1-or-later
6 : : *
7 : : * This library is free software; you can redistribute it and/or
8 : : * modify it under the terms of the GNU Lesser General Public
9 : : * License as published by the Free Software Foundation; either
10 : : * version 2.1 of the License, or (at your option) any later version.
11 : : *
12 : : * This library is distributed in the hope that it will be useful,
13 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 : : * Lesser General Public License for more details.
16 : : *
17 : : * You should have received a copy of the GNU Lesser General
18 : : * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
19 : : */
20 : :
21 : : #include <string.h>
22 : :
23 : : #include <gio/gio.h>
24 : :
25 : : /* Overview:
26 : : *
27 : : * We have an echo server, two proxy servers, two GProxy
28 : : * implementations, and two GProxyResolver implementations.
29 : : *
30 : : * The echo server runs at @server.server_addr (on
31 : : * @server.server_port).
32 : : *
33 : : * The two proxy servers, A and B, run on @proxy_a.port and
34 : : * @proxy_b.port, with @proxy_a.uri and @proxy_b.uri pointing to them.
35 : : * The "negotiation" with the two proxies is just sending the single
36 : : * letter "a" or "b" and receiving it back in uppercase; the proxy
37 : : * then connects to @server_addr.
38 : : *
39 : : * Proxy A supports "alpha://" URIs, and does not support hostname
40 : : * resolution, and Proxy B supports "beta://" URIs, and does support
41 : : * hostname resolution (but it just ignores the hostname and always
42 : : * connects to @server_addr anyway).
43 : : *
44 : : * The default GProxyResolver (GTestProxyResolver) looks at its URI
45 : : * and returns [ "direct://" ] for "simple://" URIs, and
46 : : * [ proxy_a.uri, proxy_b.uri ] for most other URIs. It can also return
47 : : * invalid results for other URIs (empty://, invalid://,
48 : : * invalid-then-simple://, and simple-then-invalid://) to test error
49 : : * handling.
50 : : *
51 : : * The other GProxyResolver (GTestAltProxyResolver) always returns
52 : : * [ proxy_a.uri ].
53 : : */
54 : :
55 : : typedef struct {
56 : : gchar *proxy_command;
57 : : gchar *supported_protocol;
58 : :
59 : : GSocket *server;
60 : : GThread *thread;
61 : : GCancellable *cancellable;
62 : : gchar *uri;
63 : : gushort port;
64 : :
65 : : GSocket *client_sock, *server_sock;
66 : : GMainLoop *loop;
67 : :
68 : : GError *last_error;
69 : : } ProxyData;
70 : :
71 : : static ProxyData proxy_a, proxy_b;
72 : :
73 : : typedef struct {
74 : : GSocket *server;
75 : : GThread *server_thread;
76 : : GCancellable *cancellable;
77 : : GSocketAddress *server_addr;
78 : : gushort server_port;
79 : : } ServerData;
80 : :
81 : : static ServerData server;
82 : :
83 : : static gchar **last_proxies;
84 : :
85 : : static GSocketClient *client;
86 : :
87 : :
88 : : /**************************************/
89 : : /* Test GProxyResolver implementation */
90 : : /**************************************/
91 : :
92 : : typedef struct {
93 : : GObject parent_instance;
94 : : } GTestProxyResolver;
95 : :
96 : : typedef struct {
97 : : GObjectClass parent_class;
98 : : } GTestProxyResolverClass;
99 : :
100 : : static void g_test_proxy_resolver_iface_init (GProxyResolverInterface *iface);
101 : :
102 : : static GType _g_test_proxy_resolver_get_type (void);
103 : : #define g_test_proxy_resolver_get_type _g_test_proxy_resolver_get_type
104 : 4 : G_DEFINE_TYPE_WITH_CODE (GTestProxyResolver, g_test_proxy_resolver, G_TYPE_OBJECT,
105 : : G_IMPLEMENT_INTERFACE (G_TYPE_PROXY_RESOLVER,
106 : : g_test_proxy_resolver_iface_init)
107 : : g_io_extension_point_implement (G_PROXY_RESOLVER_EXTENSION_POINT_NAME,
108 : : g_define_type_id,
109 : : "test",
110 : : 0))
111 : :
112 : : static void
113 : 2 : g_test_proxy_resolver_init (GTestProxyResolver *resolver)
114 : : {
115 : 2 : }
116 : :
117 : : static gboolean
118 : 1 : g_test_proxy_resolver_is_supported (GProxyResolver *resolver)
119 : : {
120 : 1 : return TRUE;
121 : : }
122 : :
123 : : static gchar **
124 : 24 : g_test_proxy_resolver_lookup (GProxyResolver *resolver,
125 : : const gchar *uri,
126 : : GCancellable *cancellable,
127 : : GError **error)
128 : : {
129 : : gchar **proxies;
130 : :
131 : 24 : g_assert (last_proxies == NULL);
132 : :
133 : 24 : if (g_cancellable_set_error_if_cancelled (cancellable, error))
134 : 0 : return NULL;
135 : :
136 : 24 : proxies = g_new (gchar *, 3);
137 : :
138 : 24 : if (g_str_has_prefix (uri, "simple://"))
139 : : {
140 : 4 : proxies[0] = g_strdup ("direct://");
141 : 4 : proxies[1] = NULL;
142 : : }
143 : 20 : else if (g_str_has_prefix (uri, "empty://"))
144 : : {
145 : 2 : proxies[0] = g_strdup ("");
146 : 2 : proxies[1] = NULL;
147 : : }
148 : 18 : else if (g_str_has_prefix (uri, "invalid://"))
149 : : {
150 : 2 : proxies[0] = g_strdup ("😼");
151 : 2 : proxies[1] = NULL;
152 : : }
153 : 16 : else if (g_str_has_prefix (uri, "invalid-then-simple://"))
154 : : {
155 : 2 : proxies[0] = g_strdup ("😼");
156 : 2 : proxies[1] = g_strdup ("direct://");
157 : 2 : proxies[2] = NULL;
158 : : }
159 : 14 : else if (g_str_has_prefix (uri, "simple-then-invalid://"))
160 : : {
161 : 2 : proxies[0] = g_strdup ("direct://");
162 : 2 : proxies[1] = g_strdup ("😼");
163 : 2 : proxies[2] = NULL;
164 : : }
165 : : else
166 : : {
167 : : /* Proxy A can only deal with "alpha://" URIs, not
168 : : * "beta://", but we always return both URIs
169 : : * anyway so we can test error handling when the first
170 : : * fails.
171 : : */
172 : 12 : proxies[0] = g_strdup (proxy_a.uri);
173 : 12 : proxies[1] = g_strdup (proxy_b.uri);
174 : 12 : proxies[2] = NULL;
175 : : }
176 : :
177 : 24 : last_proxies = g_strdupv (proxies);
178 : :
179 : 24 : return proxies;
180 : : }
181 : :
182 : : static void
183 : 13 : g_test_proxy_resolver_lookup_async (GProxyResolver *resolver,
184 : : const gchar *uri,
185 : : GCancellable *cancellable,
186 : : GAsyncReadyCallback callback,
187 : : gpointer user_data)
188 : : {
189 : 13 : GError *error = NULL;
190 : : GTask *task;
191 : : gchar **proxies;
192 : :
193 : 13 : proxies = g_proxy_resolver_lookup (resolver, uri, cancellable, &error);
194 : :
195 : 13 : task = g_task_new (resolver, NULL, callback, user_data);
196 : 13 : g_task_set_source_tag (task, g_test_proxy_resolver_lookup_async);
197 : 13 : if (proxies == NULL)
198 : 0 : g_task_return_error (task, error);
199 : : else
200 : 13 : g_task_return_pointer (task, proxies, (GDestroyNotify) g_strfreev);
201 : :
202 : 13 : g_object_unref (task);
203 : 13 : }
204 : :
205 : : static gchar **
206 : 13 : g_test_proxy_resolver_lookup_finish (GProxyResolver *resolver,
207 : : GAsyncResult *result,
208 : : GError **error)
209 : : {
210 : 13 : g_assert_true (g_task_is_valid (result, resolver));
211 : 13 : g_assert_true (g_task_get_source_tag (G_TASK (result)) == g_test_proxy_resolver_lookup_async);
212 : :
213 : 13 : return g_task_propagate_pointer (G_TASK (result), error);
214 : : }
215 : :
216 : : static void
217 : 1 : g_test_proxy_resolver_class_init (GTestProxyResolverClass *resolver_class)
218 : : {
219 : 1 : }
220 : :
221 : : static void
222 : 1 : g_test_proxy_resolver_iface_init (GProxyResolverInterface *iface)
223 : : {
224 : 1 : iface->is_supported = g_test_proxy_resolver_is_supported;
225 : 1 : iface->lookup = g_test_proxy_resolver_lookup;
226 : 1 : iface->lookup_async = g_test_proxy_resolver_lookup_async;
227 : 1 : iface->lookup_finish = g_test_proxy_resolver_lookup_finish;
228 : 1 : }
229 : :
230 : : /****************************/
231 : : /* Alternate GProxyResolver */
232 : : /****************************/
233 : :
234 : : typedef GTestProxyResolver GTestAltProxyResolver;
235 : : typedef GTestProxyResolverClass GTestAltProxyResolverClass;
236 : :
237 : : static void g_test_alt_proxy_resolver_iface_init (GProxyResolverInterface *iface);
238 : :
239 : : static GType _g_test_alt_proxy_resolver_get_type (void);
240 : : #define g_test_alt_proxy_resolver_get_type _g_test_alt_proxy_resolver_get_type
241 : 3 : G_DEFINE_TYPE_WITH_CODE (GTestAltProxyResolver, g_test_alt_proxy_resolver, g_test_proxy_resolver_get_type (),
242 : : G_IMPLEMENT_INTERFACE (G_TYPE_PROXY_RESOLVER,
243 : : g_test_alt_proxy_resolver_iface_init);
244 : : )
245 : :
246 : : static void
247 : 1 : g_test_alt_proxy_resolver_init (GTestProxyResolver *resolver)
248 : : {
249 : 1 : }
250 : :
251 : : static gchar **
252 : 6 : g_test_alt_proxy_resolver_lookup (GProxyResolver *resolver,
253 : : const gchar *uri,
254 : : GCancellable *cancellable,
255 : : GError **error)
256 : : {
257 : : gchar **proxies;
258 : :
259 : 6 : proxies = g_new (gchar *, 2);
260 : :
261 : 6 : proxies[0] = g_strdup (proxy_a.uri);
262 : 6 : proxies[1] = NULL;
263 : :
264 : 6 : last_proxies = g_strdupv (proxies);
265 : :
266 : 6 : return proxies;
267 : : }
268 : :
269 : : static void
270 : 1 : g_test_alt_proxy_resolver_class_init (GTestProxyResolverClass *resolver_class)
271 : : {
272 : 1 : }
273 : :
274 : : static void
275 : 1 : g_test_alt_proxy_resolver_iface_init (GProxyResolverInterface *iface)
276 : : {
277 : 1 : iface->lookup = g_test_alt_proxy_resolver_lookup;
278 : 1 : }
279 : :
280 : :
281 : : /****************************************/
282 : : /* Test proxy implementation base class */
283 : : /****************************************/
284 : :
285 : : typedef struct {
286 : : GObject parent;
287 : :
288 : : ProxyData *proxy_data;
289 : : } GProxyBase;
290 : :
291 : : typedef struct {
292 : : GObjectClass parent_class;
293 : : } GProxyBaseClass;
294 : :
295 : : static GType _g_proxy_base_get_type (void);
296 : : #define g_proxy_base_get_type _g_proxy_base_get_type
297 : 4 : G_DEFINE_ABSTRACT_TYPE (GProxyBase, g_proxy_base, G_TYPE_OBJECT)
298 : :
299 : : static void
300 : 44 : g_proxy_base_init (GProxyBase *proxy)
301 : : {
302 : 44 : }
303 : :
304 : : static GIOStream *
305 : 16 : g_proxy_base_connect (GProxy *proxy,
306 : : GIOStream *io_stream,
307 : : GProxyAddress *proxy_address,
308 : : GCancellable *cancellable,
309 : : GError **error)
310 : : {
311 : 16 : ProxyData *data = ((GProxyBase *) proxy)->proxy_data;
312 : : const gchar *protocol;
313 : : GOutputStream *ostream;
314 : : GInputStream *istream;
315 : : gchar response;
316 : :
317 : 16 : g_assert_no_error (data->last_error);
318 : :
319 : 16 : protocol = g_proxy_address_get_destination_protocol (proxy_address);
320 : 16 : if (strcmp (protocol, data->supported_protocol) != 0)
321 : : {
322 : 8 : g_set_error_literal (&data->last_error,
323 : : G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
324 : : "Unsupported protocol");
325 : 8 : goto fail;
326 : : }
327 : :
328 : 8 : ostream = g_io_stream_get_output_stream (io_stream);
329 : 8 : if (g_output_stream_write (ostream, data->proxy_command, 1, cancellable,
330 : : &data->last_error) != 1)
331 : 0 : goto fail;
332 : :
333 : 8 : istream = g_io_stream_get_input_stream (io_stream);
334 : 8 : if (g_input_stream_read (istream, &response, 1, cancellable,
335 : : &data->last_error) != 1)
336 : 0 : goto fail;
337 : :
338 : 8 : if (response != g_ascii_toupper (*data->proxy_command))
339 : : {
340 : 0 : g_set_error_literal (&data->last_error,
341 : : G_IO_ERROR, G_IO_ERROR_FAILED,
342 : : "Failed");
343 : 0 : goto fail;
344 : : }
345 : :
346 : 8 : return g_object_ref (io_stream);
347 : :
348 : 8 : fail:
349 : 8 : g_propagate_error (error, g_error_copy (data->last_error));
350 : 8 : return NULL;
351 : : }
352 : :
353 : : static void
354 : 8 : g_proxy_base_connect_async (GProxy *proxy,
355 : : GIOStream *io_stream,
356 : : GProxyAddress *proxy_address,
357 : : GCancellable *cancellable,
358 : : GAsyncReadyCallback callback,
359 : : gpointer user_data)
360 : : {
361 : 8 : GError *error = NULL;
362 : : GTask *task;
363 : : GIOStream *proxy_io_stream;
364 : :
365 : 8 : task = g_task_new (proxy, NULL, callback, user_data);
366 : :
367 : 8 : proxy_io_stream = g_proxy_connect (proxy, io_stream, proxy_address,
368 : : cancellable, &error);
369 : 8 : if (proxy_io_stream)
370 : 4 : g_task_return_pointer (task, proxy_io_stream, g_object_unref);
371 : : else
372 : 4 : g_task_return_error (task, error);
373 : 8 : g_object_unref (task);
374 : 8 : }
375 : :
376 : : static GIOStream *
377 : 8 : g_proxy_base_connect_finish (GProxy *proxy,
378 : : GAsyncResult *result,
379 : : GError **error)
380 : : {
381 : 8 : return g_task_propagate_pointer (G_TASK (result), error);
382 : : }
383 : :
384 : : static void
385 : 1 : g_proxy_base_class_init (GProxyBaseClass *class)
386 : : {
387 : 1 : }
388 : :
389 : :
390 : : /********************************************/
391 : : /* Test proxy implementation #1 ("Proxy A") */
392 : : /********************************************/
393 : :
394 : : typedef GProxyBase GProxyA;
395 : : typedef GProxyBaseClass GProxyAClass;
396 : :
397 : : static void g_proxy_a_iface_init (GProxyInterface *proxy_iface);
398 : :
399 : : static GType _g_proxy_a_get_type (void);
400 : : #define g_proxy_a_get_type _g_proxy_a_get_type
401 : 3 : G_DEFINE_TYPE_WITH_CODE (GProxyA, g_proxy_a, g_proxy_base_get_type (),
402 : : G_IMPLEMENT_INTERFACE (G_TYPE_PROXY,
403 : : g_proxy_a_iface_init)
404 : : g_io_extension_point_implement (G_PROXY_EXTENSION_POINT_NAME,
405 : : g_define_type_id,
406 : : "proxy-a",
407 : : 0))
408 : :
409 : : static void
410 : 28 : g_proxy_a_init (GProxyA *proxy)
411 : : {
412 : 28 : ((GProxyBase *) proxy)->proxy_data = &proxy_a;
413 : 28 : }
414 : :
415 : : static gboolean
416 : 18 : g_proxy_a_supports_hostname (GProxy *proxy)
417 : : {
418 : 18 : return FALSE;
419 : : }
420 : :
421 : : static void
422 : 1 : g_proxy_a_class_init (GProxyAClass *class)
423 : : {
424 : 1 : }
425 : :
426 : : static void
427 : 1 : g_proxy_a_iface_init (GProxyInterface *proxy_iface)
428 : : {
429 : 1 : proxy_iface->connect = g_proxy_base_connect;
430 : 1 : proxy_iface->connect_async = g_proxy_base_connect_async;
431 : 1 : proxy_iface->connect_finish = g_proxy_base_connect_finish;
432 : 1 : proxy_iface->supports_hostname = g_proxy_a_supports_hostname;
433 : 1 : }
434 : :
435 : : /********************************************/
436 : : /* Test proxy implementation #2 ("Proxy B") */
437 : : /********************************************/
438 : :
439 : : typedef GProxyBase GProxyB;
440 : : typedef GProxyBaseClass GProxyBClass;
441 : :
442 : : static void g_proxy_b_iface_init (GProxyInterface *proxy_iface);
443 : :
444 : : static GType _g_proxy_b_get_type (void);
445 : : #define g_proxy_b_get_type _g_proxy_b_get_type
446 : 3 : G_DEFINE_TYPE_WITH_CODE (GProxyB, g_proxy_b, g_proxy_base_get_type (),
447 : : G_IMPLEMENT_INTERFACE (G_TYPE_PROXY,
448 : : g_proxy_b_iface_init)
449 : : g_io_extension_point_implement (G_PROXY_EXTENSION_POINT_NAME,
450 : : g_define_type_id,
451 : : "proxy-b",
452 : : 0))
453 : :
454 : : static void
455 : 16 : g_proxy_b_init (GProxyB *proxy)
456 : : {
457 : 16 : ((GProxyBase *) proxy)->proxy_data = &proxy_b;
458 : 16 : }
459 : :
460 : : static gboolean
461 : 10 : g_proxy_b_supports_hostname (GProxy *proxy)
462 : : {
463 : 10 : return TRUE;
464 : : }
465 : :
466 : : static void
467 : 1 : g_proxy_b_class_init (GProxyBClass *class)
468 : : {
469 : 1 : }
470 : :
471 : : static void
472 : 1 : g_proxy_b_iface_init (GProxyInterface *proxy_iface)
473 : : {
474 : 1 : proxy_iface->connect = g_proxy_base_connect;
475 : 1 : proxy_iface->connect_async = g_proxy_base_connect_async;
476 : 1 : proxy_iface->connect_finish = g_proxy_base_connect_finish;
477 : 1 : proxy_iface->supports_hostname = g_proxy_b_supports_hostname;
478 : 1 : }
479 : :
480 : :
481 : : /***********************************/
482 : : /* The proxy server implementation */
483 : : /***********************************/
484 : :
485 : : static gboolean
486 : 40 : proxy_bytes (GSocket *socket,
487 : : GIOCondition condition,
488 : : gpointer user_data)
489 : : {
490 : 40 : ProxyData *proxy = user_data;
491 : : gssize nread, nwrote, total;
492 : : gchar buffer[8];
493 : : GSocket *out_socket;
494 : 40 : GError *error = NULL;
495 : :
496 : 40 : nread = g_socket_receive_with_blocking (socket, buffer, sizeof (buffer),
497 : : TRUE, NULL, &error);
498 : 40 : if (nread == -1)
499 : : {
500 : 0 : g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CLOSED);
501 : 0 : return FALSE;
502 : : }
503 : : else
504 : 40 : g_assert_no_error (error);
505 : :
506 : 40 : if (nread == 0)
507 : : {
508 : 8 : g_main_loop_quit (proxy->loop);
509 : 8 : return FALSE;
510 : : }
511 : :
512 : 32 : if (socket == proxy->client_sock)
513 : 16 : out_socket = proxy->server_sock;
514 : : else
515 : 16 : out_socket = proxy->client_sock;
516 : :
517 : 64 : for (total = 0; total < nread; total += nwrote)
518 : : {
519 : 32 : nwrote = g_socket_send_with_blocking (out_socket,
520 : 32 : buffer + total, nread - total,
521 : : TRUE, NULL, &error);
522 : 32 : g_assert_no_error (error);
523 : : }
524 : :
525 : 32 : return TRUE;
526 : : }
527 : :
528 : : static gpointer
529 : 2 : proxy_thread (gpointer user_data)
530 : : {
531 : 2 : ProxyData *proxy = user_data;
532 : 2 : GError *error = NULL;
533 : : gssize nread, nwrote;
534 : 2 : gchar command[2] = { 0, 0 };
535 : : GMainContext *context;
536 : : GSource *read_source, *write_source;
537 : :
538 : 2 : context = g_main_context_new ();
539 : 2 : proxy->loop = g_main_loop_new (context, FALSE);
540 : :
541 : : while (TRUE)
542 : : {
543 : 18 : proxy->client_sock = g_socket_accept (proxy->server, proxy->cancellable, &error);
544 : 18 : if (!proxy->client_sock)
545 : : {
546 : 2 : g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
547 : 2 : g_error_free (error);
548 : 2 : break;
549 : : }
550 : : else
551 : 16 : g_assert_no_error (error);
552 : :
553 : 16 : nread = g_socket_receive (proxy->client_sock, command, 1, NULL, &error);
554 : 16 : g_assert_no_error (error);
555 : :
556 : 16 : if (nread == 0)
557 : : {
558 : 8 : g_clear_object (&proxy->client_sock);
559 : 8 : continue;
560 : : }
561 : :
562 : 8 : g_assert_cmpint (nread, ==, 1);
563 : 8 : g_assert_cmpstr (command, ==, proxy->proxy_command);
564 : :
565 : 8 : *command = g_ascii_toupper (*command);
566 : 8 : nwrote = g_socket_send (proxy->client_sock, command, 1, NULL, &error);
567 : 8 : g_assert_no_error (error);
568 : 8 : g_assert_cmpint (nwrote, ==, 1);
569 : :
570 : 8 : proxy->server_sock = g_socket_new (G_SOCKET_FAMILY_IPV4,
571 : : G_SOCKET_TYPE_STREAM,
572 : : G_SOCKET_PROTOCOL_DEFAULT,
573 : : &error);
574 : 8 : g_assert_no_error (error);
575 : 8 : g_socket_connect (proxy->server_sock, server.server_addr, NULL, &error);
576 : 8 : g_assert_no_error (error);
577 : :
578 : 8 : read_source = g_socket_create_source (proxy->client_sock, G_IO_IN, NULL);
579 : 8 : g_source_set_callback (read_source, (GSourceFunc)proxy_bytes, proxy, NULL);
580 : 8 : g_source_attach (read_source, context);
581 : :
582 : 8 : write_source = g_socket_create_source (proxy->server_sock, G_IO_IN, NULL);
583 : 8 : g_source_set_callback (write_source, (GSourceFunc)proxy_bytes, proxy, NULL);
584 : 8 : g_source_attach (write_source, context);
585 : :
586 : 8 : g_main_loop_run (proxy->loop);
587 : :
588 : 8 : g_socket_close (proxy->client_sock, &error);
589 : 8 : g_assert_no_error (error);
590 : 8 : g_clear_object (&proxy->client_sock);
591 : :
592 : 8 : g_socket_close (proxy->server_sock, &error);
593 : 8 : g_assert_no_error (error);
594 : 8 : g_clear_object (&proxy->server_sock);
595 : :
596 : 8 : g_source_destroy (read_source);
597 : 8 : g_source_unref (read_source);
598 : 8 : g_source_destroy (write_source);
599 : 8 : g_source_unref (write_source);
600 : : }
601 : :
602 : 2 : g_main_loop_unref (proxy->loop);
603 : 2 : g_main_context_unref (context);
604 : :
605 : 2 : g_object_unref (proxy->server);
606 : 2 : g_object_unref (proxy->cancellable);
607 : :
608 : 2 : g_free (proxy->proxy_command);
609 : 2 : g_free (proxy->supported_protocol);
610 : 2 : g_free (proxy->uri);
611 : :
612 : 2 : return NULL;
613 : : }
614 : :
615 : : static void
616 : 2 : create_proxy (ProxyData *proxy,
617 : : gchar proxy_protocol,
618 : : const gchar *destination_protocol,
619 : : GCancellable *cancellable)
620 : : {
621 : 2 : GError *error = NULL;
622 : : GSocketAddress *addr;
623 : : GInetAddress *iaddr;
624 : :
625 : 2 : proxy->proxy_command = g_strdup_printf ("%c", proxy_protocol);
626 : 2 : proxy->supported_protocol = g_strdup (destination_protocol);
627 : 2 : proxy->cancellable = g_object_ref (cancellable);
628 : :
629 : 2 : proxy->server = g_socket_new (G_SOCKET_FAMILY_IPV4,
630 : : G_SOCKET_TYPE_STREAM,
631 : : G_SOCKET_PROTOCOL_DEFAULT,
632 : : &error);
633 : 2 : g_assert_no_error (error);
634 : :
635 : 2 : iaddr = g_inet_address_new_loopback (G_SOCKET_FAMILY_IPV4);
636 : 2 : addr = g_inet_socket_address_new (iaddr, 0);
637 : 2 : g_object_unref (iaddr);
638 : :
639 : 2 : g_socket_bind (proxy->server, addr, TRUE, &error);
640 : 2 : g_assert_no_error (error);
641 : 2 : g_object_unref (addr);
642 : :
643 : 2 : addr = g_socket_get_local_address (proxy->server, &error);
644 : 2 : proxy->port = g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (addr));
645 : 4 : proxy->uri = g_strdup_printf ("proxy-%c://127.0.0.1:%u",
646 : 2 : g_ascii_tolower (proxy_protocol),
647 : 2 : proxy->port);
648 : 2 : g_object_unref (addr);
649 : :
650 : 2 : g_socket_listen (proxy->server, &error);
651 : 2 : g_assert_no_error (error);
652 : :
653 : 2 : proxy->thread = g_thread_new ("proxy", proxy_thread, proxy);
654 : 2 : }
655 : :
656 : :
657 : :
658 : : /**************************/
659 : : /* The actual echo server */
660 : : /**************************/
661 : :
662 : : static gpointer
663 : 1 : echo_server_thread (gpointer user_data)
664 : : {
665 : 1 : ServerData *data = user_data;
666 : : GSocket *sock;
667 : 1 : GError *error = NULL;
668 : : gssize nread, nwrote;
669 : : gchar buf[128];
670 : :
671 : : while (TRUE)
672 : : {
673 : 15 : sock = g_socket_accept (data->server, data->cancellable, &error);
674 : 15 : if (!sock)
675 : : {
676 : 1 : g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
677 : 1 : g_error_free (error);
678 : 1 : break;
679 : : }
680 : : else
681 : 14 : g_assert_no_error (error);
682 : :
683 : : while (TRUE)
684 : : {
685 : 35 : nread = g_socket_receive (sock, buf, sizeof (buf), NULL, &error);
686 : 35 : g_assert_no_error (error);
687 : 35 : g_assert_cmpint (nread, >=, 0);
688 : :
689 : 35 : if (nread == 0)
690 : 14 : break;
691 : :
692 : 21 : nwrote = g_socket_send (sock, buf, nread, NULL, &error);
693 : 21 : g_assert_no_error (error);
694 : 21 : g_assert_cmpint (nwrote, ==, nread);
695 : : }
696 : :
697 : 14 : g_socket_close (sock, &error);
698 : 14 : g_assert_no_error (error);
699 : 14 : g_object_unref (sock);
700 : : }
701 : :
702 : 1 : g_object_unref (data->server);
703 : 1 : g_object_unref (data->server_addr);
704 : 1 : g_object_unref (data->cancellable);
705 : :
706 : 1 : return NULL;
707 : : }
708 : :
709 : : static void
710 : 1 : create_server (ServerData *data, GCancellable *cancellable)
711 : : {
712 : 1 : GError *error = NULL;
713 : : GSocketAddress *addr;
714 : : GInetAddress *iaddr;
715 : :
716 : 1 : data->cancellable = g_object_ref (cancellable);
717 : :
718 : 1 : data->server = g_socket_new (G_SOCKET_FAMILY_IPV4,
719 : : G_SOCKET_TYPE_STREAM,
720 : : G_SOCKET_PROTOCOL_DEFAULT,
721 : : &error);
722 : 1 : g_assert_no_error (error);
723 : :
724 : 1 : g_socket_set_blocking (data->server, TRUE);
725 : 1 : iaddr = g_inet_address_new_loopback (G_SOCKET_FAMILY_IPV4);
726 : 1 : addr = g_inet_socket_address_new (iaddr, 0);
727 : 1 : g_object_unref (iaddr);
728 : :
729 : 1 : g_socket_bind (data->server, addr, TRUE, &error);
730 : 1 : g_assert_no_error (error);
731 : 1 : g_object_unref (addr);
732 : :
733 : 1 : data->server_addr = g_socket_get_local_address (data->server, &error);
734 : 1 : g_assert_no_error (error);
735 : :
736 : 1 : data->server_port = g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (data->server_addr));
737 : :
738 : 1 : g_socket_listen (data->server, &error);
739 : 1 : g_assert_no_error (error);
740 : :
741 : 1 : data->server_thread = g_thread_new ("server", echo_server_thread, data);
742 : 1 : }
743 : :
744 : :
745 : : /******************************************************************/
746 : : /* Now a GResolver implementation, so the can't-resolve test will */
747 : : /* pass even if you have an evil DNS-faking ISP. */
748 : : /******************************************************************/
749 : :
750 : : typedef GResolver GFakeResolver;
751 : : typedef GResolverClass GFakeResolverClass;
752 : :
753 : : static GType g_fake_resolver_get_type (void);
754 : 3 : G_DEFINE_TYPE (GFakeResolver, g_fake_resolver, G_TYPE_RESOLVER)
755 : :
756 : : static void
757 : 1 : g_fake_resolver_init (GFakeResolver *gtr)
758 : : {
759 : 1 : }
760 : :
761 : : static GList *
762 : 7 : g_fake_resolver_lookup_by_name (GResolver *resolver,
763 : : const gchar *hostname,
764 : : GCancellable *cancellable,
765 : : GError **error)
766 : : {
767 : 7 : if (!strcmp (hostname, "example.com"))
768 : 4 : return g_list_prepend (NULL, g_inet_address_new_from_string ("127.0.0.1"));
769 : : else
770 : : {
771 : : /* Anything else is expected to fail. */
772 : 3 : g_set_error (error,
773 : : G_RESOLVER_ERROR,
774 : : G_RESOLVER_ERROR_NOT_FOUND,
775 : : "Not found");
776 : 3 : return NULL;
777 : : }
778 : : }
779 : :
780 : : static void
781 : 4 : g_fake_resolver_lookup_by_name_async (GResolver *resolver,
782 : : const gchar *hostname,
783 : : GCancellable *cancellable,
784 : : GAsyncReadyCallback callback,
785 : : gpointer user_data)
786 : : {
787 : : GTask *task;
788 : :
789 : 4 : task = g_task_new (resolver, cancellable, callback, user_data);
790 : :
791 : 4 : if (!strcmp (hostname, "example.com"))
792 : : {
793 : : GList *result;
794 : :
795 : 0 : result = g_list_prepend (NULL, g_inet_address_new_from_string ("127.0.0.1"));
796 : 0 : g_task_return_pointer (task, result, (GDestroyNotify) g_resolver_free_addresses);
797 : : }
798 : : else
799 : : {
800 : 4 : g_task_return_new_error_literal (task,
801 : : G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND,
802 : : "Not found");
803 : : }
804 : 4 : g_object_unref (task);
805 : 4 : }
806 : :
807 : : static void
808 : 2 : g_fake_resolver_lookup_by_name_with_flags_async (GResolver *resolver,
809 : : const gchar *hostname,
810 : : GResolverNameLookupFlags flags,
811 : : GCancellable *cancellable,
812 : : GAsyncReadyCallback callback,
813 : : gpointer user_data)
814 : : {
815 : : /* Note this isn't a real implementation as it ignores the flags */
816 : 2 : g_fake_resolver_lookup_by_name_async (resolver,
817 : : hostname,
818 : : cancellable,
819 : : callback,
820 : : user_data);
821 : 2 : }
822 : :
823 : : static GList *
824 : 4 : g_fake_resolver_lookup_by_name_finish (GResolver *resolver,
825 : : GAsyncResult *result,
826 : : GError **error)
827 : : {
828 : 4 : return g_task_propagate_pointer (G_TASK (result), error);
829 : : }
830 : :
831 : : static void
832 : 1 : g_fake_resolver_class_init (GFakeResolverClass *fake_class)
833 : : {
834 : 1 : GResolverClass *resolver_class = G_RESOLVER_CLASS (fake_class);
835 : :
836 : 1 : resolver_class->lookup_by_name = g_fake_resolver_lookup_by_name;
837 : 1 : resolver_class->lookup_by_name_async = g_fake_resolver_lookup_by_name_async;
838 : 1 : resolver_class->lookup_by_name_finish = g_fake_resolver_lookup_by_name_finish;
839 : 1 : resolver_class->lookup_by_name_with_flags_async = g_fake_resolver_lookup_by_name_with_flags_async;
840 : 1 : resolver_class->lookup_by_name_with_flags_finish = g_fake_resolver_lookup_by_name_finish;
841 : 1 : }
842 : :
843 : :
844 : :
845 : : /****************************************/
846 : : /* We made it! Now for the actual test! */
847 : : /****************************************/
848 : :
849 : : static void
850 : 10 : setup_test (gpointer fixture,
851 : : gconstpointer user_data)
852 : : {
853 : 10 : }
854 : :
855 : : static void
856 : 26 : teardown_test (gpointer fixture,
857 : : gconstpointer user_data)
858 : : {
859 : 26 : g_clear_pointer (&last_proxies, g_strfreev);
860 : :
861 : 26 : g_clear_error (&proxy_a.last_error);
862 : 26 : g_clear_error (&proxy_b.last_error);
863 : 26 : }
864 : :
865 : :
866 : : static const gchar *testbuf = "0123456789abcdef";
867 : :
868 : : static void
869 : 14 : do_echo_test (GSocketConnection *conn)
870 : : {
871 : 14 : GIOStream *iostream = G_IO_STREAM (conn);
872 : 14 : GInputStream *istream = g_io_stream_get_input_stream (iostream);
873 : 14 : GOutputStream *ostream = g_io_stream_get_output_stream (iostream);
874 : : gssize nread;
875 : : gsize nwrote, total;
876 : : gchar buf[128];
877 : 14 : GError *error = NULL;
878 : :
879 : 14 : g_output_stream_write_all (ostream, testbuf, strlen (testbuf),
880 : : &nwrote, NULL, &error);
881 : 14 : g_assert_no_error (error);
882 : 14 : g_assert_cmpint (nwrote, ==, strlen (testbuf));
883 : :
884 : 28 : for (total = 0; total < nwrote; total += nread)
885 : : {
886 : 14 : nread = g_input_stream_read (istream,
887 : : buf + total, sizeof (buf) - total,
888 : : NULL, &error);
889 : 14 : g_assert_no_error (error);
890 : 14 : g_assert_cmpint (nread, >, 0);
891 : : }
892 : :
893 : 14 : buf[total] = '\0';
894 : 14 : g_assert_cmpstr (buf, ==, testbuf);
895 : 14 : }
896 : :
897 : : static void
898 : 7 : async_got_conn (GObject *source,
899 : : GAsyncResult *result,
900 : : gpointer user_data)
901 : : {
902 : 7 : GSocketConnection **conn = user_data;
903 : 7 : GError *error = NULL;
904 : :
905 : 7 : *conn = g_socket_client_connect_finish (G_SOCKET_CLIENT (source),
906 : : result, &error);
907 : 7 : g_assert_no_error (error);
908 : 7 : }
909 : :
910 : : static void
911 : 7 : async_got_error (GObject *source,
912 : : GAsyncResult *result,
913 : : gpointer user_data)
914 : : {
915 : 7 : GError **error = user_data;
916 : :
917 : 7 : g_assert (error != NULL && *error == NULL);
918 : 7 : g_socket_client_connect_finish (G_SOCKET_CLIENT (source),
919 : : result, error);
920 : 7 : g_assert (*error != NULL);
921 : 7 : }
922 : :
923 : : static void
924 : 1 : async_resolver_got_error (GObject *source,
925 : : GAsyncResult *result,
926 : : gpointer user_data)
927 : : {
928 : 1 : GError **error = user_data;
929 : :
930 : 1 : g_assert (error != NULL && *error == NULL);
931 : 1 : g_proxy_resolver_lookup_finish (G_PROXY_RESOLVER (source),
932 : : result, error);
933 : 1 : g_assert (*error != NULL);
934 : 1 : }
935 : :
936 : : static void
937 : 2 : assert_direct (GSocketConnection *conn)
938 : : {
939 : : GSocketAddress *addr;
940 : 2 : GError *error = NULL;
941 : :
942 : 2 : g_assert_cmpint (g_strv_length (last_proxies), ==, 1);
943 : 2 : g_assert_cmpstr (last_proxies[0], ==, "direct://");
944 : 2 : g_assert_no_error (proxy_a.last_error);
945 : 2 : g_assert_no_error (proxy_b.last_error);
946 : :
947 : 2 : addr = g_socket_connection_get_remote_address (conn, &error);
948 : 2 : g_assert_no_error (error);
949 : 2 : g_assert (addr != NULL && !G_IS_PROXY_ADDRESS (addr));
950 : 2 : g_object_unref (addr);
951 : :
952 : 2 : addr = g_socket_connection_get_local_address (conn, &error);
953 : 2 : g_assert_no_error (error);
954 : 2 : g_object_unref (addr);
955 : :
956 : 2 : g_assert (g_socket_connection_is_connected (conn));
957 : 2 : }
958 : :
959 : : static void
960 : 1 : test_direct_sync (gpointer fixture,
961 : : gconstpointer user_data)
962 : : {
963 : : GSocketConnection *conn;
964 : : gchar *uri;
965 : 1 : GError *error = NULL;
966 : :
967 : : /* The simple:// URI should not require any proxy. */
968 : :
969 : 1 : uri = g_strdup_printf ("simple://127.0.0.1:%u", server.server_port);
970 : 1 : conn = g_socket_client_connect_to_uri (client, uri, 0, NULL, &error);
971 : 1 : g_free (uri);
972 : 1 : g_assert_no_error (error);
973 : :
974 : 1 : assert_direct (conn);
975 : 1 : do_echo_test (conn);
976 : 1 : g_object_unref (conn);
977 : 1 : }
978 : :
979 : : static void
980 : 1 : test_direct_async (gpointer fixture,
981 : : gconstpointer user_data)
982 : : {
983 : : GSocketConnection *conn;
984 : : gchar *uri;
985 : :
986 : : /* The simple:// URI should not require any proxy. */
987 : 1 : uri = g_strdup_printf ("simple://127.0.0.1:%u", server.server_port);
988 : 1 : conn = NULL;
989 : 1 : g_socket_client_connect_to_uri_async (client, uri, 0, NULL,
990 : : async_got_conn, &conn);
991 : 1 : g_free (uri);
992 : 4 : while (conn == NULL)
993 : 3 : g_main_context_iteration (NULL, TRUE);
994 : :
995 : 1 : assert_direct (conn);
996 : 1 : do_echo_test (conn);
997 : 1 : g_object_unref (conn);
998 : 1 : }
999 : :
1000 : : static void
1001 : 2 : assert_single (GSocketConnection *conn)
1002 : : {
1003 : : GSocketAddress *addr;
1004 : : const gchar *proxy_uri;
1005 : : gushort proxy_port;
1006 : 2 : GError *error = NULL;
1007 : :
1008 : 2 : g_assert_cmpint (g_strv_length (last_proxies), ==, 2);
1009 : 2 : g_assert_cmpstr (last_proxies[0], ==, proxy_a.uri);
1010 : 2 : g_assert_cmpstr (last_proxies[1], ==, proxy_b.uri);
1011 : 2 : g_assert_no_error (proxy_a.last_error);
1012 : 2 : g_assert_no_error (proxy_b.last_error);
1013 : :
1014 : 2 : addr = g_socket_connection_get_remote_address (conn, &error);
1015 : 2 : g_assert_no_error (error);
1016 : 2 : g_assert (G_IS_PROXY_ADDRESS (addr));
1017 : 2 : proxy_uri = g_proxy_address_get_uri (G_PROXY_ADDRESS (addr));
1018 : 2 : g_assert_cmpstr (proxy_uri, ==, proxy_a.uri);
1019 : 2 : proxy_port = g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (addr));
1020 : 2 : g_assert_cmpint (proxy_port, ==, proxy_a.port);
1021 : :
1022 : 2 : g_object_unref (addr);
1023 : 2 : }
1024 : :
1025 : : static void
1026 : 1 : test_single_sync (gpointer fixture,
1027 : : gconstpointer user_data)
1028 : : {
1029 : : GSocketConnection *conn;
1030 : 1 : GError *error = NULL;
1031 : : gchar *uri;
1032 : :
1033 : : /* The alpha:// URI should be proxied via Proxy A */
1034 : 1 : uri = g_strdup_printf ("alpha://127.0.0.1:%u", server.server_port);
1035 : 1 : conn = g_socket_client_connect_to_uri (client, uri, 0, NULL, &error);
1036 : 1 : g_free (uri);
1037 : 1 : g_assert_no_error (error);
1038 : :
1039 : 1 : assert_single (conn);
1040 : :
1041 : 1 : do_echo_test (conn);
1042 : 1 : g_object_unref (conn);
1043 : 1 : }
1044 : :
1045 : : static void
1046 : 1 : test_single_async (gpointer fixture,
1047 : : gconstpointer user_data)
1048 : : {
1049 : : GSocketConnection *conn;
1050 : : gchar *uri;
1051 : :
1052 : : /* The alpha:// URI should be proxied via Proxy A */
1053 : 1 : uri = g_strdup_printf ("alpha://127.0.0.1:%u", server.server_port);
1054 : 1 : conn = NULL;
1055 : 1 : g_socket_client_connect_to_uri_async (client, uri, 0, NULL,
1056 : : async_got_conn, &conn);
1057 : 1 : g_free (uri);
1058 : 6 : while (conn == NULL)
1059 : 5 : g_main_context_iteration (NULL, TRUE);
1060 : :
1061 : 1 : assert_single (conn);
1062 : 1 : do_echo_test (conn);
1063 : 1 : g_object_unref (conn);
1064 : 1 : }
1065 : :
1066 : : static void
1067 : 2 : assert_multiple (GSocketConnection *conn)
1068 : : {
1069 : : GSocketAddress *addr;
1070 : : const gchar *proxy_uri;
1071 : : gushort proxy_port;
1072 : 2 : GError *error = NULL;
1073 : :
1074 : 2 : g_assert_cmpint (g_strv_length (last_proxies), ==, 2);
1075 : 2 : g_assert_cmpstr (last_proxies[0], ==, proxy_a.uri);
1076 : 2 : g_assert_cmpstr (last_proxies[1], ==, proxy_b.uri);
1077 : 2 : g_assert_error (proxy_a.last_error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED);
1078 : 2 : g_assert_no_error (proxy_b.last_error);
1079 : :
1080 : 2 : addr = g_socket_connection_get_remote_address (conn, &error);
1081 : 2 : g_assert_no_error (error);
1082 : 2 : g_assert (G_IS_PROXY_ADDRESS (addr));
1083 : 2 : proxy_uri = g_proxy_address_get_uri (G_PROXY_ADDRESS (addr));
1084 : 2 : g_assert_cmpstr (proxy_uri, ==, proxy_b.uri);
1085 : 2 : proxy_port = g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (addr));
1086 : 2 : g_assert_cmpint (proxy_port, ==, proxy_b.port);
1087 : :
1088 : 2 : g_object_unref (addr);
1089 : 2 : }
1090 : :
1091 : : static void
1092 : 1 : test_multiple_sync (gpointer fixture,
1093 : : gconstpointer user_data)
1094 : : {
1095 : : GSocketConnection *conn;
1096 : 1 : GError *error = NULL;
1097 : : gchar *uri;
1098 : :
1099 : : /* The beta:// URI should be proxied via Proxy B, after failing
1100 : : * via Proxy A.
1101 : : */
1102 : 1 : uri = g_strdup_printf ("beta://127.0.0.1:%u", server.server_port);
1103 : 1 : conn = g_socket_client_connect_to_uri (client, uri, 0, NULL, &error);
1104 : 1 : g_free (uri);
1105 : 1 : g_assert_no_error (error);
1106 : :
1107 : 1 : assert_multiple (conn);
1108 : 1 : do_echo_test (conn);
1109 : 1 : g_object_unref (conn);
1110 : 1 : }
1111 : :
1112 : : static void
1113 : 1 : test_multiple_async (gpointer fixture,
1114 : : gconstpointer user_data)
1115 : : {
1116 : : GSocketConnection *conn;
1117 : : gchar *uri;
1118 : :
1119 : : /* The beta:// URI should be proxied via Proxy B, after failing
1120 : : * via Proxy A.
1121 : : */
1122 : 1 : uri = g_strdup_printf ("beta://127.0.0.1:%u", server.server_port);
1123 : 1 : conn = NULL;
1124 : 1 : g_socket_client_connect_to_uri_async (client, uri, 0, NULL,
1125 : : async_got_conn, &conn);
1126 : 1 : g_free (uri);
1127 : 10 : while (conn == NULL)
1128 : 9 : g_main_context_iteration (NULL, TRUE);
1129 : :
1130 : 1 : assert_multiple (conn);
1131 : 1 : do_echo_test (conn);
1132 : 1 : g_object_unref (conn);
1133 : 1 : }
1134 : :
1135 : : static void
1136 : 1 : test_invalid_uris_sync (gpointer fixture,
1137 : : gconstpointer user_data)
1138 : : {
1139 : : GSocketConnection *conn;
1140 : : gchar *uri;
1141 : 1 : GError *error = NULL;
1142 : :
1143 : 1 : g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2597");
1144 : :
1145 : : /* The empty:// URI causes the proxy resolver to return an empty string. */
1146 : 1 : uri = g_strdup_printf ("empty://127.0.0.1:%u", server.server_port);
1147 : 1 : conn = g_socket_client_connect_to_uri (client, uri, 0, NULL, &error);
1148 : 1 : g_free (uri);
1149 : 1 : g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED);
1150 : 1 : g_assert_null (conn);
1151 : 1 : g_clear_error (&error);
1152 : 1 : g_clear_pointer (&last_proxies, g_strfreev);
1153 : :
1154 : : /* The invalid:// URI causes the proxy resolver to return a cat emoji. */
1155 : 1 : uri = g_strdup_printf ("invalid://127.0.0.1:%u", server.server_port);
1156 : 1 : conn = g_socket_client_connect_to_uri (client, uri, 0, NULL, &error);
1157 : 1 : g_free (uri);
1158 : 1 : g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED);
1159 : 1 : g_assert_null (conn);
1160 : 1 : g_clear_error (&error);
1161 : 1 : g_clear_pointer (&last_proxies, g_strfreev);
1162 : :
1163 : : /* If the proxy resolver returns an invalid URI before a valid URI,
1164 : : * we should succeed.
1165 : : */
1166 : 1 : uri = g_strdup_printf ("invalid-then-simple://127.0.0.1:%u", server.server_port);
1167 : 1 : conn = g_socket_client_connect_to_uri (client, uri, 0, NULL, &error);
1168 : 1 : g_free (uri);
1169 : 1 : g_assert_no_error (error);
1170 : 1 : do_echo_test (conn);
1171 : 1 : g_object_unref (conn);
1172 : 1 : g_clear_pointer (&last_proxies, g_strfreev);
1173 : :
1174 : : /* If the proxy resolver returns a valid URI before an invalid URI,
1175 : : * we should succeed.
1176 : : */
1177 : 1 : uri = g_strdup_printf ("simple-then-invalid://127.0.0.1:%u", server.server_port);
1178 : 1 : conn = g_socket_client_connect_to_uri (client, uri, 0, NULL, &error);
1179 : 1 : g_free (uri);
1180 : 1 : g_assert_no_error (error);
1181 : 1 : do_echo_test (conn);
1182 : 1 : g_object_unref (conn);
1183 : 1 : g_clear_pointer (&last_proxies, g_strfreev);
1184 : :
1185 : : /* Trying to use something that is not a URI at all should fail. */
1186 : 1 : conn = g_socket_client_connect_to_uri (client, "asdf", 0, NULL, &error);
1187 : 1 : g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
1188 : 1 : g_clear_error (&error);
1189 : 1 : g_clear_pointer (&last_proxies, g_strfreev);
1190 : :
1191 : : /* Should still fail if using GProxyResolver directly. */
1192 : 1 : g_proxy_resolver_lookup (g_proxy_resolver_get_default (), "asdf", NULL, &error);
1193 : 1 : g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
1194 : 1 : g_clear_error (&error);
1195 : 1 : }
1196 : :
1197 : : static void
1198 : 1 : test_invalid_uris_async (gpointer fixture,
1199 : : gconstpointer user_data)
1200 : : {
1201 : 1 : GSocketConnection *conn = NULL;
1202 : 1 : GError *error = NULL;
1203 : : gchar *uri;
1204 : :
1205 : 1 : g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2597");
1206 : :
1207 : : /* The empty:// URI causes the proxy resolver to return an empty string. */
1208 : 1 : uri = g_strdup_printf ("empty://127.0.0.1:%u", server.server_port);
1209 : 1 : g_socket_client_connect_to_uri_async (client, uri, 0, NULL,
1210 : : async_got_error, &error);
1211 : 1 : g_free (uri);
1212 : 2 : while (error == NULL)
1213 : 1 : g_main_context_iteration (NULL, TRUE);
1214 : 1 : g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED);
1215 : 1 : g_clear_error (&error);
1216 : 1 : g_clear_pointer (&last_proxies, g_strfreev);
1217 : :
1218 : : /* The invalid:// URI causes the proxy resolver to return a cat emoji. */
1219 : 1 : uri = g_strdup_printf ("invalid://127.0.0.1:%u", server.server_port);
1220 : 1 : g_socket_client_connect_to_uri_async (client, uri, 0, NULL,
1221 : : async_got_error, &error);
1222 : 1 : g_free (uri);
1223 : 2 : while (error == NULL)
1224 : 1 : g_main_context_iteration (NULL, TRUE);
1225 : 1 : g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED);
1226 : 1 : g_clear_error (&error);
1227 : 1 : g_clear_pointer (&last_proxies, g_strfreev);
1228 : :
1229 : : /* If the proxy resolver returns an invalid URI before a valid URI,
1230 : : * we should succeed.
1231 : : */
1232 : 1 : uri = g_strdup_printf ("invalid-then-simple://127.0.0.1:%u", server.server_port);
1233 : 1 : g_socket_client_connect_to_uri_async (client, uri, 0, NULL,
1234 : : async_got_conn, &conn);
1235 : 1 : g_free (uri);
1236 : 4 : while (conn == NULL)
1237 : 3 : g_main_context_iteration (NULL, TRUE);
1238 : 1 : do_echo_test (conn);
1239 : 1 : g_clear_object (&conn);
1240 : 1 : g_clear_pointer (&last_proxies, g_strfreev);
1241 : :
1242 : : /* If the proxy resolver returns a valid URI before an invalid URI,
1243 : : * we should succeed.
1244 : : */
1245 : 1 : uri = g_strdup_printf ("simple-then-invalid://127.0.0.1:%u", server.server_port);
1246 : 1 : g_socket_client_connect_to_uri_async (client, uri, 0, NULL,
1247 : : async_got_conn, &conn);
1248 : 1 : g_free (uri);
1249 : 4 : while (conn == NULL)
1250 : 3 : g_main_context_iteration (NULL, TRUE);
1251 : 1 : do_echo_test (conn);
1252 : 1 : g_clear_object (&conn);
1253 : 1 : g_clear_pointer (&last_proxies, g_strfreev);
1254 : :
1255 : : /* Trying to use something that is not a URI at all should fail. */
1256 : 1 : g_socket_client_connect_to_uri_async (client, "asdf", 0, NULL,
1257 : : async_got_error, &error);
1258 : 2 : while (error == NULL)
1259 : 1 : g_main_context_iteration (NULL, TRUE);
1260 : 1 : g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
1261 : 1 : g_clear_error (&error);
1262 : 1 : g_clear_pointer (&last_proxies, g_strfreev);
1263 : :
1264 : : /* Should still fail if using GProxyResolver directly. */
1265 : 1 : g_proxy_resolver_lookup_async (g_proxy_resolver_get_default (), "asdf", NULL,
1266 : : async_resolver_got_error, &error);
1267 : 2 : while (error == NULL)
1268 : 1 : g_main_context_iteration (NULL, TRUE);
1269 : 1 : g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
1270 : 1 : g_clear_error (&error);
1271 : 1 : }
1272 : :
1273 : : static void
1274 : 1 : test_dns (gpointer fixture,
1275 : : gconstpointer user_data)
1276 : : {
1277 : : GSocketConnection *conn;
1278 : 1 : GError *error = NULL;
1279 : : gchar *uri;
1280 : :
1281 : : /* The simple:// and alpha:// URIs should fail with a DNS error,
1282 : : * but the beta:// URI should succeed, because we pass it to
1283 : : * Proxy B without trying to resolve it first
1284 : : */
1285 : :
1286 : : /* simple */
1287 : 1 : uri = g_strdup_printf ("simple://no-such-host.xx:%u", server.server_port);
1288 : 1 : conn = g_socket_client_connect_to_uri (client, uri, 0, NULL, &error);
1289 : 1 : g_assert_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND);
1290 : 1 : g_clear_error (&error);
1291 : :
1292 : 1 : g_assert_no_error (proxy_a.last_error);
1293 : 1 : g_assert_no_error (proxy_b.last_error);
1294 : 1 : teardown_test (NULL, NULL);
1295 : :
1296 : 1 : g_socket_client_connect_to_uri_async (client, uri, 0, NULL,
1297 : : async_got_error, &error);
1298 : 3 : while (error == NULL)
1299 : 2 : g_main_context_iteration (NULL, TRUE);
1300 : 1 : g_assert_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND);
1301 : 1 : g_clear_error (&error);
1302 : 1 : g_free (uri);
1303 : :
1304 : 1 : g_assert_no_error (proxy_a.last_error);
1305 : 1 : g_assert_no_error (proxy_b.last_error);
1306 : 1 : teardown_test (NULL, NULL);
1307 : :
1308 : : /* alpha */
1309 : 1 : uri = g_strdup_printf ("alpha://no-such-host.xx:%u", server.server_port);
1310 : 1 : conn = g_socket_client_connect_to_uri (client, uri, 0, NULL, &error);
1311 : : /* Since Proxy A fails, @client will try Proxy B too, which won't
1312 : : * load an alpha:// URI.
1313 : : */
1314 : 1 : g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED);
1315 : 1 : g_clear_error (&error);
1316 : :
1317 : 1 : g_assert_no_error (proxy_a.last_error);
1318 : 1 : g_assert_error (proxy_b.last_error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED);
1319 : 1 : teardown_test (NULL, NULL);
1320 : :
1321 : 1 : g_socket_client_connect_to_uri_async (client, uri, 0, NULL,
1322 : : async_got_error, &error);
1323 : 8 : while (error == NULL)
1324 : 7 : g_main_context_iteration (NULL, TRUE);
1325 : 1 : g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED);
1326 : 1 : g_clear_error (&error);
1327 : 1 : g_free (uri);
1328 : :
1329 : 1 : g_assert_no_error (proxy_a.last_error);
1330 : 1 : g_assert_error (proxy_b.last_error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED);
1331 : 1 : teardown_test (NULL, NULL);
1332 : :
1333 : : /* beta */
1334 : 1 : uri = g_strdup_printf ("beta://no-such-host.xx:%u", server.server_port);
1335 : 1 : conn = g_socket_client_connect_to_uri (client, uri, 0, NULL, &error);
1336 : 1 : g_assert_no_error (error);
1337 : :
1338 : 1 : g_assert_no_error (proxy_a.last_error);
1339 : 1 : g_assert_no_error (proxy_b.last_error);
1340 : :
1341 : 1 : do_echo_test (conn);
1342 : 1 : g_clear_object (&conn);
1343 : 1 : teardown_test (NULL, NULL);
1344 : :
1345 : 1 : g_socket_client_connect_to_uri_async (client, uri, 0, NULL,
1346 : : async_got_conn, &conn);
1347 : 7 : while (conn == NULL)
1348 : 6 : g_main_context_iteration (NULL, TRUE);
1349 : 1 : g_free (uri);
1350 : :
1351 : 1 : g_assert_no_error (proxy_a.last_error);
1352 : 1 : g_assert_no_error (proxy_b.last_error);
1353 : :
1354 : 1 : do_echo_test (conn);
1355 : 1 : g_clear_object (&conn);
1356 : 1 : teardown_test (NULL, NULL);
1357 : 1 : }
1358 : :
1359 : : static void
1360 : 6 : assert_override (GSocketConnection *conn)
1361 : : {
1362 : 6 : g_assert_cmpint (g_strv_length (last_proxies), ==, 1);
1363 : 6 : g_assert_cmpstr (last_proxies[0], ==, proxy_a.uri);
1364 : :
1365 : 6 : if (conn)
1366 : 2 : g_assert_no_error (proxy_a.last_error);
1367 : : else
1368 : 4 : g_assert_error (proxy_a.last_error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED);
1369 : 6 : }
1370 : :
1371 : : static void
1372 : 1 : test_override (gpointer fixture,
1373 : : gconstpointer user_data)
1374 : : {
1375 : : GProxyResolver *alt_resolver;
1376 : : GSocketConnection *conn;
1377 : 1 : GError *error = NULL;
1378 : : gchar *uri;
1379 : :
1380 : 1 : g_assert (g_socket_client_get_proxy_resolver (client) == g_proxy_resolver_get_default ());
1381 : 1 : alt_resolver = g_object_new (g_test_alt_proxy_resolver_get_type (), NULL);
1382 : 1 : g_socket_client_set_proxy_resolver (client, alt_resolver);
1383 : 1 : g_assert (g_socket_client_get_proxy_resolver (client) == alt_resolver);
1384 : :
1385 : : /* Alt proxy resolver always returns Proxy A, so alpha:// should
1386 : : * succeed, and simple:// and beta:// should fail.
1387 : : */
1388 : :
1389 : : /* simple */
1390 : 1 : uri = g_strdup_printf ("simple://127.0.0.1:%u", server.server_port);
1391 : 1 : conn = g_socket_client_connect_to_uri (client, uri, 0, NULL, &error);
1392 : 1 : g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED);
1393 : 1 : g_clear_error (&error);
1394 : 1 : assert_override (conn);
1395 : 1 : teardown_test (NULL, NULL);
1396 : :
1397 : 1 : g_socket_client_connect_to_uri_async (client, uri, 0, NULL,
1398 : : async_got_error, &error);
1399 : 7 : while (error == NULL)
1400 : 6 : g_main_context_iteration (NULL, TRUE);
1401 : 1 : g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED);
1402 : 1 : g_clear_error (&error);
1403 : 1 : assert_override (conn);
1404 : 1 : g_free (uri);
1405 : 1 : teardown_test (NULL, NULL);
1406 : :
1407 : : /* alpha */
1408 : 1 : uri = g_strdup_printf ("alpha://127.0.0.1:%u", server.server_port);
1409 : 1 : conn = g_socket_client_connect_to_uri (client, uri, 0, NULL, &error);
1410 : 1 : g_assert_no_error (error);
1411 : 1 : assert_override (conn);
1412 : 1 : do_echo_test (conn);
1413 : 1 : g_clear_object (&conn);
1414 : 1 : teardown_test (NULL, NULL);
1415 : :
1416 : 1 : conn = NULL;
1417 : 1 : g_socket_client_connect_to_uri_async (client, uri, 0, NULL,
1418 : : async_got_conn, &conn);
1419 : 6 : while (conn == NULL)
1420 : 5 : g_main_context_iteration (NULL, TRUE);
1421 : 1 : assert_override (conn);
1422 : 1 : do_echo_test (conn);
1423 : 1 : g_clear_object (&conn);
1424 : 1 : g_free (uri);
1425 : 1 : teardown_test (NULL, NULL);
1426 : :
1427 : : /* beta */
1428 : 1 : uri = g_strdup_printf ("beta://127.0.0.1:%u", server.server_port);
1429 : 1 : conn = g_socket_client_connect_to_uri (client, uri, 0, NULL, &error);
1430 : 1 : g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED);
1431 : 1 : g_clear_error (&error);
1432 : 1 : assert_override (conn);
1433 : 1 : teardown_test (NULL, NULL);
1434 : :
1435 : 1 : g_socket_client_connect_to_uri_async (client, uri, 0, NULL,
1436 : : async_got_error, &error);
1437 : 7 : while (error == NULL)
1438 : 6 : g_main_context_iteration (NULL, TRUE);
1439 : 1 : g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED);
1440 : 1 : g_clear_error (&error);
1441 : 1 : assert_override (conn);
1442 : 1 : g_free (uri);
1443 : 1 : teardown_test (NULL, NULL);
1444 : :
1445 : 1 : g_assert (g_socket_client_get_proxy_resolver (client) == alt_resolver);
1446 : 1 : g_socket_client_set_proxy_resolver (client, NULL);
1447 : 1 : g_assert (g_socket_client_get_proxy_resolver (client) == g_proxy_resolver_get_default ());
1448 : 1 : g_object_unref (alt_resolver);
1449 : 1 : }
1450 : :
1451 : : static void
1452 : 4 : assert_destination_port (GSocketAddressEnumerator *etor,
1453 : : guint16 port)
1454 : : {
1455 : : GSocketAddress *addr;
1456 : : GProxyAddress *paddr;
1457 : 4 : GError *error = NULL;
1458 : :
1459 : 12 : while ((addr = g_socket_address_enumerator_next (etor, NULL, &error)))
1460 : : {
1461 : 8 : g_assert_no_error (error);
1462 : :
1463 : 8 : g_assert (G_IS_PROXY_ADDRESS (addr));
1464 : 8 : paddr = G_PROXY_ADDRESS (addr);
1465 : 8 : g_assert_cmpint (g_proxy_address_get_destination_port (paddr), ==, port);
1466 : 8 : g_object_unref (addr);
1467 : : }
1468 : 4 : g_assert_no_error (error);
1469 : 4 : }
1470 : :
1471 : : static void
1472 : 1 : test_proxy_enumerator_ports (void)
1473 : : {
1474 : : GSocketAddressEnumerator *etor;
1475 : :
1476 : 1 : etor = g_object_new (G_TYPE_PROXY_ADDRESS_ENUMERATOR,
1477 : : "uri", "http://example.com/",
1478 : : NULL);
1479 : 1 : assert_destination_port (etor, 0);
1480 : 1 : g_object_unref (etor);
1481 : :
1482 : : /* Have to call this to clear last_proxies so the next call to
1483 : : * g_test_proxy_resolver_lookup() won't assert.
1484 : : */
1485 : 1 : teardown_test (NULL, NULL);
1486 : :
1487 : 1 : etor = g_object_new (G_TYPE_PROXY_ADDRESS_ENUMERATOR,
1488 : : "uri", "http://example.com:8080/",
1489 : : NULL);
1490 : 1 : assert_destination_port (etor, 8080);
1491 : 1 : g_object_unref (etor);
1492 : :
1493 : 1 : teardown_test (NULL, NULL);
1494 : :
1495 : 1 : etor = g_object_new (G_TYPE_PROXY_ADDRESS_ENUMERATOR,
1496 : : "uri", "http://example.com/",
1497 : : "default-port", 80,
1498 : : NULL);
1499 : 1 : assert_destination_port (etor, 80);
1500 : 1 : g_object_unref (etor);
1501 : :
1502 : 1 : teardown_test (NULL, NULL);
1503 : :
1504 : 1 : etor = g_object_new (G_TYPE_PROXY_ADDRESS_ENUMERATOR,
1505 : : "uri", "http://example.com:8080/",
1506 : : "default-port", 80,
1507 : : NULL);
1508 : 1 : assert_destination_port (etor, 8080);
1509 : 1 : g_object_unref (etor);
1510 : :
1511 : 1 : teardown_test (NULL, NULL);
1512 : 1 : }
1513 : :
1514 : : int
1515 : 1 : main (int argc,
1516 : : char *argv[])
1517 : : {
1518 : : GResolver *fake_resolver;
1519 : : GCancellable *cancellable;
1520 : : gint result;
1521 : :
1522 : 1 : g_test_init (&argc, &argv, NULL);
1523 : :
1524 : : /* Register stuff. The dummy g_proxy_get_default_for_protocol() call
1525 : : * is to force _g_io_modules_ensure_extension_points_registered() to
1526 : : * get called, so we can then register a proxy resolver extension
1527 : : * point.
1528 : : */
1529 : 1 : g_proxy_get_default_for_protocol ("foo");
1530 : 1 : g_test_proxy_resolver_get_type ();
1531 : 1 : g_proxy_a_get_type ();
1532 : 1 : g_proxy_b_get_type ();
1533 : 1 : g_setenv ("GIO_USE_PROXY_RESOLVER", "test", TRUE);
1534 : :
1535 : 1 : fake_resolver = g_object_new (g_fake_resolver_get_type (), NULL);
1536 : 1 : g_resolver_set_default (fake_resolver);
1537 : :
1538 : 1 : cancellable = g_cancellable_new ();
1539 : 1 : create_server (&server, cancellable);
1540 : 1 : create_proxy (&proxy_a, 'a', "alpha", cancellable);
1541 : 1 : create_proxy (&proxy_b, 'b', "beta", cancellable);
1542 : :
1543 : 1 : client = g_socket_client_new ();
1544 : 1 : g_assert_cmpint (g_socket_client_get_enable_proxy (client), ==, TRUE);
1545 : :
1546 : 1 : g_test_add_vtable ("/proxy/direct_sync", 0, NULL, setup_test, test_direct_sync, teardown_test);
1547 : 1 : g_test_add_vtable ("/proxy/direct_async", 0, NULL, setup_test, test_direct_async, teardown_test);
1548 : 1 : g_test_add_vtable ("/proxy/single_sync", 0, NULL, setup_test, test_single_sync, teardown_test);
1549 : 1 : g_test_add_vtable ("/proxy/single_async", 0, NULL, setup_test, test_single_async, teardown_test);
1550 : 1 : g_test_add_vtable ("/proxy/multiple_sync", 0, NULL, setup_test, test_multiple_sync, teardown_test);
1551 : 1 : g_test_add_vtable ("/proxy/multiple_async", 0, NULL, setup_test, test_multiple_async, teardown_test);
1552 : 1 : g_test_add_vtable ("/proxy/invalid-uris-sync", 0, NULL, setup_test, test_invalid_uris_sync, teardown_test);
1553 : 1 : g_test_add_vtable ("/proxy/invalid-uris-async", 0, NULL, setup_test, test_invalid_uris_async, teardown_test);
1554 : 1 : g_test_add_vtable ("/proxy/dns", 0, NULL, setup_test, test_dns, teardown_test);
1555 : 1 : g_test_add_vtable ("/proxy/override", 0, NULL, setup_test, test_override, teardown_test);
1556 : 1 : g_test_add_func ("/proxy/enumerator-ports", test_proxy_enumerator_ports);
1557 : :
1558 : 1 : result = g_test_run();
1559 : :
1560 : 1 : g_object_unref (client);
1561 : :
1562 : 1 : g_cancellable_cancel (cancellable);
1563 : 1 : g_thread_join (proxy_a.thread);
1564 : 1 : g_thread_join (proxy_b.thread);
1565 : 1 : g_thread_join (server.server_thread);
1566 : :
1567 : 1 : g_object_unref (cancellable);
1568 : :
1569 : 1 : return result;
1570 : : }
|