[glib-networking/wip/pwithnall/dtls: 13/21] gnutls: Implement half-duplex close in GTlsOutputStreamGnutls
- From: Philip Withnall <pwithnall src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib-networking/wip/pwithnall/dtls: 13/21] gnutls: Implement half-duplex close in GTlsOutputStreamGnutls
- Date: Mon, 9 Nov 2015 12:37:48 +0000 (UTC)
commit df845dd5bc51f675fccca5601dd18d946176e675
Author: Philip Withnall <philip withnall collabora co uk>
Date: Fri Sep 26 15:47:07 2014 +0100
gnutls: Implement half-duplex close in GTlsOutputStreamGnutls
Including unit tests.
Based on a patch by Olivier CrĂȘte <olivier crete collabora com>.
https://bugzilla.gnome.org/show_bug.cgi?id=735754
tls/gnutls/gtlsoutputstream-gnutls.c | 83 ++++++++++++++++++++++++
tls/tests/connection.c | 115 ++++++++++++++++++++++++++++++++++
2 files changed, 198 insertions(+), 0 deletions(-)
---
diff --git a/tls/gnutls/gtlsoutputstream-gnutls.c b/tls/gnutls/gtlsoutputstream-gnutls.c
index e3a4f77..aa60f08 100644
--- a/tls/gnutls/gtlsoutputstream-gnutls.c
+++ b/tls/gnutls/gtlsoutputstream-gnutls.c
@@ -128,6 +128,86 @@ g_tls_output_stream_gnutls_pollable_write_nonblocking (GPollableOutputStream *p
return ret;
}
+static gboolean
+g_tls_output_stream_gnutls_close (GOutputStream *stream,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsOutputStreamGnutls *tls_stream = G_TLS_OUTPUT_STREAM_GNUTLS (stream);
+ GIOStream *conn;
+ gboolean ret;
+
+ conn = g_weak_ref_get (&tls_stream->priv->weak_conn);
+
+ /* Special case here because this is called by the finalize
+ * of the main GTlsConnection object.
+ */
+ if (conn == NULL)
+ return TRUE;
+
+ ret = g_tls_connection_gnutls_close_internal (conn, G_TLS_DIRECTION_WRITE,
+ cancellable, error);
+
+ g_object_unref (conn);
+ return ret;
+}
+
+/* We do async close as synchronous-in-a-thread so we don't need to
+ * implement G_IO_IN/G_IO_OUT flip-flopping just for this one case
+ * (since handshakes are also done synchronously now).
+ */
+static void
+close_thread (GTask *task,
+ gpointer object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ GTlsOutputStreamGnutls *tls_stream = object;
+ GError *error = NULL;
+ GIOStream *conn;
+
+ conn = g_weak_ref_get (&tls_stream->priv->weak_conn);
+
+ if (conn && !g_tls_connection_gnutls_close_internal (conn,
+ G_TLS_DIRECTION_WRITE,
+ cancellable, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+
+ if (conn)
+ g_object_unref (conn);
+}
+
+
+static void
+g_tls_output_stream_gnutls_close_async (GOutputStream *stream,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (stream, cancellable, callback, user_data);
+ g_task_set_source_tag (task, g_tls_output_stream_gnutls_close_async);
+ g_task_set_priority (task, io_priority);
+ g_task_run_in_thread (task, close_thread);
+ g_object_unref (task);
+}
+
+static gboolean
+g_tls_output_stream_gnutls_close_finish (GOutputStream *stream,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (g_task_is_valid (result, stream), FALSE);
+ g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) ==
+ g_tls_output_stream_gnutls_close_async, FALSE);
+
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
static void
g_tls_output_stream_gnutls_class_init (GTlsOutputStreamGnutlsClass *klass)
{
@@ -140,6 +220,9 @@ g_tls_output_stream_gnutls_class_init (GTlsOutputStreamGnutlsClass *klass)
gobject_class->finalize = g_tls_output_stream_gnutls_finalize;
output_stream_class->write_fn = g_tls_output_stream_gnutls_write;
+ output_stream_class->close_fn = g_tls_output_stream_gnutls_close;
+ output_stream_class->close_async = g_tls_output_stream_gnutls_close_async;
+ output_stream_class->close_finish = g_tls_output_stream_gnutls_close_finish;
}
static void
diff --git a/tls/tests/connection.c b/tls/tests/connection.c
index 60af5f6..d2bf8cb 100644
--- a/tls/tests/connection.c
+++ b/tls/tests/connection.c
@@ -1661,6 +1661,59 @@ test_close_during_handshake (TestConnection *test,
}
static void
+test_output_stream_close_during_handshake (TestConnection *test,
+ gconstpointer data)
+{
+ GIOStream *connection;
+ GError *error = NULL;
+ GMainContext *context;
+ GMainLoop *loop;
+ gboolean handshake_complete = FALSE;
+
+ g_test_bug ("688751");
+
+ connection = start_async_server_and_connect_to_it (test, G_TLS_AUTHENTICATION_REQUESTED, TRUE);
+ test->client_connection = g_tls_client_connection_new (connection, test->identity, &error);
+ g_assert_no_error (error);
+ g_object_unref (connection);
+
+ loop = g_main_loop_new (NULL, FALSE);
+ g_signal_connect (test->client_connection, "notify::accepted-cas",
+ G_CALLBACK (quit_loop_on_notify), loop);
+
+ context = g_main_context_new ();
+ g_main_context_push_thread_default (context);
+ g_tls_connection_handshake_async (G_TLS_CONNECTION (test->client_connection),
+ G_PRIORITY_DEFAULT, NULL,
+ handshake_completed, &handshake_complete);
+ g_main_context_pop_thread_default (context);
+
+ /* Now run the (default GMainContext) loop, which is needed for
+ * the server side of things. The client-side handshake will run in
+ * a thread, but its callback will never be invoked because its
+ * context isn't running.
+ */
+ g_main_loop_run (loop);
+ g_main_loop_unref (loop);
+
+ /* At this point handshake_thread() has started (and maybe
+ * finished), but handshake_thread_completed() (and thus
+ * finish_handshake()) has not yet run. Make sure close doesn't
+ * block.
+ */
+ g_output_stream_close (g_io_stream_get_output_stream (test->client_connection), NULL, &error);
+ g_assert_no_error (error);
+
+ /* We have to let the handshake_async() call finish now, or
+ * teardown_connection() will assert.
+ */
+ while (!handshake_complete)
+ g_main_context_iteration (context, TRUE);
+ g_main_context_unref (context);
+}
+
+
+static void
test_write_during_handshake (TestConnection *test,
gconstpointer data)
{
@@ -1853,6 +1906,64 @@ test_fallback_subprocess (TestConnection *test,
g_assert_no_error (error);
}
+static void
+test_output_stream_close (TestConnection *test,
+ gconstpointer data)
+{
+ GIOStream *connection;
+ GError *error = NULL;
+ gboolean ret;
+ gboolean handshake_complete = FALSE;
+ gssize size;
+
+ connection = start_async_server_and_connect_to_it (test, G_TLS_AUTHENTICATION_NONE, TRUE);
+ test->client_connection = g_tls_client_connection_new (connection, test->identity, &error);
+ g_assert_no_error (error);
+ g_object_unref (connection);
+
+ /* No validation at all in this test */
+ g_tls_client_connection_set_validation_flags (G_TLS_CLIENT_CONNECTION (test->client_connection),
+ 0);
+
+ g_tls_connection_handshake_async (G_TLS_CONNECTION (test->client_connection),
+ G_PRIORITY_DEFAULT, NULL,
+ handshake_completed, &handshake_complete);
+
+ while (!handshake_complete)
+ g_main_context_iteration (NULL, TRUE);
+
+ ret = g_output_stream_close (g_io_stream_get_output_stream (test->client_connection),
+ NULL, &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+
+
+ /* Verify that double close returns TRUE */
+ ret = g_output_stream_close (g_io_stream_get_output_stream (test->client_connection),
+ NULL, &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+
+ size = g_output_stream_write (g_io_stream_get_output_stream (test->client_connection),
+ "data", 4, NULL, &error);
+ g_assert (size == -1);
+ g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CLOSED);
+ g_clear_error (&error);
+
+ /* We closed the output stream, but not the input stream, so receiving
+ * data should still work.
+ */
+ read_test_data_async (test);
+ g_main_loop_run (test->loop);
+
+ g_assert_no_error (test->read_error);
+ g_assert_no_error (test->server_error);
+
+ ret = g_io_stream_close (test->client_connection, NULL, &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+}
+
int
main (int argc,
char *argv[])
@@ -1933,10 +2044,14 @@ main (int argc,
setup_connection, test_close_immediately, teardown_connection);
g_test_add ("/tls/connection/close-during-handshake", TestConnection, NULL,
setup_connection, test_close_during_handshake, teardown_connection);
+ g_test_add ("/tls/connection/close-output-stream-during-handshake", TestConnection, NULL,
+ setup_connection, test_output_stream_close_during_handshake, teardown_connection);
g_test_add ("/tls/connection/write-during-handshake", TestConnection, NULL,
setup_connection, test_write_during_handshake, teardown_connection);
g_test_add ("/tls/connection/async-implicit-handshake", TestConnection, NULL,
setup_connection, test_async_implicit_handshake, teardown_connection);
+ g_test_add ("/tls/connection/output-stream-close", TestConnection, NULL,
+ setup_connection, test_output_stream_close, teardown_connection);
g_test_add_data_func ("/tls/connection/fallback/SSL", PRIORITY_SSL_FALLBACK, test_fallback);
g_test_add ("/tls/connection/fallback/subprocess/" PRIORITY_SSL_FALLBACK,
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]