LCOV - code coverage report
Current view: top level - gio/tests - gsocketclient-slow.c (source / functions) Coverage Total Hit
Test: unnamed Lines: 100.0 % 123 123
Test Date: 2024-11-26 05:23:01 Functions: 100.0 % 9 9
Branches: - 0 0

             Branch data     Line data    Source code
       1                 :             : /* GIO - GLib Input, Output and Streaming Library
       2                 :             :  *
       3                 :             :  * Copyright (C) 2018 Igalia S.L.
       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 <gio/gio.h>
      22                 :             : #include <gio/gnetworking.h> /* For IPV6_V6ONLY */
      23                 :             : 
      24                 :             : static void
      25                 :           1 : on_connected (GObject      *source_object,
      26                 :             :               GAsyncResult *result,
      27                 :             :               gpointer      user_data)
      28                 :             : {
      29                 :             :   GSocketConnection *conn;
      30                 :           1 :   GError *error = NULL;
      31                 :             : 
      32                 :           1 :   conn = g_socket_client_connect_to_uri_finish (G_SOCKET_CLIENT (source_object), result, &error);
      33                 :           1 :   g_assert_no_error (error);
      34                 :             : 
      35                 :           1 :   g_object_unref (conn);
      36                 :           1 :   g_main_loop_quit (user_data);
      37                 :           1 : }
      38                 :             : 
      39                 :             : static void
      40                 :           1 : test_happy_eyeballs (void)
      41                 :             : {
      42                 :             :   GSocketClient *client;
      43                 :             :   GSocketService *service;
      44                 :           1 :   GError *error = NULL;
      45                 :             :   guint16 port;
      46                 :             :   GMainLoop *loop;
      47                 :             : 
      48                 :           1 :   loop = g_main_loop_new (NULL, FALSE);
      49                 :             : 
      50                 :           1 :   service = g_socket_service_new ();
      51                 :           1 :   port = g_socket_listener_add_any_inet_port (G_SOCKET_LISTENER (service), NULL, &error);
      52                 :           1 :   g_assert_no_error (error);
      53                 :           1 :   g_socket_service_start (service);
      54                 :             : 
      55                 :             :   /* All of the magic here actually happens in slow-connect-preload.c
      56                 :             :    * which as you would guess is preloaded. So this is just making a
      57                 :             :    * normal connection that happens to take 600ms each time. This will
      58                 :             :    * trigger the logic to make multiple parallel connections.
      59                 :             :    */
      60                 :           1 :   client = g_socket_client_new ();
      61                 :           1 :   g_socket_client_connect_to_host_async (client, "localhost", port, NULL, on_connected, loop);
      62                 :           1 :   g_main_loop_run (loop);
      63                 :             : 
      64                 :           1 :   g_main_loop_unref (loop);
      65                 :           1 :   g_object_unref (service);
      66                 :           1 :   g_object_unref (client);
      67                 :           1 : }
      68                 :             : 
      69                 :             : static void
      70                 :           2 : on_connected_cancelled (GObject      *source_object,
      71                 :             :                         GAsyncResult *result,
      72                 :             :                         gpointer      user_data)
      73                 :             : {
      74                 :             :   GSocketConnection *conn;
      75                 :           2 :   GError *error = NULL;
      76                 :             : 
      77                 :           2 :   conn = g_socket_client_connect_to_uri_finish (G_SOCKET_CLIENT (source_object), result, &error);
      78                 :           2 :   g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
      79                 :           2 :   g_assert_null (conn);
      80                 :             : 
      81                 :           2 :   g_error_free (error);
      82                 :           2 :   g_main_loop_quit (user_data);
      83                 :           2 : }
      84                 :             : 
      85                 :             : typedef struct
      86                 :             : {
      87                 :             :   GCancellable *cancellable;
      88                 :             :   gboolean completed;
      89                 :             : } EventCallbackData;
      90                 :             : 
      91                 :             : static void
      92                 :           5 : on_event (GSocketClient      *client,
      93                 :             :           GSocketClientEvent  event,
      94                 :             :           GSocketConnectable *connectable,
      95                 :             :           GIOStream          *connection,
      96                 :             :           EventCallbackData  *data)
      97                 :             : {
      98                 :           5 :   if (data->cancellable && event == G_SOCKET_CLIENT_CONNECTED)
      99                 :             :     {
     100                 :           1 :       g_cancellable_cancel (data->cancellable);
     101                 :             :     }
     102                 :           4 :   else if (event == G_SOCKET_CLIENT_COMPLETE)
     103                 :             :     {
     104                 :           2 :       data->completed = TRUE;
     105                 :           2 :       g_assert_null (connection);
     106                 :             :     }
     107                 :           5 : }
     108                 :             : 
     109                 :             : static void
     110                 :           1 : test_happy_eyeballs_cancel_delayed (void)
     111                 :             : {
     112                 :             :   GSocketClient *client;
     113                 :             :   GSocketService *service;
     114                 :           1 :   GError *error = NULL;
     115                 :             :   guint16 port;
     116                 :             :   GMainLoop *loop;
     117                 :           1 :   EventCallbackData data = { NULL, FALSE };
     118                 :             : 
     119                 :             :   /* This just tests that cancellation works as expected, still emits the completed signal,
     120                 :             :    * and never returns a connection */
     121                 :             : 
     122                 :           1 :   loop = g_main_loop_new (NULL, FALSE);
     123                 :             : 
     124                 :           1 :   service = g_socket_service_new ();
     125                 :           1 :   port = g_socket_listener_add_any_inet_port (G_SOCKET_LISTENER (service), NULL, &error);
     126                 :           1 :   g_assert_no_error (error);
     127                 :           1 :   g_socket_service_start (service);
     128                 :             : 
     129                 :           1 :   client = g_socket_client_new ();
     130                 :           1 :   data.cancellable = g_cancellable_new ();
     131                 :           1 :   g_socket_client_connect_to_host_async (client, "localhost", port, data.cancellable, on_connected_cancelled, loop);
     132                 :           1 :   g_signal_connect (client, "event", G_CALLBACK (on_event), &data);
     133                 :           1 :   g_main_loop_run (loop);
     134                 :             : 
     135                 :           1 :   g_assert_true (data.completed);
     136                 :           1 :   g_main_loop_unref (loop);
     137                 :           1 :   g_object_unref (service);
     138                 :           1 :   g_object_unref (client);
     139                 :           1 :   g_object_unref (data.cancellable);
     140                 :           1 : }
     141                 :             : 
     142                 :             : static void
     143                 :           1 : test_happy_eyeballs_cancel_instant (void)
     144                 :             : {
     145                 :             :   GSocketClient *client;
     146                 :             :   GSocketService *service;
     147                 :           1 :   GError *error = NULL;
     148                 :             :   guint16 port;
     149                 :             :   GMainLoop *loop;
     150                 :             :   GCancellable *cancel;
     151                 :           1 :   EventCallbackData data = { NULL, FALSE };
     152                 :             : 
     153                 :             :   /* This tests the same things as above, test_happy_eyeballs_cancel_delayed(), but
     154                 :             :    * with different timing since it sends an already cancelled cancellable */
     155                 :             : 
     156                 :           1 :   loop = g_main_loop_new (NULL, FALSE);
     157                 :             : 
     158                 :           1 :   service = g_socket_service_new ();
     159                 :           1 :   port = g_socket_listener_add_any_inet_port (G_SOCKET_LISTENER (service), NULL, &error);
     160                 :           1 :   g_assert_no_error (error);
     161                 :           1 :   g_socket_service_start (service);
     162                 :             : 
     163                 :           1 :   client = g_socket_client_new ();
     164                 :           1 :   cancel = g_cancellable_new ();
     165                 :           1 :   g_cancellable_cancel (cancel);
     166                 :           1 :   g_socket_client_connect_to_host_async (client, "localhost", port, cancel, on_connected_cancelled, loop);
     167                 :           1 :   g_signal_connect (client, "event", G_CALLBACK (on_event), &data);
     168                 :           1 :   g_main_loop_run (loop);
     169                 :             : 
     170                 :           1 :   g_assert_true (data.completed);
     171                 :           1 :   g_main_loop_unref (loop);
     172                 :           1 :   g_object_unref (service);
     173                 :           1 :   g_object_unref (client);
     174                 :           1 :   g_object_unref (cancel);
     175                 :           1 : }
     176                 :             : 
     177                 :             : static void
     178                 :           1 : async_result_cb (GObject      *source,
     179                 :             :                  GAsyncResult *res,
     180                 :             :                  gpointer      user_data)
     181                 :             : {
     182                 :           1 :   GAsyncResult **result_out = user_data;
     183                 :             : 
     184                 :           1 :   g_assert_null (*result_out);
     185                 :           1 :   *result_out = g_object_ref (res);
     186                 :             : 
     187                 :           1 :   g_main_context_wakeup (NULL);
     188                 :           1 : }
     189                 :             : 
     190                 :             : static void
     191                 :           1 : test_connection_failed (void)
     192                 :             : {
     193                 :           1 :   GSocketClient *client = NULL;
     194                 :           1 :   GInetAddress *inet_address = NULL;
     195                 :           1 :   GSocketAddress *address = NULL;
     196                 :           1 :   GSocket *socket = NULL;
     197                 :             :   guint16 port;
     198                 :           1 :   GAsyncResult *async_result = NULL;
     199                 :           1 :   GSocketConnection *conn = NULL;
     200                 :           1 :   GError *local_error = NULL;
     201                 :             : 
     202                 :           1 :   g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/3184");
     203                 :             : 
     204                 :           1 :   inet_address = g_inet_address_new_any (G_SOCKET_FAMILY_IPV6);
     205                 :           1 :   address = g_inet_socket_address_new (inet_address, 0);
     206                 :           1 :   g_object_unref (inet_address);
     207                 :             : 
     208                 :           1 :   socket = g_socket_new (G_SOCKET_FAMILY_IPV6, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_TCP, &local_error);
     209                 :           1 :   g_assert_no_error (local_error);
     210                 :           1 :   g_socket_set_option (socket, IPPROTO_IPV6, IPV6_V6ONLY, FALSE, NULL);
     211                 :           1 :   g_socket_bind (socket, address, TRUE, &local_error);
     212                 :           1 :   g_assert_no_error (local_error);
     213                 :             : 
     214                 :             :   /* reserve a port without listening so we know that connecting to it will fail */
     215                 :           1 :   g_object_unref (address);
     216                 :           1 :   address = g_socket_get_local_address (socket, &local_error);
     217                 :           1 :   g_assert_no_error (local_error);
     218                 :           1 :   port = g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (address));
     219                 :             : 
     220                 :           1 :   client = g_socket_client_new ();
     221                 :             :   /* Connect to the port we have reserved but do not listen to. Because of the slow connection
     222                 :             :    * caused by slow-connect-preload.c and the fact that we try to connect to both IPv4 and IPv6
     223                 :             :    * we will in some way exercise the code path in try_next_connection_or_finish() that ends
     224                 :             :    * with a call to complete_connection_with_error(). This path previously had a memory leak.
     225                 :             :    * Note that the slowness is important, because without it we could bail out already in the
     226                 :             :    * address enumeration phase because it finishes when there are no connection attempts in
     227                 :             :    * progress. */
     228                 :           1 :   g_socket_client_connect_to_host_async (client, "localhost", port, NULL, async_result_cb, &async_result);
     229                 :             : 
     230                 :           6 :   while (async_result == NULL)
     231                 :           5 :     g_main_context_iteration (NULL, TRUE);
     232                 :             : 
     233                 :           1 :   conn = g_socket_client_connect_to_uri_finish (client, async_result, &local_error);
     234                 :           1 :   g_assert_nonnull (local_error);
     235                 :           1 :   g_assert_cmpint (local_error->domain, ==, G_IO_ERROR);
     236                 :           1 :   g_assert_null (conn);
     237                 :           1 :   g_clear_error (&local_error);
     238                 :           1 :   g_clear_object (&async_result);
     239                 :             : 
     240                 :           1 :   g_clear_object (&client);
     241                 :           1 :   g_clear_object (&socket);
     242                 :           1 :   g_clear_object (&address);
     243                 :           1 : }
     244                 :             : 
     245                 :             : int
     246                 :           1 : main (int argc, char *argv[])
     247                 :             : {
     248                 :           1 :   g_test_init (&argc, &argv, NULL);
     249                 :             : 
     250                 :           1 :   g_test_add_func ("/socket-client/happy-eyeballs/slow", test_happy_eyeballs);
     251                 :           1 :   g_test_add_func ("/socket-client/happy-eyeballs/cancellation/instant", test_happy_eyeballs_cancel_instant);
     252                 :           1 :   g_test_add_func ("/socket-client/happy-eyeballs/cancellation/delayed", test_happy_eyeballs_cancel_delayed);
     253                 :           1 :   g_test_add_func ("/socket-client/connection-fail", test_connection_failed);
     254                 :             : 
     255                 :           1 :   return g_test_run ();
     256                 :             : }
        

Generated by: LCOV version 2.0-1