Merge branch 'ebassi/socket-bytes' into 'main'

Add GBytes variants for GSocket receive methods

See merge request GNOME/glib!3603
This commit is contained in:
Philip Withnall 2023-12-18 14:40:00 +00:00
commit 9b915507af
3 changed files with 411 additions and 57 deletions

View File

@ -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

View File

@ -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,

View File

@ -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,
@ -144,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);
@ -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;
}
@ -310,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)
@ -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
@ -483,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. */
@ -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
@ -611,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,
@ -622,7 +629,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);
@ -711,7 +718,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);
@ -801,12 +808,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
@ -862,7 +867,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
@ -1007,7 +1012,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);
@ -1015,7 +1020,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);
@ -1046,10 +1051,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)
@ -1123,7 +1127,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);
@ -1132,11 +1136,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 +1201,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
@ -1265,7 +1267,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 */
@ -1277,7 +1279,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);
@ -1310,11 +1312,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
@ -1334,12 +1335,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 +1349,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 +1400,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 +1429,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 +1444,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 +1526,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 +1613,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 +1714,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 +1919,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);
@ -2071,10 +2072,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 +2144,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);
}
}
@ -2345,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 theres nothing to receive, with a timeout. This should
* be the per-operation timeout, not the sockets 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 theres nothing to receive, with a timeout. This should
* be the per-operation timeout, not the sockets 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[])
@ -2411,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();
}