From 7cb953dab8d0f3a83897265210fede781b5a45fd Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 2 Oct 2023 18:22:02 +0100 Subject: [PATCH 1/5] Add GBytes variants for GSocket receive methods The current buffer API is pretty much C-specific, and cannot be adequately described in a way that is friendly to introspection and language bindings: the passed buffer is allocated by the caller, but the written size of the buffer is in the return value. Using GBytes, we get a better API at the cost of an additional allocation. --- gio/gsocket.c | 142 ++++++++++++++++++++++++++++++++++++++++++++++++++ gio/gsocket.h | 13 +++++ 2 files changed, 155 insertions(+) diff --git a/gio/gsocket.c b/gio/gsocket.c index 2609150d8..c2ddadeaf 100644 --- a/gio/gsocket.c +++ b/gio/gsocket.c @@ -3365,6 +3365,69 @@ g_socket_receive_with_timeout (GSocket *socket, return ret; } +/** + * g_socket_receive_bytes: + * @socket: a #GSocket + * @size: the number of bytes you want to read from the socket + * @timeout_us: the timeout to wait for, in microseconds, or `-1` to block + * indefinitely + * @cancellable: (nullable): a %GCancellable, or `NULL` + * @error: return location for a #GError, or `NULL` + * + * Receives data (up to @size bytes) from a socket. + * + * This function is a variant of [method@Gio.Socket.receive] which returns a + * [struct@GLib.Bytes] rather than a plain buffer. + * + * Pass `-1` to @timeout_us to block indefinitely until data is received (or + * the connection is closed, or there is an error). Pass `0` to use the default + * timeout from [property@Gio.Socket:timeout], or pass a positive number to wait + * for that many microseconds for data before returning `G_IO_ERROR_TIMED_OUT`. + * + * Returns: (transfer full): a bytes buffer containing the + * received bytes, or `NULL` on error + * Since: 2.80 + */ +GBytes * +g_socket_receive_bytes (GSocket *socket, + gsize size, + gint64 timeout_us, + GCancellable *cancellable, + GError **error) +{ + guint8 *data; + gssize res; + GBytes *buf; + + g_return_val_if_fail (G_IS_SOCKET (socket), NULL); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + data = g_new0 (guint8, size); + res = g_socket_receive_with_timeout (socket, data, size, timeout_us, cancellable, error); + if (res < 0) + { + g_free (data); + return NULL; + } + + if ((gsize) res == size) + { + buf = g_bytes_new_take (g_steal_pointer (&data), (gsize) res); + } + else + { + GBytes *sub_buf; + + buf = g_bytes_new_take (g_steal_pointer (&data), size); + sub_buf = g_bytes_new_from_bytes (buf, 0, (gsize) res); + g_bytes_unref (buf); + buf = g_steal_pointer (&sub_buf); + } + + return g_steal_pointer (&buf); +} + /** * g_socket_receive: * @socket: a #GSocket @@ -3446,6 +3509,85 @@ g_socket_receive_with_blocking (GSocket *socket, blocking ? -1 : 0, cancellable, error); } +/** + * g_socket_receive_bytes_from: + * @socket: a #GSocket + * @address: (out) (optional): return location for a #GSocketAddress + * @size: the number of bytes you want to read from the socket + * @timeout_us: the timeout to wait for, in microseconds, or `-1` to block + * indefinitely + * @cancellable: (nullable): a #GCancellable, or `NULL` + * @error: return location for a #GError, or `NULL` + * + * Receive data (up to @size bytes) from a socket. + * + * This function is a variant of [method@Gio.Socket.receive_from] which returns + * a [struct@GLib.Bytes] rather than a plain buffer. + * + * If @address is non-%NULL then @address will be set equal to the + * source address of the received packet. + * + * The @address is owned by the caller. + * + * Pass `-1` to @timeout_us to block indefinitely until data is received (or + * the connection is closed, or there is an error). Pass `0` to use the default + * timeout from [property@Gio.Socket:timeout], or pass a positive number to wait + * for that many microseconds for data before returning `G_IO_ERROR_TIMED_OUT`. + * + * Returns: (transfer full): a bytes buffer containing the + * received bytes, or `NULL` on error + * Since: 2.80 + */ +GBytes * +g_socket_receive_bytes_from (GSocket *socket, + GSocketAddress **address, + gsize size, + gint64 timeout_us, + GCancellable *cancellable, + GError **error) +{ + GInputVector v; + gssize res; + GBytes *buf; + + g_return_val_if_fail (G_IS_SOCKET (socket), NULL); + g_return_val_if_fail (address == NULL || *address == NULL, NULL); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + v.buffer = g_new0 (guint8, size); + v.size = size; + + res = g_socket_receive_message_with_timeout (socket, + address, + &v, 1, + NULL, 0, NULL, + timeout_us, + cancellable, + error); + if (res < 0) + { + g_free (v.buffer); + return NULL; + } + + if ((gsize) res == size) + { + buf = g_bytes_new_take (g_steal_pointer (&v.buffer), (gsize) res); + } + else + { + GBytes *sub_buf; + + buf = g_bytes_new_take (g_steal_pointer (&v.buffer), size); + sub_buf = g_bytes_new_from_bytes (buf, 0, (gsize) res); + g_bytes_unref (buf); + buf = g_steal_pointer (&sub_buf); + } + + return g_steal_pointer (&buf); +} + /** * g_socket_receive_from: * @socket: a #GSocket diff --git a/gio/gsocket.h b/gio/gsocket.h index 0f0624be5..341e23e3b 100644 --- a/gio/gsocket.h +++ b/gio/gsocket.h @@ -210,6 +210,12 @@ gssize g_socket_receive (GSocket gsize size, GCancellable *cancellable, GError **error); +GIO_AVAILABLE_IN_2_80 +GBytes * g_socket_receive_bytes (GSocket *socket, + gsize size, + gint64 timeout_us, + GCancellable *cancellable, + GError **error); GIO_AVAILABLE_IN_ALL gssize g_socket_receive_from (GSocket *socket, GSocketAddress **address, @@ -217,6 +223,13 @@ gssize g_socket_receive_from (GSocket gsize size, GCancellable *cancellable, GError **error); +GIO_AVAILABLE_IN_2_80 +GBytes * g_socket_receive_bytes_from (GSocket *socket, + GSocketAddress **address, + gsize size, + gint64 timeout_us, + GCancellable *cancellable, + GError **error); GIO_AVAILABLE_IN_ALL gssize g_socket_send (GSocket *socket, const gchar *buffer, From 309f5384caf8fcf9734c84e43e36f070c21151af Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Mon, 11 Dec 2023 18:54:00 +0000 Subject: [PATCH 2/5] tests: Clear up freeing of test data in socket test The same struct was reused across multiple tests, but without a shared way of freeing its members. Refactor to add one. Signed-off-by: Philip Withnall --- gio/tests/socket.c | 59 ++++++++++++++++++++++------------------------ 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/gio/tests/socket.c b/gio/tests/socket.c index 1930e37ad..10e643ae3 100644 --- a/gio/tests/socket.c +++ b/gio/tests/socket.c @@ -43,14 +43,25 @@ static gboolean ipv6_supported; typedef struct { - GSocket *server; - GSocket *client; + GSocket *server; /* (owned) (not nullable) */ + GSocket *client; /* (owned) (nullable) */ GSocketFamily family; - GThread *thread; - GMainLoop *loop; - GCancellable *cancellable; /* to shut down dgram echo server thread */ + GThread *thread; /* (owned) (not nullable) */ + GMainLoop *loop; /* (owned) (nullable) */ + GCancellable *cancellable; /* to shut down dgram echo server thread; (owned) (nullable) */ } IPTestData; +static void +ip_test_data_free (IPTestData *data) +{ + g_clear_object (&data->server); + g_clear_object (&data->client); + g_clear_pointer (&data->loop, g_main_loop_unref); + g_clear_object (&data->cancellable); + + g_slice_free (IPTestData, data); +} + static gpointer echo_server_dgram_thread (gpointer user_data) { @@ -130,7 +141,7 @@ create_server_full (GSocketFamily family, GSocketAddress *addr; GInetAddress *iaddr; - data = g_slice_new (IPTestData); + data = g_slice_new0 (IPTestData); data->family = family; data->server = server = g_socket_new (family, @@ -198,8 +209,7 @@ create_server_full (GSocketFamily family, return data; error: - g_clear_object (&data->server); - g_slice_free (IPTestData, data); + ip_test_data_free (data); return NULL; } @@ -396,7 +406,7 @@ test_ip_async (GSocketFamily family) data->loop = g_main_loop_new (NULL, TRUE); g_main_loop_run (data->loop); - g_main_loop_unref (data->loop); + g_clear_pointer (&data->loop, g_main_loop_unref); g_socket_shutdown (client, FALSE, TRUE, &error); g_assert_no_error (error); @@ -426,10 +436,7 @@ test_ip_async (GSocketFamily family) g_socket_close (data->server, &error); g_assert_no_error (error); - g_object_unref (data->server); - g_object_unref (client); - - g_slice_free (IPTestData, data); + ip_test_data_free (data); } static void @@ -567,10 +574,9 @@ test_ip_sync (GSocketFamily family) g_socket_close (data->server, &error); g_assert_no_error (error); - g_object_unref (data->server); g_object_unref (client); - g_slice_free (IPTestData, data); + ip_test_data_free (data); } static void @@ -801,12 +807,10 @@ test_ip_sync_dgram (GSocketFamily family) g_socket_close (data->server, &error); g_assert_no_error (error); - g_object_unref (data->server); - g_object_unref (data->cancellable); g_object_unref (client); g_object_unref (dest_addr); - g_slice_free (IPTestData, data); + ip_test_data_free (data); } static void @@ -1046,10 +1050,9 @@ test_close_graceful (void) g_assert_no_error (error); g_object_unref (server); - g_object_unref (data->server); g_object_unref (client); - g_slice_free (IPTestData, data); + ip_test_data_free (data); } #if defined (IPPROTO_IPV6) && defined (IPV6_V6ONLY) @@ -1132,11 +1135,10 @@ test_ipv6_v4mapped (void) g_socket_close (data->server, &error); g_assert_no_error (error); - g_object_unref (data->server); g_object_unref (client); g_object_unref (v4addr); - g_slice_free (IPTestData, data); + ip_test_data_free (data); } #endif @@ -1198,10 +1200,9 @@ test_timed_wait (void) g_socket_close (data->server, &error); g_assert_no_error (error); - g_object_unref (data->server); g_object_unref (client); - g_slice_free (IPTestData, data); + ip_test_data_free (data); } static int @@ -1310,11 +1311,10 @@ test_fd_reuse (void) g_assert_cmpint (g_socket_get_fd (client2), ==, -1); g_assert_cmpint (g_socket_get_fd (data->server), ==, -1); - g_object_unref (data->server); g_object_unref (client); g_object_unref (client2); - g_slice_free (IPTestData, data); + ip_test_data_free (data); } static void @@ -2071,10 +2071,9 @@ test_credentials_tcp_client (void) g_socket_close (data->server, &error); g_assert_no_error (error); - g_object_unref (data->server); g_object_unref (client); - g_slice_free (IPTestData, data); + ip_test_data_free (data); } static void @@ -2144,10 +2143,8 @@ beach: g_clear_object (&iaddr); g_clear_pointer (&data->thread, g_thread_join); - g_clear_object (&data->server); - g_clear_object (&data->client); - g_slice_free (IPTestData, data); + ip_test_data_free (data); } } From e46184205f7fe50a9d34ed28693d106bedccb690 Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Mon, 11 Dec 2023 18:56:40 +0000 Subject: [PATCH 3/5] tests: Use g_assert_*() rather than g_assert() in socket tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It won’t get compiled out with `G_DISABLE_ASSERT`. Signed-off-by: Philip Withnall --- gio/tests/socket.c | 52 +++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/gio/tests/socket.c b/gio/tests/socket.c index 10e643ae3..d67a33f8d 100644 --- a/gio/tests/socket.c +++ b/gio/tests/socket.c @@ -155,7 +155,7 @@ create_server_full (GSocketFamily family, g_assert_cmpint (g_socket_get_socket_type (server), ==, socket_type); g_assert_cmpint (g_socket_get_protocol (server), ==, G_SOCKET_PROTOCOL_DEFAULT); #ifdef G_OS_WIN32 - g_assert (GLIB_PRIVATE_CALL (g_win32_handle_is_socket) ((HANDLE)(gintptr) g_socket_get_fd (server))); + g_assert_true (GLIB_PRIVATE_CALL (g_win32_handle_is_socket) ((HANDLE)(gintptr) g_socket_get_fd (server))); #endif g_socket_set_blocking (server, TRUE); @@ -320,7 +320,7 @@ test_ip_async_connected (GSocket *client, */ g_assert_cmpint (cond, ==, G_IO_OUT); - g_assert (g_socket_is_connected (client)); + g_assert_true (g_socket_is_connected (client)); /* This adds 1 second to "make check", so let's just only do it once. */ if (data->family == G_SOCKET_FAMILY_IPV4) @@ -490,14 +490,14 @@ test_ip_sync (GSocketFamily family) g_assert_cmpint (g_socket_get_socket_type (client), ==, G_SOCKET_TYPE_STREAM); g_assert_cmpint (g_socket_get_protocol (client), ==, G_SOCKET_PROTOCOL_DEFAULT); #ifdef G_OS_WIN32 - g_assert (GLIB_PRIVATE_CALL (g_win32_handle_is_socket) ((HANDLE)(gintptr) g_socket_get_fd (client))); + g_assert_true (GLIB_PRIVATE_CALL (g_win32_handle_is_socket) ((HANDLE)(gintptr) g_socket_get_fd (client))); #endif g_socket_set_blocking (client, TRUE); g_socket_set_timeout (client, 1); g_socket_connect (client, addr, NULL, &error); g_assert_no_error (error); - g_assert (g_socket_is_connected (client)); + g_assert_true (g_socket_is_connected (client)); g_object_unref (addr); /* This adds 1 second to "make check", so let's just only do it once. */ @@ -628,7 +628,7 @@ test_ip_sync_dgram (GSocketFamily family) g_assert_cmpint (g_socket_get_socket_type (client), ==, G_SOCKET_TYPE_DATAGRAM); g_assert_cmpint (g_socket_get_protocol (client), ==, G_SOCKET_PROTOCOL_DEFAULT); #ifdef G_OS_WIN32 - g_assert (GLIB_PRIVATE_CALL (g_win32_handle_is_socket) ((HANDLE)(gintptr) g_socket_get_fd (client))); + g_assert_true (GLIB_PRIVATE_CALL (g_win32_handle_is_socket) ((HANDLE)(gintptr) g_socket_get_fd (client))); #endif g_socket_set_blocking (client, TRUE); @@ -717,7 +717,7 @@ test_ip_sync_dgram (GSocketFamily family) g_assert_no_error (error); /* v[0].size + v[1].size + v[2].size + v[3].size + v[4].size + v[5].size */ g_assert_cmpint (len, ==, 17); - g_assert (memcmp (testbuf2, buf, 17) == 0); + g_assert_cmpmem (testbuf2, 17, buf, 17); memset (buf, 0, sizeof (buf)); len = g_socket_receive_from (client, NULL, buf, sizeof (buf), NULL, &error); @@ -866,7 +866,7 @@ test_ip_sync_dgram_timeouts (GSocketFamily family) g_assert_cmpint (g_socket_get_socket_type (client), ==, G_SOCKET_TYPE_DATAGRAM); g_assert_cmpint (g_socket_get_protocol (client), ==, G_SOCKET_PROTOCOL_DEFAULT); #ifdef G_OS_WIN32 - g_assert (GLIB_PRIVATE_CALL (g_win32_handle_is_socket) ((HANDLE)(gintptr) g_socket_get_fd (client))); + g_assert_true (GLIB_PRIVATE_CALL (g_win32_handle_is_socket) ((HANDLE)(gintptr) g_socket_get_fd (client))); #endif #ifdef G_OS_WIN32 @@ -1011,7 +1011,7 @@ test_close_graceful (void) g_assert_cmpint (g_socket_get_socket_type (client), ==, G_SOCKET_TYPE_STREAM); g_assert_cmpint (g_socket_get_protocol (client), ==, G_SOCKET_PROTOCOL_DEFAULT); #ifdef G_OS_WIN32 - g_assert (GLIB_PRIVATE_CALL (g_win32_handle_is_socket) ((HANDLE)(gintptr) g_socket_get_fd (client))); + g_assert_true (GLIB_PRIVATE_CALL (g_win32_handle_is_socket) ((HANDLE)(gintptr) g_socket_get_fd (client))); #endif g_socket_set_blocking (client, TRUE); @@ -1019,7 +1019,7 @@ test_close_graceful (void) g_socket_connect (client, addr, NULL, &error); g_assert_no_error (error); - g_assert (g_socket_is_connected (client)); + g_assert_true (g_socket_is_connected (client)); g_object_unref (addr); server = g_thread_join (data->thread); @@ -1126,7 +1126,7 @@ test_ipv6_v4mapped (void) g_socket_connect (client, v4addr, NULL, &error); g_assert_no_error (error); - g_assert (g_socket_is_connected (client)); + g_assert_true (g_socket_is_connected (client)); g_thread_join (data->thread); @@ -1266,7 +1266,7 @@ test_fd_reuse (void) g_socket_connect (client, addr, NULL, &error); g_assert_no_error (error); - g_assert (g_socket_is_connected (client)); + g_assert_true (g_socket_is_connected (client)); g_object_unref (addr); /* we have to dup otherwise the fd gets closed twice on unref */ @@ -1278,7 +1278,7 @@ test_fd_reuse (void) g_assert_cmpint (g_socket_get_socket_type (client2), ==, g_socket_get_socket_type (client)); g_assert_cmpint (g_socket_get_protocol (client2), ==, G_SOCKET_PROTOCOL_TCP); #ifdef G_OS_WIN32 - g_assert (GLIB_PRIVATE_CALL (g_win32_handle_is_socket) ((HANDLE)(gintptr) g_socket_get_fd (client))); + g_assert_true (GLIB_PRIVATE_CALL (g_win32_handle_is_socket) ((HANDLE)(gintptr) g_socket_get_fd (client))); #endif len = g_socket_send (client2, testbuf, strlen (testbuf) + 1, NULL, &error); @@ -1334,12 +1334,12 @@ test_sockaddr (void) sin6.sin6_flowinfo = 1729; saddr = g_socket_address_new_from_native (&sin6, sizeof (sin6)); - g_assert (G_IS_INET_SOCKET_ADDRESS (saddr)); + g_assert_true (G_IS_INET_SOCKET_ADDRESS (saddr)); isaddr = G_INET_SOCKET_ADDRESS (saddr); iaddr = g_inet_socket_address_get_address (isaddr); g_assert_cmpint (g_inet_address_get_family (iaddr), ==, G_SOCKET_FAMILY_IPV6); - g_assert (g_inet_address_get_is_loopback (iaddr)); + g_assert_true (g_inet_address_get_is_loopback (iaddr)); g_assert_cmpint (g_inet_socket_address_get_port (isaddr), ==, 42); g_assert_cmpint (g_inet_socket_address_get_scope_id (isaddr), ==, 17); @@ -1348,7 +1348,7 @@ test_sockaddr (void) g_socket_address_to_native (saddr, &gsin6, sizeof (gsin6), &error); g_assert_no_error (error); - g_assert (memcmp (&sin6.sin6_addr, &gsin6.sin6_addr, sizeof (struct in6_addr)) == 0); + g_assert_cmpmem (&sin6.sin6_addr, sizeof (struct in6_addr), &gsin6.sin6_addr, sizeof (struct in6_addr)); g_assert_cmpint (sin6.sin6_port, ==, gsin6.sin6_port); g_assert_cmpint (sin6.sin6_scope_id, ==, gsin6.sin6_scope_id); g_assert_cmpint (sin6.sin6_flowinfo, ==, gsin6.sin6_flowinfo); @@ -1399,7 +1399,7 @@ test_unix_from_fd (void) g_assert_cmpint (g_socket_get_socket_type (s), ==, G_SOCKET_TYPE_STREAM); g_assert_cmpint (g_socket_get_protocol (s), ==, G_SOCKET_PROTOCOL_DEFAULT); #ifdef G_OS_WIN32 - g_assert (GLIB_PRIVATE_CALL (g_win32_handle_is_socket) ((HANDLE)(gintptr) g_socket_get_fd (s))); + g_assert_true (GLIB_PRIVATE_CALL (g_win32_handle_is_socket) ((HANDLE)(gintptr) g_socket_get_fd (s))); #endif g_object_unref (s); } @@ -1428,7 +1428,7 @@ test_unix_connection (void) s = g_socket_new_from_fd (fd, &error); g_assert_no_error (error); c = g_socket_connection_factory_create_connection (s); - g_assert (G_IS_UNIX_CONNECTION (c)); + g_assert_true (G_IS_UNIX_CONNECTION (c)); g_object_unref (c); g_object_unref (s); } @@ -1443,9 +1443,9 @@ create_connection_for_fd (int fd) socket = g_socket_new_from_fd (fd, &err); g_assert_no_error (err); - g_assert (G_IS_SOCKET (socket)); + g_assert_true (G_IS_SOCKET (socket)); connection = g_socket_connection_factory_create_connection (socket); - g_assert (G_IS_UNIX_CONNECTION (connection)); + g_assert_true (G_IS_UNIX_CONNECTION (connection)); g_object_unref (socket); return connection; } @@ -1525,7 +1525,7 @@ test_unix_connection_ancillary_data (void) g_assert_cmpstr (buffer, ==, TEST_DATA); waitpid (pid, &status, 0); - g_assert (WIFEXITED (status)); + g_assert_true (WIFEXITED (status)); g_assert_cmpint (WEXITSTATUS (status), ==, 0); } @@ -1612,8 +1612,8 @@ test_source_postmortem (void) /* Test that, after a socket is closed, its source callback should be called * exactly once. */ g_main_context_iteration (context, FALSE); - g_assert (callback_visited); - g_assert (!g_main_context_pending (context)); + g_assert_true (callback_visited); + g_assert_false (g_main_context_pending (context)); g_main_context_unref (context); } @@ -1713,14 +1713,14 @@ test_get_available (gconstpointer user_data) G_SOCKET_PROTOCOL_DEFAULT, &err); g_assert_no_error (err); - g_assert (G_IS_SOCKET (listener)); + g_assert_true (G_IS_SOCKET (listener)); client = g_socket_new (G_SOCKET_FAMILY_IPV4, socket_type, G_SOCKET_PROTOCOL_DEFAULT, &err); g_assert_no_error (err); - g_assert (G_IS_SOCKET (client)); + g_assert_true (G_IS_SOCKET (client)); if (socket_type == G_SOCKET_TYPE_STREAM) { @@ -1918,14 +1918,14 @@ test_read_write (gconstpointer user_data) G_SOCKET_PROTOCOL_DEFAULT, &err); g_assert_no_error (err); - g_assert (G_IS_SOCKET (listener)); + g_assert_true (G_IS_SOCKET (listener)); client = g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_DEFAULT, &err); g_assert_no_error (err); - g_assert (G_IS_SOCKET (client)); + g_assert_true (G_IS_SOCKET (client)); addr = g_inet_address_new_any (G_SOCKET_FAMILY_IPV4); saddr = g_inet_socket_address_new (addr, 0); From 7463a256cb64e50cd0ea85a5fa8b9be28ebd87b0 Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Mon, 11 Dec 2023 19:53:45 +0000 Subject: [PATCH 4/5] tests: Add a missing no-error assertion in the socket tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It doesn’t fail, but at least now it’s there to catch problems if they do happen. Signed-off-by: Philip Withnall --- gio/tests/socket.c | 1 + 1 file changed, 1 insertion(+) diff --git a/gio/tests/socket.c b/gio/tests/socket.c index d67a33f8d..ffcb6f751 100644 --- a/gio/tests/socket.c +++ b/gio/tests/socket.c @@ -617,6 +617,7 @@ test_ip_sync_dgram (GSocketFamily family) } dest_addr = g_socket_get_local_address (data->server, &error); + g_assert_no_error (error); client = g_socket_new (family, G_SOCKET_TYPE_DATAGRAM, From 98e4b3adecb293c7cd64ceb882898a8efadd4956 Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Mon, 11 Dec 2023 19:54:15 +0000 Subject: [PATCH 5/5] tests: Add tests for g_socket_receive_bytes() and g_socket_receive_bytes_from() Signed-off-by: Philip Withnall --- gio/tests/socket.c | 201 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 201 insertions(+) diff --git a/gio/tests/socket.c b/gio/tests/socket.c index ffcb6f751..5747c2017 100644 --- a/gio/tests/socket.c +++ b/gio/tests/socket.c @@ -2343,6 +2343,205 @@ test_credentials_unix_socketpair (void) } #endif +static void +test_receive_bytes (void) +{ + const GSocketFamily family = G_SOCKET_FAMILY_IPV4; + IPTestData *data; + GError *error = NULL; + GSocket *client; + GSocketAddress *addr; + gssize len; + GBytes *bytes = NULL; + gint64 time_start; + GCancellable *cancellable = NULL; + + g_test_summary ("Test basic functionality of g_socket_receive_bytes()"); + + data = create_server (family, echo_server_thread, FALSE, &error); + if (error != NULL) + { + g_test_skip_printf ("Failed to create server: %s", error->message); + g_clear_error (&error); + return; + } + + addr = g_socket_get_local_address (data->server, &error); + g_assert_no_error (error); + + client = g_socket_new (family, + G_SOCKET_TYPE_STREAM, + G_SOCKET_PROTOCOL_DEFAULT, + &error); + g_assert_no_error (error); + + g_socket_set_blocking (client, TRUE); + g_socket_set_timeout (client, 10); + + g_socket_connect (client, addr, NULL, &error); + g_assert_no_error (error); + g_object_unref (addr); + + /* Send something. */ + len = g_socket_send (client, "hello", strlen ("hello"), NULL, &error); + g_assert_no_error (error); + g_assert_cmpint (len, ==, strlen ("hello")); + + /* And receive it back again. */ + bytes = g_socket_receive_bytes (client, 5, -1, NULL, &error); + g_assert_no_error (error); + g_assert_nonnull (bytes); + g_assert_cmpuint (g_bytes_get_size (bytes), ==, 5); + g_assert_cmpmem (g_bytes_get_data (bytes, NULL), 5, "hello", 5); + g_bytes_unref (bytes); + + /* Try again with a receive buffer which is bigger than the sent bytes, to + * test sub-buffer handling. */ + len = g_socket_send (client, "hello", strlen ("hello"), NULL, &error); + g_assert_no_error (error); + g_assert_cmpint (len, ==, strlen ("hello")); + + /* And receive it back again. */ + bytes = g_socket_receive_bytes (client, 500, -1, NULL, &error); + g_assert_no_error (error); + g_assert_nonnull (bytes); + g_assert_cmpuint (g_bytes_get_size (bytes), ==, 5); + g_assert_cmpmem (g_bytes_get_data (bytes, NULL), 5, "hello", 5); + g_bytes_unref (bytes); + + /* Try receiving when there’s nothing to receive, with a timeout. This should + * be the per-operation timeout, not the socket’s overall timeout */ + time_start = g_get_real_time (); + bytes = g_socket_receive_bytes (client, 500, 10, NULL, &error); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT); + g_assert_null (bytes); + g_assert_cmpint (g_get_real_time () - time_start, <, g_socket_get_timeout (client) * G_USEC_PER_SEC); + g_clear_error (&error); + + /* And try receiving when already cancelled. */ + cancellable = g_cancellable_new (); + g_cancellable_cancel (cancellable); + bytes = g_socket_receive_bytes (client, 500, -1, cancellable, &error); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); + g_assert_null (bytes); + g_clear_error (&error); + g_clear_object (&cancellable); + + /* Tidy up. */ + g_socket_close (client, &error); + g_assert_no_error (error); + + g_cancellable_cancel (data->cancellable); + g_thread_join (data->thread); + + g_socket_close (data->server, &error); + g_assert_no_error (error); + + g_object_unref (client); + + ip_test_data_free (data); +} + +static void +test_receive_bytes_from (void) +{ + const GSocketFamily family = G_SOCKET_FAMILY_IPV4; + IPTestData *data; + GError *error = NULL; + GSocket *client; + GSocketAddress *dest_addr = NULL, *sender_addr = NULL; + gssize len; + GBytes *bytes = NULL; + gint64 time_start; + GCancellable *cancellable = NULL; + + g_test_summary ("Test basic functionality of g_socket_receive_bytes_from()"); + + data = create_server_full (family, G_SOCKET_TYPE_DATAGRAM, + echo_server_dgram_thread, FALSE, &error); + if (error != NULL) + { + g_test_skip_printf ("Failed to create server: %s", error->message); + g_clear_error (&error); + return; + } + + dest_addr = g_socket_get_local_address (data->server, &error); + g_assert_no_error (error); + + client = g_socket_new (family, + G_SOCKET_TYPE_DATAGRAM, + G_SOCKET_PROTOCOL_DEFAULT, + &error); + g_assert_no_error (error); + + g_socket_set_blocking (client, TRUE); + g_socket_set_timeout (client, 10); + + /* Send something. */ + len = g_socket_send_to (client, dest_addr, "hello", strlen ("hello"), NULL, &error); + g_assert_no_error (error); + g_assert_cmpint (len, ==, strlen ("hello")); + + /* And receive it back again. */ + bytes = g_socket_receive_bytes_from (client, &sender_addr, 5, -1, NULL, &error); + g_assert_no_error (error); + g_assert_nonnull (bytes); + g_assert_cmpuint (g_bytes_get_size (bytes), ==, 5); + g_assert_cmpmem (g_bytes_get_data (bytes, NULL), 5, "hello", 5); + g_assert_nonnull (sender_addr); + g_clear_object (&sender_addr); + g_bytes_unref (bytes); + + /* Try again with a receive buffer which is bigger than the sent bytes, to + * test sub-buffer handling. */ + len = g_socket_send_to (client, dest_addr, "hello", strlen ("hello"), NULL, &error); + g_assert_no_error (error); + g_assert_cmpint (len, ==, strlen ("hello")); + + /* And receive it back again. */ + bytes = g_socket_receive_bytes_from (client, NULL, 500, -1, NULL, &error); + g_assert_no_error (error); + g_assert_nonnull (bytes); + g_assert_cmpuint (g_bytes_get_size (bytes), ==, 5); + g_assert_cmpmem (g_bytes_get_data (bytes, NULL), 5, "hello", 5); + g_bytes_unref (bytes); + + /* Try receiving when there’s nothing to receive, with a timeout. This should + * be the per-operation timeout, not the socket’s overall timeout */ + time_start = g_get_real_time (); + bytes = g_socket_receive_bytes_from (client, &sender_addr, 500, 10, NULL, &error); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT); + g_assert_null (bytes); + g_assert_null (sender_addr); + g_assert_cmpint (g_get_real_time () - time_start, <, g_socket_get_timeout (client) * G_USEC_PER_SEC); + g_clear_error (&error); + + /* And try receiving when already cancelled. */ + cancellable = g_cancellable_new (); + g_cancellable_cancel (cancellable); + bytes = g_socket_receive_bytes_from (client, &sender_addr, 500, -1, cancellable, &error); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); + g_assert_null (bytes); + g_assert_null (sender_addr); + g_clear_error (&error); + g_clear_object (&cancellable); + + /* Tidy up. */ + g_socket_close (client, &error); + g_assert_no_error (error); + + g_cancellable_cancel (data->cancellable); + g_thread_join (data->thread); + + g_socket_close (data->server, &error); + g_assert_no_error (error); + + g_object_unref (client); + + ip_test_data_free (data); +} + int main (int argc, char *argv[]) @@ -2409,6 +2608,8 @@ main (int argc, g_test_add_func ("/socket/credentials/tcp_server", test_credentials_tcp_server); g_test_add_func ("/socket/credentials/unix_socketpair", test_credentials_unix_socketpair); #endif + g_test_add_func ("/socket/receive_bytes", test_receive_bytes); + g_test_add_func ("/socket/receive_bytes_from", test_receive_bytes_from); return g_test_run(); }