[glib-networking/mcatanzaro/base-rebase: 32/33] progress



commit b897e524ef6eedc1a6b8a643c031ee4f02ce6c3c
Author: Michael Catanzaro <mcatanzaro igalia com>
Date:   Fri Apr 19 14:34:37 2019 -0500

    progress

 meson.build                              |    2 +-
 tls/base/gtlsconnection-base.c           |  300 ++++-
 tls/base/gtlsconnection-base.h           |   50 +-
 tls/gnutls/gtlsclientconnection-gnutls.c |   74 +-
 tls/gnutls/gtlsconnection-gnutls.c       | 1831 +++++-------------------------
 tls/gnutls/gtlsconnection-gnutls.h       |   47 +-
 tls/gnutls/gtlsinputstream-gnutls.c      |  268 -----
 tls/gnutls/gtlsinputstream-gnutls.h      |   41 -
 tls/gnutls/gtlsoutputstream-gnutls.c     |  269 -----
 tls/gnutls/gtlsoutputstream-gnutls.h     |   41 -
 tls/gnutls/gtlsserverconnection-gnutls.c |   25 +-
 tls/openssl/gtlsconnection-openssl.c     |   74 +-
 tls/tests/connection.c                   |   12 -
 tls/tests/dtls-connection.c              |    8 -
 14 files changed, 677 insertions(+), 2365 deletions(-)
---
diff --git a/meson.build b/meson.build
index 8d01f52..c5ca8f4 100644
--- a/meson.build
+++ b/meson.build
@@ -43,7 +43,7 @@ if host_system.contains('linux')
 endif
 
 # *** Check GLib GIO        ***
-glib_dep = dependency('glib-2.0', version: '>= 2.55.1',
+glib_dep = dependency('glib-2.0', version: '>= 2.60.0',
   fallback: ['glib', 'libglib_dep'])
 gio_dep = dependency('gio-2.0',
   fallback: ['glib', 'libgio_dep'])
diff --git a/tls/base/gtlsconnection-base.c b/tls/base/gtlsconnection-base.c
index 984231a..73f2a8b 100644
--- a/tls/base/gtlsconnection-base.c
+++ b/tls/base/gtlsconnection-base.c
@@ -87,7 +87,7 @@ typedef struct
   GTlsInteraction       *interaction;
 
   GTlsCertificate       *certificate;
-  gboolean               certificate_requested;
+  gboolean               missing_requested_client_certificate;
   GError                *certificate_error;
   GTlsCertificate       *peer_certificate;
   GTlsCertificateFlags   peer_certificate_errors;
@@ -145,6 +145,9 @@ typedef struct
 
   GMutex         op_mutex;
   GCancellable  *waiting_for_op;
+
+  gchar        **advertised_protocols;
+  gchar         *negotiated_protocol;
 } GTlsConnectionBasePrivate;
 
 static void g_tls_connection_base_dtls_connection_iface_init (GDtlsConnectionInterface *iface);
@@ -182,7 +185,11 @@ enum
   PROP_CERTIFICATE,
   PROP_INTERACTION,
   PROP_PEER_CERTIFICATE,
-  PROP_PEER_CERTIFICATE_ERRORS
+  PROP_PEER_CERTIFICATE_ERRORS,
+#if GLIB_CHECK_VERSION(2, 60, 0)
+  PROP_ADVERTISED_PROTOCOLS,
+  PROP_NEGOTIATED_PROTOCOL,
+#endif
 };
 
 static gboolean
@@ -242,6 +249,11 @@ g_tls_connection_base_finalize (GObject *object)
 
   g_clear_pointer (&priv->app_data_buf, g_byte_array_unref);
 
+#if GLIB_CHECK_VERSION(2, 60, 0)
+  g_clear_pointer (&priv->advertised_protocols, g_strfreev);
+  g_clear_pointer (&priv->negotiated_protocol, g_free);
+#endif
+
   G_OBJECT_CLASS (g_tls_connection_base_parent_class)->finalize (object);
 }
 
@@ -303,6 +315,14 @@ g_tls_connection_base_get_property (GObject    *object,
       g_value_set_flags (value, priv->peer_certificate_errors);
       break;
 
+    case PROP_ADVERTISED_PROTOCOLS:
+      g_value_set_boxed (value, priv->advertised_protocols);
+      break;
+
+    case PROP_NEGOTIATED_PROTOCOL:
+      g_value_set_string (value, priv->negotiated_protocol);
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     }
@@ -403,6 +423,11 @@ g_tls_connection_base_set_property (GObject      *object,
       priv->interaction = g_value_dup_object (value);
       break;
 
+    case PROP_ADVERTISED_PROTOCOLS:
+      g_clear_pointer (&priv->advertised_protocols, g_strfreev);
+      priv->advertised_protocols = g_value_dup_boxed (value);
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     }
@@ -715,7 +740,7 @@ g_tls_connection_base_pop_io (GTlsConnectionBase  *tls,
 
 /* Checks whether the underlying base stream or GDatagramBased meets
  * @condition. */
-static gboolean
+gboolean
 g_tls_connection_base_base_check (GTlsConnectionBase *tls,
                                   GIOCondition        condition)
 {
@@ -1084,39 +1109,6 @@ g_tls_connection_base_condition_wait (GDatagramBased  *datagram_based,
   return !g_cancellable_set_error_if_cancelled (cancellable, error);
 }
 
-gboolean
-g_tls_connection_base_accept_peer_certificate (GTlsConnectionBase   *tls,
-                                               GTlsCertificate      *peer_certificate,
-                                               GTlsCertificateFlags  peer_certificate_errors)
-{
-  GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
-  gboolean accepted = FALSE;
-
-  if (G_IS_TLS_CLIENT_CONNECTION (tls) && priv->peer_certificate)
-    {
-      GTlsCertificateFlags validation_flags;
-
-      if (!g_tls_connection_base_is_dtls (tls))
-        validation_flags =
-          g_tls_client_connection_get_validation_flags (G_TLS_CLIENT_CONNECTION (tls));
-      else
-        validation_flags =
-          g_dtls_client_connection_get_validation_flags (G_DTLS_CLIENT_CONNECTION (tls));
-
-      if ((peer_certificate_errors & validation_flags) == 0)
-        accepted = TRUE;
-    }
-
-  if (!accepted)
-    {
-      accepted = g_tls_connection_emit_accept_certificate (G_TLS_CONNECTION (tls),
-                                                           peer_certificate,
-                                                           peer_certificate_errors);
-    }
-
-  return accepted;
-}
-
 void
 g_tls_connection_base_set_peer_certificate (GTlsConnectionBase   *tls,
                                             GTlsCertificate      *peer_certificate,
@@ -1149,7 +1141,7 @@ handshake_thread (GTask        *task,
   timeout = *((gint64 *)task_data);
 
   priv->started_handshake = FALSE;
-  priv->certificate_requested = FALSE;
+  priv->missing_requested_client_certificate = FALSE;
 
   if (!claim_op (tls, G_TLS_CONNECTION_BASE_OP_HANDSHAKE,
                  timeout, cancellable, &error))
@@ -1176,15 +1168,12 @@ handshake_thread (GTask        *task,
   priv->peer_certificate_errors = 0;
 
   priv->started_handshake = TRUE;
-  tls_class->handshake (tls, timeout, cancellable, &error);
+  tls_class->handshake_thread_handshake (tls, timeout, cancellable, &error);
   priv->need_handshake = FALSE;
 
   if (error)
     {
-      if ((g_error_matches (error, G_IO_ERROR, G_IO_ERROR_FAILED) ||
-           g_error_matches (error, G_IO_ERROR, G_IO_ERROR_BROKEN_PIPE) ||
-           g_error_matches (error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS)) &&
-          priv->certificate_requested)
+      if (*error != NULL && priv->missing_requested_client_certificate)
         {
           g_clear_error (&error);
           if (priv->certificate_error)
@@ -1207,6 +1196,44 @@ handshake_thread (GTask        *task,
     }
 }
 
+static void
+sync_handshake_thread_completed (GObject      *object,
+                                 GAsyncResult *result,
+                                 gpointer      user_data)
+{
+  GTlsConnectionBase *tls = G_TLS_CONNECTION_BASE (object);
+  GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
+
+  g_assert (g_main_context_is_owner (priv->handshake_context));
+
+  g_mutex_lock (&priv->op_mutex);
+  priv->sync_handshake_completed = TRUE;
+  g_mutex_unlock (&priv->op_mutex);
+
+  g_main_context_wakeup (priv->handshake_context);
+}
+
+static void
+crank_sync_handshake_context (GTlsConnectionBase *tls,
+                              GCancellable       *cancellable)
+{
+  GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
+
+  /* need_finish_handshake will be set inside sync_handshake_thread_completed(),
+   * which should only ever be invoked while iterating the handshake context
+   * here. So need_finish_handshake should only change on this thread.
+   */
+  g_mutex_lock (&priv->op_mutex);
+  priv->sync_handshake_completed = FALSE;
+  while (!priv->sync_handshake_completed && !g_cancellable_is_cancelled (cancellable))
+    {
+      g_mutex_unlock (&priv->op_mutex);
+      g_main_context_iteration (priv->handshake_context, TRUE);
+      g_mutex_lock (&priv->op_mutex);
+    }
+  g_mutex_unlock (&priv->op_mutex);
+}
+
 static gboolean
 finish_handshake (GTlsConnectionBase  *tls,
                   GTask               *task,
@@ -1214,10 +1241,17 @@ finish_handshake (GTlsConnectionBase  *tls,
 {
   GTlsConnectionBaseClass *tls_class = G_TLS_CONNECTION_BASE_GET_CLASS (tls);
   GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
+  gchar *original_negotiated_protocol;
   GError *my_error = NULL;
 
+  orig_negotiated_protocol = g_steal_pointer (&priv->negotiated_protocol);
+
   if (g_task_propagate_boolean (task, &my_error))
-    tls_class->complete_handshake (tls, &my_error);
+    tls_class->complete_handshake (tls, &priv->negotiated_protocol, &my_error);
+
+  if (g_strcmp0 (original_negotiated_protocol, priv->negotiated_protocol) != 0)
+    g_object_notify (G_OBJECT (tls), "negotiated-protocol");
+  g_free (original_negotiated_protocol);
 
   if (my_error && priv->started_handshake)
     priv->handshake_error = g_error_copy (my_error);
@@ -1235,19 +1269,33 @@ g_tls_connection_base_handshake (GTlsConnection   *conn,
                                  GError          **error)
 {
   GTlsConnectionBase *tls = G_TLS_CONNECTION_BASE (conn);
+  GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
   GTask *task;
   gboolean success;
   gint64 *timeout = NULL;
   GError *my_error = NULL;
 
-  task = g_task_new (conn, cancellable, NULL, NULL);
+  g_assert (priv->handshake_context == NULL);
+  priv->handshake_context = g_main_context_new ();
+
+  g_main_context_push_thread_default (priv->handshake_context);
+
+  if (tls_class->prepare_handshake)
+    tls_class->prepare_handshake (tls, priv->advertised_protocols);
+
+  task = g_task_new (conn, cancellable, sync_handshake_thread_completed, NULL);
   g_task_set_source_tag (task, g_tls_connection_base_handshake);
+  g_task_set_return_on_cancel (task, TRUE);
 
   timeout = g_new0 (gint64, 1);
   *timeout = -1; /* blocking */
   g_task_set_task_data (task, timeout, g_free);
 
-  g_task_run_in_thread_sync (task, handshake_thread);
+  g_task_run_in_thread (task, handshake_thread);
+  crank_sync_handshake_context (tls, cancellable);
+
+  g_clear_pointer (&priv->handshake_context, g_main_context_unref);
+
   success = finish_handshake (tls, task, &my_error);
   g_object_unref (task);
 
@@ -1285,6 +1333,8 @@ handshake_thread_completed (GObject      *object,
   GError *error = NULL;
   gboolean need_finish_handshake, success;
 
+  g_clear_pointer (&priv->handshake_context, g_main_context_unref);
+
   g_mutex_lock (&priv->op_mutex);
   if (priv->need_finish_handshake)
     {
@@ -1336,15 +1386,20 @@ async_handshake_thread (GTask        *task,
 }
 
 static void
-g_tls_connection_base_handshake_async (GTlsConnection       *conn,
-                                       int                   io_priority,
-                                       GCancellable         *cancellable,
-                                       GAsyncReadyCallback   callback,
-                                       gpointer              user_data)
+g_tls_connection_base_handshake_async (GTlsConnection      *conn,
+                                       int                  io_priority,
+                                       GCancellable        *cancellable,
+                                       GAsyncReadyCallback  callback,
+                                       gpointer             user_data)
 {
+  GTlsConnectionBase *tls = G_TLS_CONNECTION_BASE (conn);
+  GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (G_TLS_CONNECTION_GNUTLS 
(conn));
   GTask *thread_task, *caller_task;
   gint64 *timeout = NULL;
 
+  g_assert (!priv->handshake_context);
+  priv->handshake_context = g_main_context_ref_thread_default ();
+
   caller_task = g_task_new (conn, cancellable, callback, user_data);
   g_task_set_source_tag (caller_task, g_tls_connection_base_handshake_async);
   g_task_set_priority (caller_task, io_priority);
@@ -1361,9 +1416,9 @@ g_tls_connection_base_handshake_async (GTlsConnection       *conn,
 }
 
 static gboolean
-g_tls_connection_base_handshake_finish (GTlsConnection       *conn,
-                                        GAsyncResult         *result,
-                                        GError              **error)
+g_tls_connection_base_handshake_finish (GTlsConnection  *conn,
+                                        GAsyncResult    *result,
+                                        GError         **error)
 {
   g_return_val_if_fail (g_task_is_valid (result, conn), FALSE);
 
@@ -1417,9 +1472,20 @@ do_implicit_handshake (GTlsConnectionBase  *tls,
 
   /* We have op_mutex */
 
+  g_assert (priv->handshake_context == NULL);
+  if (timeout != 0)
+    {
+      priv->handshake_context = g_main_context_new ();
+      g_main_context_push_thread_default (priv->handshake_context);
+    }
+  else
+    {
+      priv->handshake_context = g_main_context_ref_thread_default ();
+    }
+
   g_assert (priv->implicit_handshake == NULL);
   priv->implicit_handshake = g_task_new (tls, cancellable,
-                                        implicit_handshake_completed,
+                                        timeout ? sync_handshake_thread_completed : NULL,
                                         NULL);
   g_task_set_source_tag (priv->implicit_handshake, do_implicit_handshake);
 
@@ -1440,8 +1506,15 @@ do_implicit_handshake (GTlsConnectionBase  *tls,
       *thread_timeout = timeout;
 
       g_mutex_unlock (&priv->op_mutex);
-      g_task_run_in_thread_sync (priv->implicit_handshake,
-                                 handshake_thread);
+
+      g_task_set_return_on_cancel (priv->implicit_handshake, TRUE);
+      g_task_run_in_thread (priv->implicit_handshake, handshake_thread);
+      crank_sync_handshake_context (tls, cancellable);
+
+      g_main_context_pop_thread_default (priv->handshake_context);
+
+      g_clear_pointer (&priv->handshake_context, g_main_context_unref);
+
       success = finish_handshake (tls,
                                   priv->implicit_handshake,
                                   &my_error);
@@ -1556,6 +1629,7 @@ g_tls_connection_base_read_message (GTlsConnectionBase  *tls,
       }
     else
       {
+        g_assert (G_TLS_CONNECTION_BASE_GET_CLASS (tls)->read_message_fn);
         status = G_TLS_CONNECTION_BASE_GET_CLASS (tls)->
           read_message_fn (tls, vectors, num_vectors, timeout, &nread, cancellable, error);
       }
@@ -1691,6 +1765,7 @@ g_tls_connection_base_write_message (GTlsConnectionBase  *tls,
                    timeout, cancellable, error))
       return -1;
 
+    g_assert (G_TLS_CONNECTION_BASE_GET_CLASS (tls)->read_message_fn);
     status = G_TLS_CONNECTION_BASE_GET_CLASS (tls)->
       write_message_fn (tls, vectors, num_vectors, timeout, &nwrote, cancellable, error);
 
@@ -2001,15 +2076,85 @@ g_tls_connection_base_dtls_shutdown_finish (GDtlsConnection  *conn,
   return g_task_propagate_boolean (G_TASK (result), error);
 }
 
+#if GLIB_CHECK_VERSION(2, 60, 0)
+static void
+g_tls_connection_base_dtls_set_advertised_protocols (GDtlsConnection     *conn,
+                                                     const gchar * const *protocols)
+{
+  g_object_set (conn, "advertised-protocols", protocols, NULL);
+}
+
+const gchar *
+g_tls_connection_base_dtls_get_negotiated_protocol (GDtlsConnection *conn)
+{
+  GTlsConnectionBase *tls = G_TLS_CONNECTION_BASE (conn);
+  GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (gnutls);
+
+  return priv->negotiated_protocol;
+}
+#endif
+
+gboolean
+g_tls_connection_base_is_dtls (GTlsConnectionBase *tls)
+{
+  GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
+
+  return !!priv->base_socket;
+}
+
+GDatagramBased *
+g_tls_connection_base_get_base_socket (GTlsConnectionBase *tls)
+{
+  GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
+
+// FIXME:
+// g_assert (g_tls_connection_base_is_dtls (tls));
+
+  return priv->base_socket;
+}
+
+GIOStream *
+g_tls_connection_base_get_base_iostream (GTlsConnectionBase *tls)
+{
+  GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
+
+// FIXME:
+// g_assert (!g_tls_connection_base_is_dtls (tls));
+
+  return priv->base_iostream;
+}
+
+GInputStream *
+g_tls_connection_base_get_base_istream (GTlsConnectionBase *tls)
+{
+  GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
+
+// FIXME:
+// g_assert (!g_tls_connection_base_is_dtls (tls));
+
+  return priv->base_istream;
+}
+
+GOutputStream *
+g_tls_connection_base_get_base_ostream (GTlsConnectionBase *tls)
+{
+  GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
+
+// FIXME:
+// g_assert (!g_tls_connection_base_is_dtls (tls));
+
+  return priv->base_ostream;
+}
+
 void
-g_tls_connection_base_set_certificate_requested (GTlsConnectionBase *tls)
+g_tls_connection_base_set_missing_requested_client_certificate (GTlsConnectionBase *tls)
 {
   GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
 
   /* FIXME: Assert this is only used on the handshake thread. */
   /* FIXME: Assert it's not already requested? Probably. */
 
-  priv->certificate_requested = TRUE;
+  priv->missing_requested_client_certificate = TRUE;
 }
 
 GError **
@@ -2036,6 +2181,22 @@ g_tls_connection_base_get_write_error (GTlsConnectionBase *tls)
   return &priv->write_error;
 }
 
+gint64
+g_tls_connection_base_get_read_timeout (GTlsConnectionBase *tls)
+{
+  GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
+
+  return priv->read_timeout;
+}
+
+gint64
+g_tls_connection_base_get_write_timeout (GTlsConnectionBase *tls)
+{
+  GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
+
+  return priv->write_timeout;
+}
+
 gboolean
 g_tls_connection_base_is_handshaking (GTlsConnectionBase *tls)
 {
@@ -2074,6 +2235,21 @@ g_tls_connection_base_request_certificate (GTlsConnectionBase  *tls,
   return res != G_TLS_INTERACTION_FAILED;
 }
 
+void
+g_tls_connection_base_buffer_application_data (GTlsConnectionBase *tls,
+                                               guint8             *data,
+                                               gsize               length)
+{
+  GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
+
+// FIXME: Called from handshake thread, needs a mutex!!
+
+  if (!priv->app_data_buf)
+    priv->app_data_buf = g_byte_array_new ();
+
+  g_byte_array_append (priv->app_data_buf, data, length);
+}
+
 static void
 g_tls_connection_base_class_init (GTlsConnectionBaseClass *klass)
 {
@@ -2109,6 +2285,10 @@ g_tls_connection_base_class_init (GTlsConnectionBaseClass *klass)
   g_object_class_override_property (gobject_class, PROP_INTERACTION, "interaction");
   g_object_class_override_property (gobject_class, PROP_PEER_CERTIFICATE, "peer-certificate");
   g_object_class_override_property (gobject_class, PROP_PEER_CERTIFICATE_ERRORS, "peer-certificate-errors");
+#if GLIB_CHECK_VERSION(2, 60, 0)
+  g_object_class_override_property (gobject_class, PROP_ADVERTISED_PROTOCOLS, "advertised-protocols");
+  g_object_class_override_property (gobject_class, PROP_NEGOTIATED_PROTOCOL, "negotiated-protocol");
+#endif
 }
 
 static void
@@ -2120,6 +2300,10 @@ g_tls_connection_base_dtls_connection_iface_init (GDtlsConnectionInterface *ifac
   iface->shutdown = g_tls_connection_base_dtls_shutdown;
   iface->shutdown_async = g_tls_connection_base_dtls_shutdown_async;
   iface->shutdown_finish = g_tls_connection_base_dtls_shutdown_finish;
+#if GLIB_CHECK_VERSION(2, 60, 0)
+  iface->set_advertised_protocols = g_tls_connection_base_dtls_set_advertised_protocols;
+  iface->get_negotiated_protocol = g_tls_connection_gnutls_dtls_get_negotiated_protocol;
+#endif
 }
 
 static void
diff --git a/tls/base/gtlsconnection-base.h b/tls/base/gtlsconnection-base.h
index 2bea4ad..66e2048 100644
--- a/tls/base/gtlsconnection-base.h
+++ b/tls/base/gtlsconnection-base.h
@@ -52,17 +52,21 @@ typedef enum {
 
 struct _GTlsConnectionBaseClass
 {
-  GTlsConnectionClass parent_class;
+  GTlsConnectionClass parent_class;                                      
 
   GTlsConnectionBaseStatus (*request_rehandshake)  (GTlsConnectionBase  *tls,
                                                     gint64               timeout,
                                                     GCancellable        *cancellable,
                                                     GError             **error);
-  GTlsConnectionBaseStatus (*handshake)            (GTlsConnectionBase  *tls,
+  void                     (*prepare_handshake)    (GTlsConnectionBase  *tls,
+                                                    gchar              **advertised_protocols);            
+  GTlsConnectionBaseStatus (*handshake_thread_handshake)
+                                                   (GTlsConnectionBase  *tls,
                                                     gint64               timeout,
                                                     GCancellable        *cancellable,
                                                     GError             **error);
-  GTlsConnectionBaseStatus (*complete_handshake)   (GTlsConnectionBase  *tls,
+  void                     (*complete_handshake)   (GTlsConnectionBase  *tls,
+                                                    gchar              **negotiated_protocol,
                                                     GError             **error);
 
   void                     (*push_io)              (GTlsConnectionBase  *tls,
@@ -110,10 +114,6 @@ struct _GTlsConnectionBaseClass
                                                     GError             **error);
 };
 
-gboolean g_tls_connection_base_accept_peer_certificate (GTlsConnectionBase   *tls,
-                                                        GTlsCertificate      *peer_certificate,
-                                                        GTlsCertificateFlags  peer_certificate_errors);
-
 void g_tls_connection_base_set_peer_certificate (GTlsConnectionBase   *tls,
                                                  GTlsCertificate      *peer_certificate,
                                                  GTlsCertificateFlags  peer_certificate_errors);
@@ -143,6 +143,8 @@ gssize   g_tls_connection_base_write         (GTlsConnectionBase  *tls,
 
 gboolean g_tls_connection_base_check         (GTlsConnectionBase  *tls,
                                               GIOCondition         condition);
+gboolean g_tls_connection_base_base_check    (GTlsConnectionBase  *tls,
+                                              GIOCondition         condition);
 GSource *g_tls_connection_base_create_source (GTlsConnectionBase  *tls,
                                               GIOCondition         condition,
                                               GCancellable        *cancellable);
@@ -153,18 +155,36 @@ gboolean g_tls_connection_base_close_internal (GIOStream      *stream,
                                                GCancellable   *cancellable,
                                                GError        **error);
 
-void     g_tls_connection_base_set_certificate_requested (GTlsConnectionBase *tls);
+gboolean        g_tls_connection_base_is_dtls            (GTlsConnectionBase *tls);
 
-GError **g_tls_connection_base_get_certificate_error     (GTlsConnectionBase *tls);
-GError **g_tls_connection_base_get_read_error            (GTlsConnectionBase *tls);
-GError **g_tls_connection_base_get_write_error           (GTlsConnectionBase *tls);
+GDatagramBased *g_tls_connection_base_get_base_socket    (GTlsConnectionBase *tls);
 
-gboolean g_tls_connection_base_is_handshaking            (GTlsConnectionBase *tls);
+GIOStream              *g_tls_connection_base_get_base_iostream  (GTlsConnectionBase *tls);
+GPollableInputStream   *g_tls_connection_base_get_base_istream   (GTlsConnectionBase *tls);
+GPollableOutputStream  *g_tls_connection_base_get_base_ostream   (GTlsConnectionBase *tls);
 
-gboolean g_tls_connection_base_ever_handshaked           (GTlsConnectionBase *tls);
+void            g_tls_connection_base_set_missing_requested_client_certificate
+                                                                 (GTlsConnectionBase *tls);
 
-gboolean g_tls_connection_base_request_certificate (GTlsConnectionBase  *tls,
-                                                    GError             **error);
+GError        **g_tls_connection_base_get_certificate_error      (GTlsConnectionBase *tls);
+GError        **g_tls_connection_base_get_read_error             (GTlsConnectionBase *tls);
+GError        **g_tls_connection_base_get_write_error            (GTlsConnectionBase *tls);
+
+gint64          g_tls_connection_base_get_read_timeout           (GTlsConnectionBase *tls);
+gint64          g_tls_connection_base_get_write_timeout          (GTlsConnectionBase *tls);
+
+gboolean        g_tls_connection_base_is_handshaking             (GTlsConnectionBase *tls);
+
+gboolean        g_tls_connection_base_ever_handshaked            (GTlsConnectionBase *tls);
+
+gboolean        g_tls_connection_base_request_certificate (GTlsConnectionBase  *tls,
+                                                           GError             **error);
+
+void            g_tls_connection_base_buffer_application_data (GTlsConnectionBase *tls,
+                                                               guint8             *data,
+                                                               gsize               length);
+
+void            g_tls_connection_base_set_advertised_protocols
 
 void GTLS_DEBUG (gpointer    gnutls,
                  const char *message,
diff --git a/tls/gnutls/gtlsclientconnection-gnutls.c b/tls/gnutls/gtlsclientconnection-gnutls.c
index cac2deb..fc5227b 100644
--- a/tls/gnutls/gtlsclientconnection-gnutls.c
+++ b/tls/gnutls/gtlsclientconnection-gnutls.c
@@ -30,6 +30,7 @@
 #include <gnutls/x509.h>
 #include <string.h>
 
+#include "gtlsconnection-base.h"
 #include "gtlsclientconnection-gnutls.h"
 #include "gtlsbackend-gnutls.h"
 #include "gtlscertificate-gnutls.h"
@@ -56,8 +57,6 @@ struct _GTlsClientConnectionGnutls
   GBytes *session_id;
   GBytes *session_data;
 
-  gboolean requested_cert_missing;
-  GError *cert_error;
   GPtrArray *accepted_cas;
 
   gnutls_pcert_st *pcert;
@@ -189,7 +188,6 @@ g_tls_client_connection_gnutls_finalize (GObject *object)
   g_clear_pointer (&gnutls->accepted_cas, g_ptr_array_unref);
   g_clear_pointer (&gnutls->session_id, g_bytes_unref);
   g_clear_pointer (&gnutls->session_data, g_bytes_unref);
-  g_clear_error (&gnutls->cert_error);
 
   clear_gnutls_certificate_copy (gnutls);
 
@@ -324,6 +322,7 @@ g_tls_client_connection_gnutls_retrieve_function (gnutls_session_t
                                                   unsigned int                 *pcert_length,
                                                   gnutls_privkey_t             *pkey)
 {
+  GTlsConnectionBase *tls = gnutls_transport_get_ptr (session);
   GTlsClientConnectionGnutls *gnutls = gnutls_transport_get_ptr (session);
   GTlsConnectionGnutls *conn = G_TLS_CONNECTION_GNUTLS (gnutls);
   GPtrArray *accepted_cas;
@@ -353,9 +352,9 @@ g_tls_client_connection_gnutls_retrieve_function (gnutls_session_t
   if (*pcert_length == 0)
     {
       g_tls_certificate_gnutls_copy_free (*pcert, *pcert_length, *pkey);
-      g_clear_error (&gnutls->cert_error);
+      g_clear_error (g_tls_connection_base_get_certificate_error (tls));
 
-      if (g_tls_connection_gnutls_request_certificate (conn, &gnutls->cert_error))
+      if (g_tls_connection_gnutls_request_certificate (conn, g_tls_connection_base_get_certificate_error 
(tls)))
         g_tls_connection_gnutls_get_certificate (conn, pcert, pcert_length, pkey);
 
       if (*pcert_length == 0)
@@ -365,7 +364,7 @@ g_tls_client_connection_gnutls_retrieve_function (gnutls_session_t
           /* If there is still no client certificate, this connection will
            * probably fail, but no reason to give up: let's try anyway.
            */
-          gnutls->requested_cert_missing = TRUE;
+          g_tls_connection_base_set_missing_requested_client_certificate (tls);
           return 0;
         }
     }
@@ -377,7 +376,7 @@ g_tls_client_connection_gnutls_retrieve_function (gnutls_session_t
       /* No private key. GnuTLS expects it to be non-null if pcert_length is
        * nonzero, so we have to abort now.
        */
-      gnutls->requested_cert_missing = TRUE;
+      g_tls_connection_base_set_missing_requested_client_certificate (tls);
       return -1;
     }
 
@@ -399,17 +398,19 @@ g_tls_client_connection_gnutls_failed (GTlsConnectionGnutls *conn)
     g_tls_backend_gnutls_remove_session (GNUTLS_CLIENT, gnutls->session_id);
 }
 
-static void
-g_tls_client_connection_gnutls_begin_handshake (GTlsConnectionGnutls *conn)
+static GTlsConnectionBaseStatus
+g_tls_client_connection_gnutls_handshake (GTlsConnectionBase  *tls,
+                                          GCancellable        *cancellable,
+                                          GError             **error)
 {
-  GTlsClientConnectionGnutls *gnutls = G_TLS_CLIENT_CONNECTION_GNUTLS (conn);
+  GTlsClientConnectionGnutls *gnutls = G_TLS_CLIENT_CONNECTION_GNUTLS (tls);
 
   g_tls_client_connection_gnutls_compute_session_id (gnutls);
 
   /* Try to get a cached session */
   if (gnutls->session_data_override)
     {
-      gnutls_session_set_data (g_tls_connection_gnutls_get_session (conn),
+      gnutls_session_set_data (g_tls_connection_gnutls_get_session (G_TLS_CONNECTION_GNUTLS (tls)),
                                g_bytes_get_data (gnutls->session_data, NULL),
                                g_bytes_get_size (gnutls->session_data));
     }
@@ -420,7 +421,7 @@ g_tls_client_connection_gnutls_begin_handshake (GTlsConnectionGnutls *conn)
       session_data = g_tls_backend_gnutls_lookup_session (GNUTLS_CLIENT, gnutls->session_id);
       if (session_data)
         {
-          gnutls_session_set_data (g_tls_connection_gnutls_get_session (conn),
+          gnutls_session_set_data (g_tls_connection_gnutls_get_session (G_TLS_CONNECTION_GNUTLS (tls)),
                                    g_bytes_get_data (session_data, NULL),
                                    g_bytes_get_size (session_data));
           g_clear_pointer (&gnutls->session_data, g_bytes_unref);
@@ -428,35 +429,22 @@ g_tls_client_connection_gnutls_begin_handshake (GTlsConnectionGnutls *conn)
         }
     }
 
-  gnutls->requested_cert_missing = FALSE;
+  return G_TLS_CONNECTION_BASE_CLASS (g_tls_client_connection_gnutls_parent_class)->
+    handshake (tls, cancellable, error);
 }
 
-static void
-g_tls_client_connection_gnutls_finish_handshake (GTlsConnectionGnutls  *conn,
-                                                 GError               **inout_error)
+static GTlsConnectionBaseStatus
+g_tls_client_connection_gnutls_complete_handshake (GTlsConnectionBase  *tls,
+                                                   GError             **error)
 {
-  GTlsClientConnectionGnutls *gnutls = G_TLS_CLIENT_CONNECTION_GNUTLS (conn);
+  GTlsClientConnectionGnutls *gnutls = G_TLS_CLIENT_CONNECTION_GNUTLS (tls);
+  GTlsConnectionBaseStatus status;
   int resumed;
 
-  g_assert (inout_error != NULL);
+  status = G_TLS_CONNECTION_BASE_CLASS (g_tls_client_connection_gnutls_parent_class)->complete_handshake 
(tls, error);
 
-  if (*inout_error != NULL && gnutls->requested_cert_missing)
-    {
-      g_clear_error (inout_error);
-      if (gnutls->cert_error)
-        {
-          *inout_error = gnutls->cert_error;
-          gnutls->cert_error = NULL;
-        }
-      else
-        {
-          g_set_error_literal (inout_error, G_TLS_ERROR, G_TLS_ERROR_CERTIFICATE_REQUIRED,
-                               _("Server required TLS certificate"));
-        }
-    }
-
-  resumed = gnutls_session_is_resumed (g_tls_connection_gnutls_get_session (conn));
-  if (*inout_error || !resumed)
+  resumed = gnutls_session_is_resumed (g_tls_connection_gnutls_get_session (G_TLS_CONNECTION_GNUTLS (tls)));
+  if (status != G_TLS_CONNECTION_BASE_OK || !resumed)
     {
       /* Clear session data since the server did not accept what we provided. */
       gnutls->session_data_override = FALSE;
@@ -465,11 +453,11 @@ g_tls_client_connection_gnutls_finish_handshake (GTlsConnectionGnutls  *conn,
         g_tls_backend_gnutls_remove_session (GNUTLS_CLIENT, gnutls->session_id);
     }
 
-  if (!*inout_error && !resumed)
+  if (status == G_TLS_CONNECTION_BASE_OK && !resumed)
     {
       gnutls_datum_t session_datum;
 
-      if (gnutls_session_get_data2 (g_tls_connection_gnutls_get_session (conn),
+      if (gnutls_session_get_data2 (g_tls_connection_gnutls_get_session (G_TLS_CONNECTION_GNUTLS (tls)),
                                     &session_datum) == 0)
         {
           gnutls->session_data = g_bytes_new_with_free_func (session_datum.data,
@@ -483,6 +471,8 @@ g_tls_client_connection_gnutls_finish_handshake (GTlsConnectionGnutls  *conn,
                                                 gnutls->session_data);
         }
     }
+
+  return status;
 }
 
 static void
@@ -508,15 +498,17 @@ static void
 g_tls_client_connection_gnutls_class_init (GTlsClientConnectionGnutlsClass *klass)
 {
   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
-  GTlsConnectionGnutlsClass *connection_gnutls_class = G_TLS_CONNECTION_GNUTLS_CLASS (klass);
+  GTlsConnectionBaseClass *base_class = G_TLS_CONNECTION_BASE_CLASS (klass);
+  GTlsConnectionGnutlsClass *gnutls_class = G_TLS_CONNECTION_GNUTLS_CLASS (klass);
 
   gobject_class->get_property = g_tls_client_connection_gnutls_get_property;
   gobject_class->set_property = g_tls_client_connection_gnutls_set_property;
   gobject_class->finalize     = g_tls_client_connection_gnutls_finalize;
 
-  connection_gnutls_class->failed           = g_tls_client_connection_gnutls_failed;
-  connection_gnutls_class->begin_handshake  = g_tls_client_connection_gnutls_begin_handshake;
-  connection_gnutls_class->finish_handshake = g_tls_client_connection_gnutls_finish_handshake;
+  base_class->begin_handshake    = g_tls_client_connection_gnutls_handshake;
+  base_class->complete_handshake = g_tls_client_connection_gnutls_complete_handshake;
+
+  gnutls_class->failed           = g_tls_client_connection_gnutls_failed;
 
   g_object_class_override_property (gobject_class, PROP_VALIDATION_FLAGS, "validation-flags");
   g_object_class_override_property (gobject_class, PROP_SERVER_IDENTITY, "server-identity");
diff --git a/tls/gnutls/gtlsconnection-gnutls.c b/tls/gnutls/gtlsconnection-gnutls.c
index 774d668..0d1bd37 100644
--- a/tls/gnutls/gtlsconnection-gnutls.c
+++ b/tls/gnutls/gtlsconnection-gnutls.c
@@ -36,9 +36,6 @@
 #include "gtlsbackend-gnutls.h"
 #include "gtlscertificate-gnutls.h"
 #include "gtlsclientconnection-gnutls.h"
-#include "gtlsinputstream-gnutls.h"
-#include "gtlsoutputstream-gnutls.h"
-#include "gtlsserverconnection-gnutls.h"
 
 #ifdef G_OS_WIN32
 #include <winsock2.h>
@@ -53,35 +50,6 @@
 #include <glib/gi18n-lib.h>
 #include <glib/gprintf.h>
 
-/*
- * GTlsConnectionGnutls is the base abstract implementation of TLS and DTLS
- * support, for both the client and server side of a connection. The choice
- * between TLS and DTLS is made by setting the base-io-stream or
- * base-socket properties — exactly one of them must be set at
- * construction time.
- *
- * Client and server specific code is in the GTlsClientConnectionGnutls and
- * GTlsServerConnectionGnutls concrete subclasses, although the line about where
- * code is put is a little blurry, and there are various places in
- * GTlsConnectionGnutls which check G_IS_TLS_CLIENT_CONNECTION(self) to switch
- * to a client-only code path.
- *
- * This abstract class implements a lot of interfaces:
- *  • Derived from GTlsConnection (itself from GIOStream), for TLS and streaming
- *    communications.
- *  • Implements GDtlsConnection and GDatagramBased, for DTLS and datagram
- *    communications.
- *  • Implements GInitable for failable GnuTLS initialisation.
- *
- * The GTlsClientConnectionGnutls and GTlsServerConnectionGnutls subclasses are
- * both derived from GTlsConnectionGnutls (and hence GIOStream), and both
- * implement the relevant TLS and DTLS interfaces:
- *  • GTlsClientConnection
- *  • GDtlsClientConnection
- *  • GTlsServerConnection
- *  • GDtlsServerConnection
- */
-
 static ssize_t g_tls_connection_gnutls_push_func (gnutls_transport_ptr_t  transport_data,
                                                   const void             *buf,
                                                   size_t                  buflen);
@@ -95,156 +63,23 @@ static ssize_t g_tls_connection_gnutls_pull_func (gnutls_transport_ptr_t  transp
 static int     g_tls_connection_gnutls_pull_timeout_func (gnutls_transport_ptr_t transport_data,
                                                           unsigned int           ms);
 
-
-static void     g_tls_connection_gnutls_initable_iface_init (GInitableIface  *iface);
-static gboolean g_tls_connection_gnutls_initable_init       (GInitable       *initable,
-                                                             GCancellable    *cancellable,
-                                                             GError         **error);
-static void     g_tls_connection_gnutls_dtls_connection_iface_init (GDtlsConnectionInterface *iface);
-static void     g_tls_connection_gnutls_datagram_based_iface_init  (GDatagramBasedInterface  *iface);
+static void g_tls_connection_gnutls_initable_iface_init (GInitableIface *iface);
 
 static void g_tls_connection_gnutls_init_priorities (void);
 
 static int verify_certificate_cb (gnutls_session_t session);
 
-static gboolean do_implicit_handshake (GTlsConnectionGnutls  *gnutls,
-                                       gint64                 timeout,
-                                       GCancellable          *cancellable,
-                                       GError               **error);
-static gboolean finish_handshake (GTlsConnectionGnutls  *gnutls,
-                                  GTask                 *task,
-                                  GError               **error);
-
-enum
-{
-  PROP_0,
-  /* For this class: */
-  PROP_BASE_IO_STREAM,
-  PROP_BASE_SOCKET,
-  /* For GTlsConnection and GDtlsConnection: */
-  PROP_REQUIRE_CLOSE_NOTIFY,
-  PROP_REHANDSHAKE_MODE,
-  PROP_USE_SYSTEM_CERTDB,
-  PROP_DATABASE,
-  PROP_CERTIFICATE,
-  PROP_INTERACTION,
-  PROP_PEER_CERTIFICATE,
-  PROP_PEER_CERTIFICATE_ERRORS,
-#if GLIB_CHECK_VERSION(2, 60, 0)
-  PROP_ADVERTISED_PROTOCOLS,
-  PROP_NEGOTIATED_PROTOCOL,
-#endif
-};
-
 typedef struct
 {
-  /* When operating in stream mode, as a GTlsConnection. These are
-   * mutually-exclusive with base_socket. There are two different
-   * GIOStreams here: (a) base_io_stream and (b) the GTlsConnectionGnutls
-   * itself. base_io_stream is the GIOStream used to create the GTlsConnection,
-   * and corresponds to the GTlsConnection::base-io-stream property.
-   * base_istream and base_ostream are the GInputStream and GOutputStream,
-   * respectively, of base_io_stream. These are for the underlying sockets that
-   * don't know about TLS.
-   *
-   * Then the GTlsConnectionGnutls also has tls_istream and tls_ostream which
-   * wrap the aforementioned base streams with a TLS session.
-   *
-   * When operating in datagram mode, none of these are used.
-   */
-  GIOStream *base_io_stream;
-  GPollableInputStream *base_istream;
-  GPollableOutputStream *base_ostream;
-  GInputStream *tls_istream;
-  GOutputStream *tls_ostream;
-
-  /* When operating in datagram mode, as a GDtlsConnection, the
-   * GTlsConnectionGnutls is itself the DTLS GDatagramBased. It uses base_socket
-   * for the underlying I/O. It is mutually-exclusive with base_io_stream and
-   * the other streams.
-   */
-  GDatagramBased *base_socket;
-
   gnutls_certificate_credentials_t creds;
   gnutls_session_t session;
-
-  GTlsCertificate *certificate, *peer_certificate;
-  GTlsCertificateFlags peer_certificate_errors;
-
-  GMutex verify_certificate_mutex;
-  GCond verify_certificate_condition;
-  gboolean peer_certificate_accepted;
-  gboolean peer_certificate_examined;
-
-  gboolean require_close_notify;
-  GTlsRehandshakeMode rehandshake_mode;
-  gboolean is_system_certdb;
-  GTlsDatabase *database;
-  gboolean database_is_unset;
-
-  /* need_handshake means the next claim_op() will get diverted into
-   * an implicit handshake (unless it's an OP_HANDSHAKE or OP_CLOSE*).
-   * need_finish_handshake means the next claim_op() will get diverted
-   * into finish_handshake() (unless it's an OP_CLOSE*).
-   *
-   * handshaking is TRUE as soon as a handshake thread is queued. For
-   * a sync handshake it becomes FALSE after finish_handshake()
-   * completes in the calling thread, but for an async implicit
-   * handshake, it becomes FALSE (and need_finish_handshake becomes
-   * TRUE) at the end of the handshaking thread (and then the next
-   * non-close op will call finish_handshake()). We can't just wait
-   * for handshake_thread_completed() to run, because it's possible
-   * that its main loop is being blocked by a synchronous op which is
-   * waiting for handshaking to become FALSE...
-   *
-   * started_handshake indicates that the current handshake attempt
-   * got at least as far as calling gnutls_handshake() (and so any
-   * error should be copied to handshake_error and returned on all
-   * future operations). ever_handshaked indicates that TLS has
-   * been successfully negotiated at some point.
-   */
-  gboolean need_handshake, need_finish_handshake, sync_handshake_completed;
-  gboolean started_handshake, handshaking, ever_handshaked;
-  GMainContext *handshake_context;
-  GTask *implicit_handshake;
-  GError *handshake_error;
-  GByteArray *app_data_buf;
-
-  /* read_closed means the read direction has closed; write_closed similarly.
-   * If (and only if) both are set, the entire GTlsConnection is closed. */
-  gboolean read_closing, read_closed;
-  gboolean write_closing, write_closed;
-
-  GTlsInteraction *interaction;
   gchar *interaction_id;
-
-#if GLIB_CHECK_VERSION(2, 60, 0)
-  gchar **advertised_protocols;
-  gchar *negotiated_protocol;
-#endif
-
-  GMutex        op_mutex;
-  GCancellable *waiting_for_op;
-
-  gboolean      reading;
-  gint64        read_timeout;
-  GError       *read_error;
-  GCancellable *read_cancellable;
-
-  gboolean      writing;
-  gint64        write_timeout;
-  GError       *write_error;
-  GCancellable *write_cancellable;
 } GTlsConnectionGnutlsPrivate;
 
-G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GTlsConnectionGnutls, g_tls_connection_gnutls, G_TYPE_TLS_CONNECTION,
+G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GTlsConnectionGnutls, g_tls_connection_gnutls, G_TYPE_TLS_CONNECTION_BASE,
                                   G_ADD_PRIVATE (GTlsConnectionGnutls);
                                   G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
                                                          g_tls_connection_gnutls_initable_iface_init);
-                                  G_IMPLEMENT_INTERFACE (G_TYPE_DATAGRAM_BASED,
-                                                         g_tls_connection_gnutls_datagram_based_iface_init);
-                                  G_IMPLEMENT_INTERFACE (G_TYPE_DTLS_CONNECTION,
-                                                         g_tls_connection_gnutls_dtls_connection_iface_init);
                                   g_tls_connection_gnutls_init_priorities ();
                                   );
 
@@ -261,17 +96,8 @@ g_tls_connection_gnutls_init (GTlsConnectionGnutls *gnutls)
   g_mutex_init (&priv->verify_certificate_mutex);
   g_cond_init (&priv->verify_certificate_condition);
 
-  priv->need_handshake = TRUE;
-
-  priv->database_is_unset = TRUE;
-  priv->is_system_certdb = TRUE;
-
   unique_id = g_atomic_int_add (&unique_interaction_id, 1);
   priv->interaction_id = g_strdup_printf ("gtls:%d", unique_id);
-
-  priv->waiting_for_op = g_cancellable_new ();
-  g_cancellable_cancel (priv->waiting_for_op);
-  g_mutex_init (&priv->op_mutex);
 }
 
 /* First field is "fallback", second is "allow unsafe rehandshaking" */
@@ -358,19 +184,11 @@ g_tls_connection_gnutls_set_handshake_priority (GTlsConnectionGnutls *gnutls)
     }
   else
     fallback = FALSE;
-  unsafe_rehandshake = (priv->rehandshake_mode == G_TLS_REHANDSHAKE_UNSAFELY);
+  unsafe_rehandshake = g_tls_connection_get_rehandshake_mode (G_TLS_CONNECTION (gnutls)) == 
G_TLS_REHANDSHAKE_UNSAFELY;
   gnutls_priority_set (priv->session,
                        priorities[fallback][unsafe_rehandshake]);
 }
 
-static gboolean
-g_tls_connection_gnutls_is_dtls (GTlsConnectionGnutls *gnutls)
-{
-  GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-
-  return (priv->base_socket != NULL);
-}
-
 static gboolean
 g_tls_connection_gnutls_initable_init (GInitable     *initable,
                                        GCancellable  *cancellable,
@@ -378,17 +196,22 @@ g_tls_connection_gnutls_initable_init (GInitable     *initable,
 {
   GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (initable);
   GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
+  GIOStream *base_io_stream = NULL;
+  GDatagramBased *base_socket = NULL;
   gboolean client = G_IS_TLS_CLIENT_CONNECTION (gnutls);
   guint flags = client ? GNUTLS_CLIENT : GNUTLS_SERVER;
   int status;
 
-  g_return_val_if_fail ((priv->base_istream == NULL) ==
-                        (priv->base_ostream == NULL), FALSE);
-  g_return_val_if_fail ((priv->base_socket == NULL) !=
-                        (priv->base_istream == NULL), FALSE);
+// FIXME: Avoid g_object_get?
+  g_object_get (gnutls,
+                "base-io-stream", &base_io_stream,
+                "base-socket", &base_socket,
+                NULL);
 
-  /* Check whether to use DTLS or TLS. */
-  if (g_tls_connection_gnutls_is_dtls (gnutls))
+  /* Ensure we are in TLS mode or DTLS mode. */
+  g_return_val_if_fail (!!base_io_stream != !!base_socket, FALSE);
+
+  if (base_socket)
     flags |= GNUTLS_DATAGRAM;
 
   gnutls_init (&priv->session, flags);
@@ -416,7 +239,7 @@ g_tls_connection_gnutls_initable_init (GInitable     *initable,
   gnutls_transport_set_ptr (priv->session, gnutls);
 
   /* GDatagramBased supports vectored I/O; GPollableOutputStream does not. */
-  if (priv->base_socket != NULL)
+  if (base_socket != NULL)
     {
       gnutls_transport_set_vec_push_function (priv->session,
                                               g_tls_connection_gnutls_vec_push_func);
@@ -426,13 +249,6 @@ g_tls_connection_gnutls_initable_init (GInitable     *initable,
   if (flags & GNUTLS_DATAGRAM)
     gnutls_dtls_set_mtu (priv->session, 1400);
 
-  /* Create output streams if operating in streaming mode. */
-  if (!(flags & GNUTLS_DATAGRAM))
-    {
-      priv->tls_istream = g_tls_input_stream_gnutls_new (gnutls);
-      priv->tls_ostream = g_tls_output_stream_gnutls_new (gnutls);
-    }
-
   return TRUE;
 }
 
@@ -442,228 +258,16 @@ g_tls_connection_gnutls_finalize (GObject *object)
   GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (object);
   GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
 
-  g_clear_object (&priv->base_io_stream);
-  g_clear_object (&priv->base_socket);
-
-  g_clear_object (&priv->tls_istream);
-  g_clear_object (&priv->tls_ostream);
-
   if (priv->session)
     gnutls_deinit (priv->session);
   if (priv->creds)
     gnutls_certificate_free_credentials (priv->creds);
 
-  g_clear_object (&priv->database);
-  g_clear_object (&priv->certificate);
-  g_clear_object (&priv->peer_certificate);
-
-  g_mutex_clear (&priv->verify_certificate_mutex);
-  g_cond_clear (&priv->verify_certificate_condition);
-
-  g_clear_pointer (&priv->app_data_buf, g_byte_array_unref);
-
   g_free (priv->interaction_id);
-  g_clear_object (&priv->interaction);
-
-#if GLIB_CHECK_VERSION(2, 60, 0)
-  g_clear_pointer (&priv->advertised_protocols, g_strfreev);
-  g_clear_pointer (&priv->negotiated_protocol, g_free);
-#endif
-
-  g_clear_error (&priv->handshake_error);
-  g_clear_error (&priv->read_error);
-  g_clear_error (&priv->write_error);
-
-  g_clear_pointer (&priv->handshake_context, g_main_context_unref);
-
-  /* This must always be NULL here, as it holds a reference to @gnutls as
-   * its source object. However, we clear it anyway just in case this changes
-   * in future. */
-  g_clear_object (&priv->implicit_handshake);
-
-  g_clear_object (&priv->read_cancellable);
-  g_clear_object (&priv->write_cancellable);
-
-  g_clear_object (&priv->waiting_for_op);
-  g_mutex_clear (&priv->op_mutex);
 
   G_OBJECT_CLASS (g_tls_connection_gnutls_parent_class)->finalize (object);
 }
 
-static void
-g_tls_connection_gnutls_get_property (GObject    *object,
-                                      guint       prop_id,
-                                      GValue     *value,
-                                      GParamSpec *pspec)
-{
-  GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (object);
-  GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-  GTlsBackend *backend;
-
-  switch (prop_id)
-    {
-    case PROP_BASE_IO_STREAM:
-      g_value_set_object (value, priv->base_io_stream);
-      break;
-
-    case PROP_BASE_SOCKET:
-      g_value_set_object (value, priv->base_socket);
-      break;
-
-    case PROP_REQUIRE_CLOSE_NOTIFY:
-      g_value_set_boolean (value, priv->require_close_notify);
-      break;
-
-    case PROP_REHANDSHAKE_MODE:
-      g_value_set_enum (value, priv->rehandshake_mode);
-      break;
-
-    case PROP_USE_SYSTEM_CERTDB:
-      g_value_set_boolean (value, priv->is_system_certdb);
-      break;
-
-    case PROP_DATABASE:
-      if (priv->database_is_unset)
-        {
-          backend = g_tls_backend_get_default ();
-          priv->database =  g_tls_backend_get_default_database (backend);
-          priv->database_is_unset = FALSE;
-        }
-      g_value_set_object (value, priv->database);
-      break;
-
-    case PROP_CERTIFICATE:
-      g_value_set_object (value, priv->certificate);
-      break;
-
-    case PROP_INTERACTION:
-      g_value_set_object (value, priv->interaction);
-      break;
-
-    case PROP_PEER_CERTIFICATE:
-      g_value_set_object (value, priv->peer_certificate);
-      break;
-
-    case PROP_PEER_CERTIFICATE_ERRORS:
-      g_value_set_flags (value, priv->peer_certificate_errors);
-      break;
-
-#if GLIB_CHECK_VERSION(2, 60, 0)
-    case PROP_ADVERTISED_PROTOCOLS:
-      g_value_set_boxed (value, priv->advertised_protocols);
-      break;
-
-    case PROP_NEGOTIATED_PROTOCOL:
-      g_value_set_string (value, priv->negotiated_protocol);
-      break;
-#endif
-
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-    }
-}
-
-static void
-g_tls_connection_gnutls_set_property (GObject      *object,
-                                      guint         prop_id,
-                                      const GValue *value,
-                                      GParamSpec   *pspec)
-{
-  GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (object);
-  GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-  GInputStream *istream;
-  GOutputStream *ostream;
-  gboolean system_certdb;
-  GTlsBackend *backend;
-
-  switch (prop_id)
-    {
-    case PROP_BASE_IO_STREAM:
-      g_assert (g_value_get_object (value) == NULL ||
-                priv->base_socket == NULL);
-
-      if (priv->base_io_stream)
-        {
-          g_object_unref (priv->base_io_stream);
-          priv->base_istream = NULL;
-          priv->base_ostream = NULL;
-        }
-      priv->base_io_stream = g_value_dup_object (value);
-      if (!priv->base_io_stream)
-        return;
-
-      istream = g_io_stream_get_input_stream (priv->base_io_stream);
-      ostream = g_io_stream_get_output_stream (priv->base_io_stream);
-
-      if (G_IS_POLLABLE_INPUT_STREAM (istream) &&
-          g_pollable_input_stream_can_poll (G_POLLABLE_INPUT_STREAM (istream)))
-        priv->base_istream = G_POLLABLE_INPUT_STREAM (istream);
-      if (G_IS_POLLABLE_OUTPUT_STREAM (ostream) &&
-          g_pollable_output_stream_can_poll (G_POLLABLE_OUTPUT_STREAM (ostream)))
-        priv->base_ostream = G_POLLABLE_OUTPUT_STREAM (ostream);
-      break;
-
-    case PROP_BASE_SOCKET:
-      g_assert (g_value_get_object (value) == NULL ||
-                priv->base_io_stream == NULL);
-
-      g_clear_object (&priv->base_socket);
-      priv->base_socket = g_value_dup_object (value);
-      break;
-
-    case PROP_REQUIRE_CLOSE_NOTIFY:
-      priv->require_close_notify = g_value_get_boolean (value);
-      break;
-
-    case PROP_REHANDSHAKE_MODE:
-      priv->rehandshake_mode = g_value_get_enum (value);
-      break;
-
-    case PROP_USE_SYSTEM_CERTDB:
-      system_certdb = g_value_get_boolean (value);
-      if (system_certdb != priv->is_system_certdb)
-        {
-          g_clear_object (&priv->database);
-          if (system_certdb)
-            {
-              backend = g_tls_backend_get_default ();
-              priv->database = g_tls_backend_get_default_database (backend);
-            }
-          priv->is_system_certdb = system_certdb;
-          priv->database_is_unset = FALSE;
-        }
-      break;
-
-    case PROP_DATABASE:
-      g_clear_object (&priv->database);
-      priv->database = g_value_dup_object (value);
-      priv->is_system_certdb = FALSE;
-      priv->database_is_unset = FALSE;
-      break;
-
-    case PROP_CERTIFICATE:
-      if (priv->certificate)
-        g_object_unref (priv->certificate);
-      priv->certificate = g_value_dup_object (value);
-      break;
-
-    case PROP_INTERACTION:
-      g_clear_object (&priv->interaction);
-      priv->interaction = g_value_dup_object (value);
-      break;
-
-#if GLIB_CHECK_VERSION(2, 60, 0)
-    case PROP_ADVERTISED_PROTOCOLS:
-      g_clear_pointer (&priv->advertised_protocols, g_strfreev);
-      priv->advertised_protocols = g_value_dup_boxed (value);
-      break;
-#endif
-
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-    }
-}
-
 gnutls_certificate_credentials_t
 g_tls_connection_gnutls_get_credentials (GTlsConnectionGnutls *gnutls)
 {
@@ -693,809 +297,190 @@ g_tls_connection_gnutls_get_certificate (GTlsConnectionGnutls  *gnutls,
 
   if (cert)
     {
-      g_tls_certificate_gnutls_copy (G_TLS_CERTIFICATE_GNUTLS (cert),
-                                     priv->interaction_id,
-                                     pcert, pcert_length, pkey);
-    }
-  else
-    {
-      *pcert = NULL;
-      *pcert_length = 0;
-      *pkey = NULL;
-    }
-}
-
-typedef enum {
-  G_TLS_CONNECTION_GNUTLS_OP_HANDSHAKE,
-  G_TLS_CONNECTION_GNUTLS_OP_READ,
-  G_TLS_CONNECTION_GNUTLS_OP_WRITE,
-  G_TLS_CONNECTION_GNUTLS_OP_CLOSE_READ,
-  G_TLS_CONNECTION_GNUTLS_OP_CLOSE_WRITE,
-  G_TLS_CONNECTION_GNUTLS_OP_CLOSE_BOTH,
-} GTlsConnectionGnutlsOp;
-
-static gboolean
-claim_op (GTlsConnectionGnutls    *gnutls,
-          GTlsConnectionGnutlsOp   op,
-          gint64                   timeout,
-          GCancellable            *cancellable,
-          GError                 **error)
-{
-  GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-
- try_again:
-  if (g_cancellable_set_error_if_cancelled (cancellable, error))
-    return FALSE;
-
-  g_mutex_lock (&priv->op_mutex);
-
-  if (((op == G_TLS_CONNECTION_GNUTLS_OP_HANDSHAKE ||
-        op == G_TLS_CONNECTION_GNUTLS_OP_READ) &&
-       (priv->read_closing || priv->read_closed)) ||
-      ((op == G_TLS_CONNECTION_GNUTLS_OP_HANDSHAKE ||
-        op == G_TLS_CONNECTION_GNUTLS_OP_WRITE) &&
-       (priv->write_closing || priv->write_closed)))
-    {
-      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_CLOSED,
-                           _("Connection is closed"));
-      g_mutex_unlock (&priv->op_mutex);
-      return FALSE;
-    }
-
-  if (priv->handshake_error &&
-      op != G_TLS_CONNECTION_GNUTLS_OP_CLOSE_BOTH &&
-      op != G_TLS_CONNECTION_GNUTLS_OP_CLOSE_READ &&
-      op != G_TLS_CONNECTION_GNUTLS_OP_CLOSE_WRITE)
-    {
-      if (error)
-        *error = g_error_copy (priv->handshake_error);
-      g_mutex_unlock (&priv->op_mutex);
-      return FALSE;
-    }
-
-  if (op != G_TLS_CONNECTION_GNUTLS_OP_HANDSHAKE)
-    {
-      if (op != G_TLS_CONNECTION_GNUTLS_OP_CLOSE_BOTH &&
-          op != G_TLS_CONNECTION_GNUTLS_OP_CLOSE_READ &&
-          op != G_TLS_CONNECTION_GNUTLS_OP_CLOSE_WRITE &&
-          priv->need_handshake)
-        {
-          priv->need_handshake = FALSE;
-          priv->handshaking = TRUE;
-          if (!do_implicit_handshake (gnutls, timeout, cancellable, error))
-            {
-              g_mutex_unlock (&priv->op_mutex);
-              return FALSE;
-            }
-        }
-
-      if (priv->need_finish_handshake &&
-          priv->implicit_handshake)
-        {
-          GError *my_error = NULL;
-          gboolean success;
-
-          priv->need_finish_handshake = FALSE;
-
-          g_mutex_unlock (&priv->op_mutex);
-          success = finish_handshake (gnutls, priv->implicit_handshake, &my_error);
-          g_clear_object (&priv->implicit_handshake);
-          g_clear_pointer (&priv->handshake_context, g_main_context_unref);
-          g_mutex_lock (&priv->op_mutex);
-
-          if (op != G_TLS_CONNECTION_GNUTLS_OP_CLOSE_BOTH &&
-              op != G_TLS_CONNECTION_GNUTLS_OP_CLOSE_READ &&
-              op != G_TLS_CONNECTION_GNUTLS_OP_CLOSE_WRITE &&
-              (!success || g_cancellable_set_error_if_cancelled (cancellable, &my_error)))
-            {
-              g_propagate_error (error, my_error);
-              g_mutex_unlock (&priv->op_mutex);
-              return FALSE;
-            }
-
-          g_clear_error (&my_error);
-        }
-    }
-
-  if (priv->handshaking &&
-      timeout != 0 &&
-      g_main_context_is_owner (priv->handshake_context))
-    {
-      /* Cannot perform a blocking operation during a handshake on the
-       * same thread that triggered the handshake. The only way this can
-       * occur is if the application is doing something weird in its
-       * accept-certificate callback. Allowing a blocking op would stall
-       * the handshake (forever, if there's no timeout). Even a close
-       * op would deadlock here.
-       */
-      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Cannot perform blocking operation during 
TLS handshake"));
-      g_mutex_unlock (&priv->op_mutex);
-      return FALSE;
-    }
-
-  if ((op != G_TLS_CONNECTION_GNUTLS_OP_WRITE && priv->reading) ||
-      (op != G_TLS_CONNECTION_GNUTLS_OP_READ && priv->writing) ||
-      (op != G_TLS_CONNECTION_GNUTLS_OP_HANDSHAKE && priv->handshaking))
-    {
-      GPollFD fds[2];
-      int nfds;
-      gint64 start_time;
-      gint result = 1;  /* if the loop is never entered, it’s as if we cancelled early */
-
-      g_cancellable_reset (priv->waiting_for_op);
-
-      g_mutex_unlock (&priv->op_mutex);
-
-      if (timeout == 0)
-        {
-          /* Intentionally not translated because this is not a fatal error to be
-           * presented to the user, and to avoid this showing up in profiling. */
-          g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK, "Operation would block");
-          return FALSE;
-        }
-
-      g_cancellable_make_pollfd (priv->waiting_for_op, &fds[0]);
-      if (g_cancellable_make_pollfd (cancellable, &fds[1]))
-        nfds = 2;
-      else
-        nfds = 1;
-
-      /* Convert from microseconds to milliseconds. */
-      if (timeout != -1)
-        timeout = timeout / 1000;
-
-      /* Poll until cancellation or the timeout is reached. */
-      start_time = g_get_monotonic_time ();
-
-      while (!g_cancellable_is_cancelled (priv->waiting_for_op) &&
-             !g_cancellable_is_cancelled (cancellable))
-        {
-          result = g_poll (fds, nfds, timeout);
-
-          if (result == 0)
-            break;
-          if (result != -1 || errno != EINTR)
-            continue;
-
-          if (timeout != -1)
-            {
-              timeout -= (g_get_monotonic_time () - start_time) / 1000;
-              if (timeout < 0)
-                timeout = 0;
-            }
-        }
-
-      if (nfds > 1)
-        g_cancellable_release_fd (cancellable);
-
-      if (result == 0)
-        {
-          g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT,
-                               _("Socket I/O timed out"));
-          return FALSE;
-        }
-
-      goto try_again;
-    }
-
-  if (op == G_TLS_CONNECTION_GNUTLS_OP_HANDSHAKE)
-    {
-      priv->handshaking = TRUE;
-      priv->need_handshake = FALSE;
-    }
-  if (op == G_TLS_CONNECTION_GNUTLS_OP_CLOSE_BOTH ||
-      op == G_TLS_CONNECTION_GNUTLS_OP_CLOSE_READ)
-    priv->read_closing = TRUE;
-  if (op == G_TLS_CONNECTION_GNUTLS_OP_CLOSE_BOTH ||
-      op == G_TLS_CONNECTION_GNUTLS_OP_CLOSE_WRITE)
-    priv->write_closing = TRUE;
-
-  if (op != G_TLS_CONNECTION_GNUTLS_OP_WRITE)
-    priv->reading = TRUE;
-  if (op != G_TLS_CONNECTION_GNUTLS_OP_READ)
-    priv->writing = TRUE;
-
-  g_mutex_unlock (&priv->op_mutex);
-  return TRUE;
-}
-
-static void
-yield_op (GTlsConnectionGnutls   *gnutls,
-          GTlsConnectionGnutlsOp  op)
-{
-  GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-
-  g_mutex_lock (&priv->op_mutex);
-
-  if (op == G_TLS_CONNECTION_GNUTLS_OP_HANDSHAKE)
-    priv->handshaking = FALSE;
-  if (op == G_TLS_CONNECTION_GNUTLS_OP_CLOSE_BOTH ||
-      op == G_TLS_CONNECTION_GNUTLS_OP_CLOSE_READ)
-    priv->read_closing = FALSE;
-  if (op == G_TLS_CONNECTION_GNUTLS_OP_CLOSE_BOTH ||
-      op == G_TLS_CONNECTION_GNUTLS_OP_CLOSE_WRITE)
-    priv->write_closing = FALSE;
-
-  if (op != G_TLS_CONNECTION_GNUTLS_OP_WRITE)
-    priv->reading = FALSE;
-  if (op != G_TLS_CONNECTION_GNUTLS_OP_READ)
-    priv->writing = FALSE;
-
-  g_cancellable_cancel (priv->waiting_for_op);
-  g_mutex_unlock (&priv->op_mutex);
-}
-
-static void
-begin_gnutls_io (GTlsConnectionGnutls  *gnutls,
-                 GIOCondition           direction,
-                 gint64                 timeout,
-                 GCancellable          *cancellable)
-{
-  GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-
-  g_assert (direction & (G_IO_IN | G_IO_OUT));
-
-  if (direction & G_IO_IN)
-    {
-      priv->read_timeout = timeout;
-      priv->read_cancellable = cancellable;
-      g_clear_error (&priv->read_error);
-    }
-
-  if (direction & G_IO_OUT)
-    {
-      priv->write_timeout = timeout;
-      priv->write_cancellable = cancellable;
-      g_clear_error (&priv->write_error);
-    }
-}
-
-static int
-end_gnutls_io (GTlsConnectionGnutls  *gnutls,
-               GIOCondition           direction,
-               int                    status,
-               GError               **error,
-               const char            *err_prefix);
-
-static int
-end_gnutls_io (GTlsConnectionGnutls  *gnutls,
-               GIOCondition           direction,
-               int                    status,
-               GError               **error,
-               const char            *err_prefix)
-{
-  GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-  GError *my_error = NULL;
-
-  g_assert (direction & (G_IO_IN | G_IO_OUT));
-  g_assert (!error || !*error);
-
-  /* We intentionally do not check for GNUTLS_E_INTERRUPTED here
-   * Instead, the caller may poll for the source to become ready again.
-   * (Note that GTlsOutputStreamGnutls and GTlsInputStreamGnutls inherit
-   * from GPollableOutputStream and GPollableInputStream, respectively.)
-   * See also the comment in set_gnutls_error().
-   */
-  if (status == GNUTLS_E_AGAIN ||
-      status == GNUTLS_E_WARNING_ALERT_RECEIVED)
-    return GNUTLS_E_AGAIN;
-
-  if (direction & G_IO_IN)
-    {
-      priv->read_cancellable = NULL;
-      if (status < 0)
-        {
-          my_error = priv->read_error;
-          priv->read_error = NULL;
-        }
-      else
-        g_clear_error (&priv->read_error);
-    }
-  if (direction & G_IO_OUT)
-    {
-      priv->write_cancellable = NULL;
-      if (status < 0 && !my_error)
-        {
-          my_error = priv->write_error;
-          priv->write_error = NULL;
-        }
-      else
-        g_clear_error (&priv->write_error);
-    }
-
-  if (status >= 0)
-    return status;
-
-  if (priv->handshaking && !priv->ever_handshaked)
-    {
-      if (g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_FAILED) ||
-          g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_BROKEN_PIPE) ||
-          status == GNUTLS_E_UNEXPECTED_PACKET_LENGTH ||
-          status == GNUTLS_E_DECRYPTION_FAILED ||
-          status == GNUTLS_E_UNSUPPORTED_VERSION_PACKET)
-        {
-          g_clear_error (&my_error);
-          g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS,
-                               _("Peer failed to perform TLS handshake"));
-          return GNUTLS_E_PULL_ERROR;
-        }
-    }
-
-  if (my_error)
-    {
-      if (!g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK) &&
-          !g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT))
-        G_TLS_CONNECTION_GNUTLS_GET_CLASS (gnutls)->failed (gnutls);
-      g_propagate_error (error, my_error);
-      return status;
-    }
-  else if (status == GNUTLS_E_REHANDSHAKE)
-    {
-      if (priv->rehandshake_mode == G_TLS_REHANDSHAKE_NEVER)
-        {
-          g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_MISC,
-                               _("Peer requested illegal TLS rehandshake"));
-          return GNUTLS_E_PULL_ERROR;
-        }
-
-      g_mutex_lock (&priv->op_mutex);
-      if (!priv->handshaking)
-        priv->need_handshake = TRUE;
-      g_mutex_unlock (&priv->op_mutex);
-      return status;
-    }
-  else if (status == GNUTLS_E_PREMATURE_TERMINATION)
-    {
-      if (priv->handshaking && !priv->ever_handshaked)
-        {
-          g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS,
-                               _("Peer failed to perform TLS handshake"));
-          return GNUTLS_E_PULL_ERROR;
-        }
-      else if (priv->require_close_notify)
-        {
-          g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_EOF,
-                               _("TLS connection closed unexpectedly"));
-          G_TLS_CONNECTION_GNUTLS_GET_CLASS (gnutls)->failed (gnutls);
-          return status;
-        }
-      else
-        return 0;
-    }
-  else if (status == GNUTLS_E_NO_CERTIFICATE_FOUND
-#ifdef GNUTLS_E_CERTIFICATE_REQUIRED
-           || status == GNUTLS_E_CERTIFICATE_REQUIRED /* Added in GnuTLS 3.6.7 */
-#endif
-          )
-    {
-      g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_CERTIFICATE_REQUIRED,
-                           _("TLS connection peer did not send a certificate"));
-      return status;
-    }
-  else if (status == GNUTLS_E_CERTIFICATE_ERROR)
-    {
-      g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
-                   _("Unacceptable TLS certificate"));
-      return status;
-    }
-  else if (status == GNUTLS_E_FATAL_ALERT_RECEIVED)
-    {
-      g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_MISC,
-                   _("Peer sent fatal TLS alert: %s"),
-                   gnutls_alert_get_name (gnutls_alert_get (priv->session)));
-      return status;
-    }
-  else if (status == GNUTLS_E_INAPPROPRIATE_FALLBACK)
-    {
-      g_set_error_literal (error, G_TLS_ERROR,
-#if GLIB_CHECK_VERSION(2, 60, 0)
-                           G_TLS_ERROR_INAPPROPRIATE_FALLBACK,
-#else
-                           G_TLS_ERROR_MISC,
-#endif
-                           _("Protocol version downgrade attack detected"));
-      return status;
-    }
-  else if (status == GNUTLS_E_LARGE_PACKET)
-    {
-      guint mtu = gnutls_dtls_get_data_mtu (priv->session);
-      g_set_error (error, G_IO_ERROR, G_IO_ERROR_MESSAGE_TOO_LARGE,
-                   ngettext ("Message is too large for DTLS connection; maximum is %u byte",
-                             "Message is too large for DTLS connection; maximum is %u bytes", mtu), mtu);
-      return status;
-    }
-  else if (status == GNUTLS_E_TIMEDOUT)
-    {
-      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT,
-                           _("The operation timed out"));
-      return status;
-    }
-
-  if (error)
-    {
-      *error = g_error_new (G_TLS_ERROR, G_TLS_ERROR_MISC, "%s: %s",
-          err_prefix, gnutls_strerror (status));
-    }
-  return status;
-}
-
-#define BEGIN_GNUTLS_IO(gnutls, direction, timeout, cancellable)        \
-  begin_gnutls_io (gnutls, direction, timeout, cancellable);            \
-  do {
-
-#define END_GNUTLS_IO(gnutls, direction, ret, errmsg, err)              \
-  } while ((ret = end_gnutls_io (gnutls, direction, ret, err, errmsg)) == GNUTLS_E_AGAIN);
-
-/* Checks whether the underlying base stream or GDatagramBased meets
- * @condition. */
-static gboolean
-g_tls_connection_gnutls_base_check (GTlsConnectionGnutls  *gnutls,
-                                    GIOCondition           condition)
-{
-  GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-
-  if (g_tls_connection_gnutls_is_dtls (gnutls))
-    return g_datagram_based_condition_check (priv->base_socket,
-                                             condition);
-  else if (condition & G_IO_IN)
-    return g_pollable_input_stream_is_readable (priv->base_istream);
-  else if (condition & G_IO_OUT)
-    return g_pollable_output_stream_is_writable (priv->base_ostream);
-  else
-    g_assert_not_reached ();
-}
-
-/* Checks whether the (D)TLS stream meets @condition; not the underlying base
- * stream or GDatagramBased. */
-gboolean
-g_tls_connection_gnutls_check (GTlsConnectionGnutls  *gnutls,
-                               GIOCondition           condition)
-{
-  GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-
-  /* Racy, but worst case is that we just get WOULD_BLOCK back */
-  if (priv->need_finish_handshake)
-    return TRUE;
-
-  /* If a handshake or close is in progress, then tls_istream and
-   * tls_ostream are blocked, regardless of the base stream status.
-   */
-  if (priv->handshaking)
-    return FALSE;
-
-  if (((condition & G_IO_IN) && priv->read_closing) ||
-      ((condition & G_IO_OUT) && priv->write_closing))
-    return FALSE;
-
-  /* Defer to the base stream or GDatagramBased. */
-  return g_tls_connection_gnutls_base_check (gnutls, condition);
-}
-
-typedef struct {
-  GSource               source;
-
-  GTlsConnectionGnutls *gnutls;
-  /* Either a GDatagramBased (datagram mode), or a GPollableInputStream or
-   * GPollableOutputStream (streaming mode):
-   */
-  GObject              *base;
-
-  GSource              *child_source;
-  GIOCondition          condition;
-
-  gboolean              io_waiting;
-  gboolean              op_waiting;
-} GTlsConnectionGnutlsSource;
-
-static gboolean
-gnutls_source_prepare (GSource *source,
-                       gint    *timeout)
-{
-  *timeout = -1;
-  return FALSE;
-}
-
-static gboolean
-gnutls_source_check (GSource *source)
-{
-  return FALSE;
-}
-
-/* Use a custom dummy callback instead of g_source_set_dummy_callback(), as that
- * uses a GClosure and is slow. (The GClosure is necessary to deal with any
- * function prototype.) */
-static gboolean
-dummy_callback (gpointer data)
-{
-  return G_SOURCE_CONTINUE;
-}
-
-static void
-gnutls_source_sync (GTlsConnectionGnutlsSource *gnutls_source)
-{
-  GTlsConnectionGnutls *gnutls = gnutls_source->gnutls;
-  GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-  gboolean io_waiting, op_waiting;
-
-  /* Was the source destroyed earlier in this main context iteration? */
-  if (g_source_is_destroyed ((GSource *)gnutls_source))
-    return;
-
-  g_mutex_lock (&priv->op_mutex);
-  if (((gnutls_source->condition & G_IO_IN) && priv->reading) ||
-      ((gnutls_source->condition & G_IO_OUT) && priv->writing) ||
-      (priv->handshaking && !priv->need_finish_handshake))
-    op_waiting = TRUE;
-  else
-    op_waiting = FALSE;
-
-  if (!op_waiting && !priv->need_handshake &&
-      !priv->need_finish_handshake)
-    io_waiting = TRUE;
-  else
-    io_waiting = FALSE;
-  g_mutex_unlock (&priv->op_mutex);
-
-  if (op_waiting == gnutls_source->op_waiting &&
-      io_waiting == gnutls_source->io_waiting)
-    return;
-  gnutls_source->op_waiting = op_waiting;
-  gnutls_source->io_waiting = io_waiting;
-
-  if (gnutls_source->child_source)
-    {
-      g_source_remove_child_source ((GSource *)gnutls_source,
-                                    gnutls_source->child_source);
-      g_source_unref (gnutls_source->child_source);
-    }
-
-  if (op_waiting)
-    gnutls_source->child_source = g_cancellable_source_new (priv->waiting_for_op);
-  else if (io_waiting && G_IS_DATAGRAM_BASED (gnutls_source->base))
-    gnutls_source->child_source = g_datagram_based_create_source (priv->base_socket, 
gnutls_source->condition, NULL);
-  else if (io_waiting && G_IS_POLLABLE_INPUT_STREAM (gnutls_source->base))
-    gnutls_source->child_source = g_pollable_input_stream_create_source (priv->base_istream, NULL);
-  else if (io_waiting && G_IS_POLLABLE_OUTPUT_STREAM (gnutls_source->base))
-    gnutls_source->child_source = g_pollable_output_stream_create_source (priv->base_ostream, NULL);
-  else
-    gnutls_source->child_source = g_timeout_source_new (0);
-
-  g_source_set_callback (gnutls_source->child_source, dummy_callback, NULL, NULL);
-  g_source_add_child_source ((GSource *)gnutls_source, gnutls_source->child_source);
-}
-
-static gboolean
-gnutls_source_dispatch (GSource     *source,
-                        GSourceFunc  callback,
-                        gpointer     user_data)
-{
-  GDatagramBasedSourceFunc datagram_based_func = (GDatagramBasedSourceFunc)callback;
-  GPollableSourceFunc pollable_func = (GPollableSourceFunc)callback;
-  GTlsConnectionGnutlsSource *gnutls_source = (GTlsConnectionGnutlsSource *)source;
-  gboolean ret;
-
-  if (G_IS_DATAGRAM_BASED (gnutls_source->base))
-    ret = (*datagram_based_func) (G_DATAGRAM_BASED (gnutls_source->base),
-                                  gnutls_source->condition, user_data);
-  else
-    ret = (*pollable_func) (gnutls_source->base, user_data);
-
-  if (ret)
-    gnutls_source_sync (gnutls_source);
-
-  return ret;
-}
-
-static void
-gnutls_source_finalize (GSource *source)
-{
-  GTlsConnectionGnutlsSource *gnutls_source = (GTlsConnectionGnutlsSource *)source;
-
-  g_object_unref (gnutls_source->gnutls);
-  g_source_unref (gnutls_source->child_source);
-}
-
-static gboolean
-g_tls_connection_gnutls_source_closure_callback (GObject  *stream,
-                                                 gpointer  data)
-{
-  GClosure *closure = data;
-
-  GValue param = { 0, };
-  GValue result_value = { 0, };
-  gboolean result;
-
-  g_value_init (&result_value, G_TYPE_BOOLEAN);
-
-  g_value_init (&param, G_TYPE_OBJECT);
-  g_value_set_object (&param, stream);
-
-  g_closure_invoke (closure, &result_value, 1, &param, NULL);
-
-  result = g_value_get_boolean (&result_value);
-  g_value_unset (&result_value);
-  g_value_unset (&param);
-
-  return result;
-}
-
-static gboolean
-g_tls_connection_gnutls_source_dtls_closure_callback (GObject  *stream,
-                                                      GIOCondition condition,
-                                                      gpointer  data)
-{
-  GClosure *closure = data;
-
-  GValue param[2] = { G_VALUE_INIT, G_VALUE_INIT };
-  GValue result_value = G_VALUE_INIT;
-  gboolean result;
-
-  g_value_init (&result_value, G_TYPE_BOOLEAN);
-
-  g_value_init (&param[0], G_TYPE_DATAGRAM_BASED);
-  g_value_set_object (&param[0], stream);
-  g_value_init (&param[1], G_TYPE_IO_CONDITION);
-  g_value_set_flags (&param[1], condition);
-
-  g_closure_invoke (closure, &result_value, 2, param, NULL);
-
-  result = g_value_get_boolean (&result_value);
-  g_value_unset (&result_value);
-  g_value_unset (&param[0]);
-  g_value_unset (&param[1]);
-
-  return result;
-}
-
-static GSourceFuncs gnutls_tls_source_funcs =
-{
-  gnutls_source_prepare,
-  gnutls_source_check,
-  gnutls_source_dispatch,
-  gnutls_source_finalize,
-  (GSourceFunc)g_tls_connection_gnutls_source_closure_callback,
-  (GSourceDummyMarshal)g_cclosure_marshal_generic
-};
-
-static GSourceFuncs gnutls_dtls_source_funcs =
-{
-  gnutls_source_prepare,
-  gnutls_source_check,
-  gnutls_source_dispatch,
-  gnutls_source_finalize,
-  (GSourceFunc)g_tls_connection_gnutls_source_dtls_closure_callback,
-  (GSourceDummyMarshal)g_cclosure_marshal_generic
-};
-
-GSource *
-g_tls_connection_gnutls_create_source (GTlsConnectionGnutls  *gnutls,
-                                       GIOCondition           condition,
-                                       GCancellable          *cancellable)
-{
-  GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-  GSource *source, *cancellable_source;
-  GTlsConnectionGnutlsSource *gnutls_source;
-
-  if (g_tls_connection_gnutls_is_dtls (gnutls))
-    {
-      source = g_source_new (&gnutls_dtls_source_funcs,
-                             sizeof (GTlsConnectionGnutlsSource));
+      g_tls_certificate_gnutls_copy (G_TLS_CERTIFICATE_GNUTLS (cert),
+                                     priv->interaction_id,
+                                     pcert, pcert_length, pkey);
     }
   else
     {
-      source = g_source_new (&gnutls_tls_source_funcs,
-                             sizeof (GTlsConnectionGnutlsSource));
+      *pcert = NULL;
+      *pcert_length = 0;
+      *pkey = NULL;
     }
-  g_source_set_name (source, "GTlsConnectionGnutlsSource");
-  gnutls_source = (GTlsConnectionGnutlsSource *)source;
-  gnutls_source->gnutls = g_object_ref (gnutls);
-  gnutls_source->condition = condition;
-  if (g_tls_connection_gnutls_is_dtls (gnutls))
-    gnutls_source->base = G_OBJECT (gnutls);
-  else if (priv->tls_istream != NULL && condition & G_IO_IN)
-    gnutls_source->base = G_OBJECT (priv->tls_istream);
-  else if (priv->tls_ostream != NULL && condition & G_IO_OUT)
-    gnutls_source->base = G_OBJECT (priv->tls_ostream);
-  else
-    g_assert_not_reached ();
+}
+
+static GTlsConnectionBaseStatus
+end_gnutls_io (GTlsConnectionGnutls  *gnutls,
+               GIOCondition           direction,
+               int                    ret,
+               GError               **error,
+               const char            *err_prefix)
+{
+  GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
+  GTlsConnectionBase *tls = G_TLS_CONNECTION_BASE (gnutls);
+  GTlsConnectionBaseStatus status;
+  gboolean handshaking;
+  gboolean ever_handshaked;
+  GError *my_error = NULL;
 
-  gnutls_source->op_waiting = (gboolean) -1;
-  gnutls_source->io_waiting = (gboolean) -1;
-  gnutls_source_sync (gnutls_source);
+  /* We intentionally do not check for GNUTLS_E_INTERRUPTED here
+   * Instead, the caller may poll for the source to become ready again.
+   * (Note that GTlsOutputStreamGnutls and GTlsInputStreamGnutls inherit
+   * from GPollableOutputStream and GPollableInputStream, respectively.)
+   * See also the comment in set_gnutls_error().
+   */
+  if (ret == GNUTLS_E_AGAIN ||
+      ret == GNUTLS_E_WARNING_ALERT_RECEIVED)
+    return G_TLS_CONNECTION_BASE_TRY_AGAIN;
 
-  if (cancellable)
+  status = g_tls_connection_base_pop_io (tls, direction, ret >= 0, &my_error);
+  if (status == G_TLS_CONNECTION_BASE_OK ||
+      status == G_TLS_CONNECTION_BASE_WOULD_BLOCK ||
+      status == G_TLS_CONNECTION_BASE_TIMED_OUT)
     {
-      cancellable_source = g_cancellable_source_new (cancellable);
-      g_source_set_dummy_callback (cancellable_source);
-      g_source_add_child_source (source, cancellable_source);
-      g_source_unref (cancellable_source);
+      if (my_error)
+        g_propagate_error (error, my_error);
+      return status;
     }
 
-  return source;
-}
+  g_assert (status == G_TLS_CONNECTION_BASE_ERROR);
 
-static GSource *
-g_tls_connection_gnutls_dtls_create_source (GDatagramBased  *datagram_based,
-                                            GIOCondition     condition,
-                                            GCancellable    *cancellable)
-{
-  GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (datagram_based);
+// FIXME: Right place?
+  G_TLS_CONNECTION_GNUTLS_GET_CLASS (gnutls)->failed (gnutls);
 
-  return g_tls_connection_gnutls_create_source (gnutls, condition, cancellable);
-}
+  handshaking = g_tls_connection_base_is_handshaking (tls);
+  ever_handshaked = g_tls_connection_base_ever_handshaked (tls);
 
-static GIOCondition
-g_tls_connection_gnutls_condition_check (GDatagramBased  *datagram_based,
-                                         GIOCondition     condition)
-{
-  GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (datagram_based);
+  if (handshaking && !ever_handshaked)
+    {
+      if (status == GNUTLS_E_UNEXPECTED_PACKET_LENGTH ||
+          status == GNUTLS_E_DECRYPTION_FAILED ||
+          status == GNUTLS_E_UNSUPPORTED_VERSION_PACKET)
+        {
+          g_clear_error (&my_error);
+          g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS,
+                               _("Peer failed to perform TLS handshake"));
+          return G_TLS_CONNECTION_BASE_ERROR;
+        }
+    }
 
-  return (g_tls_connection_gnutls_check (gnutls, condition)) ? condition : 0;
-}
+  if (ret == GNUTLS_E_REHANDSHAKE)
+    {
+      if (g_tls_connection_get_rehandshake_mode (G_TLS_CONNECTION (gnutls)) == G_TLS_REHANDSHAKE_NEVER)
+        {
+          g_clear_error (&my_error);
+          g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_MISC,
+                               _("Peer requested illegal TLS rehandshake"));
+          return G_TLS_CONNECTION_BASE_ERROR;
+        }
 
-static gboolean
-g_tls_connection_gnutls_condition_wait (GDatagramBased  *datagram_based,
-                                        GIOCondition     condition,
-                                        gint64           timeout,
-                                        GCancellable    *cancellable,
-                                        GError         **error)
-{
-  GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (datagram_based);
-  GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-  GPollFD fds[2];
-  guint n_fds;
-  gint result = 1;  /* if the loop is never entered, it’s as if we cancelled early */
-  gint64 start_time;
+      return G_TLS_CONNECTION_BASE_REHANDSHAKE;
+    }
 
-  if (g_cancellable_set_error_if_cancelled (cancellable, error))
-    return FALSE;
+  if (ret == GNUTLS_E_PREMATURE_TERMINATION)
+    {
+      if (handshaking && !ever_handshaked)
+        {
+          g_clear_error (&my_error);
+          g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS,
+                               _("Peer failed to perform TLS handshake"));
+          return G_TLS_CONNECTION_BASE_ERROR;
+        }
 
-  /* Convert from microseconds to milliseconds. */
-  if (timeout != -1)
-    timeout = timeout / 1000;
+      if (g_tls_connection_get_require_close_notify (G_TLS_CONNECTION (gnutls)))
+        {
+          g_clear_error (&my_error);
+          g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_EOF,
+                               _("TLS connection closed unexpectedly"));
+          return G_TLS_CONNECTION_BASE_ERROR;
+        }
 
-  start_time = g_get_monotonic_time ();
+      return G_TLS_CONNECTION_BASE_OK;
+    }
 
-  g_cancellable_make_pollfd (priv->waiting_for_op, &fds[0]);
-  n_fds = 1;
+  if (ret == GNUTLS_E_NO_CERTIFICATE_FOUND
+#ifdef GNUTLS_E_CERTIFICATE_REQUIRED
+           || ret == GNUTLS_E_CERTIFICATE_REQUIRED /* Added in GnuTLS 3.6.7 */
+#endif
+          )
+    {
+      g_clear_error (&my_error);
+      g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_CERTIFICATE_REQUIRED,
+                           _("TLS connection peer did not send a certificate"));
+      return G_TLS_CONNECTION_BASE_ERROR;
+    }
 
-  if (g_cancellable_make_pollfd (cancellable, &fds[1]))
-    n_fds++;
+  if (ret == GNUTLS_E_CERTIFICATE_ERROR)
+    {
+      g_clear_error (&my_error);
+      g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+                   _("Unacceptable TLS certificate"));
+      return G_TLS_CONNECTION_BASE_ERROR;
+    }
 
-  while (!g_tls_connection_gnutls_condition_check (datagram_based, condition) &&
-         !g_cancellable_is_cancelled (cancellable))
+  if (ret == GNUTLS_E_FATAL_ALERT_RECEIVED)
     {
-      result = g_poll (fds, n_fds, timeout);
-      if (result == 0)
-        break;
-      if (result != -1 || errno != EINTR)
-        continue;
+      g_clear_error (&my_error);
+      g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_MISC,
+                   _("Peer sent fatal TLS alert: %s"),
+                   gnutls_alert_get_name (gnutls_alert_get (priv->session)));
+      return G_TLS_CONNECTION_BASE_ERROR;
+    }
 
-      if (timeout != -1)
-        {
-          timeout -= (g_get_monotonic_time () - start_time) / 1000;
-          if (timeout < 0)
-            timeout = 0;
-        }
+  if (ret == GNUTLS_E_INAPPROPRIATE_FALLBACK)
+    {
+      g_clear_error (&my_error);
+      g_set_error_literal (error, G_TLS_ERROR,
+#if GLIB_CHECK_VERSION(2, 60, 0)
+                           G_TLS_ERROR_INAPPROPRIATE_FALLBACK,
+#else
+                           G_TLS_ERROR_MISC,
+#endif
+                           _("Protocol version downgrade attack detected"));
+      return G_TLS_CONNECTION_BASE_ERROR;
     }
 
-  if (n_fds > 1)
-    g_cancellable_release_fd (cancellable);
+  if (ret == GNUTLS_E_LARGE_PACKET)
+    {
+      guint mtu = gnutls_dtls_get_data_mtu (priv->session);
+      g_clear_error (&my_error);
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_MESSAGE_TOO_LARGE,
+                   ngettext ("Message is too large for DTLS connection; maximum is %u byte",
+                             "Message is too large for DTLS connection; maximum is %u bytes", mtu), mtu);
+      return G_TLS_CONNECTION_BASE_ERROR;
+    }
 
-  if (result == 0)
+  if (ret == GNUTLS_E_TIMEDOUT)
     {
+      g_clear_error (&my_error);
       g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT,
-                           _("Socket I/O timed out"));
-      return FALSE;
+                           _("The operation timed out"));
+      return G_TLS_CONNECTION_BASE_ERROR;
     }
 
-  return !g_cancellable_set_error_if_cancelled (cancellable, error);
+  g_propagate_error (error, my_error);
+  if (error && !*error)
+    {
+      *error = g_error_new (G_TLS_ERROR, G_TLS_ERROR_MISC, "%s: %s",
+                            err_prefix, gnutls_strerror (status));
+    }
+  return G_TLS_CONNECTION_BASE_ERROR;
 }
 
+#define BEGIN_GNUTLS_IO(gnutls, direction, timeout, cancellable)        \
+  g_tls_connection_base_push_io (G_TLS_CONNECTION_BASE (gnutls),        \
+                                 direction, timeout, cancellable);      \
+  do {
+
+#define END_GNUTLS_IO(gnutls, direction, ret, status, errmsg, err)     \
+    status = end_gnutls_io (gnutls, direction, ret, err, errmsg, gnutls_strerror (ret));       \
+  } while (status == G_TLS_CONNECTION_BASE_TRY_AGAIN);
+
 static void
 set_gnutls_error (GTlsConnectionGnutls *gnutls,
                   GError               *error)
 {
+  GTlsConnectionBase *tls = G_TLS_CONNECTION_BASE (gnutls);
   GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
 
   /* We set EINTR rather than EAGAIN for G_IO_ERROR_WOULD_BLOCK so
@@ -1511,7 +496,7 @@ set_gnutls_error (GTlsConnectionGnutls *gnutls,
     {
       /* Return EAGAIN while handshaking so that GnuTLS handles retries for us
        * internally in its handshaking code. */
-      if (priv->base_socket && priv->handshaking)
+      if (g_tls_connection_base_is_dtls (tls) && g_tls_connection_base_is_handshaking (tls))
         gnutls_transport_set_errno (priv->session, EAGAIN);
       else
         gnutls_transport_set_errno (priv->session, EINTR);
@@ -1529,43 +514,44 @@ g_tls_connection_gnutls_pull_func (gnutls_transport_ptr_t  transport_data,
                                    void                   *buf,
                                    size_t                  buflen)
 {
+  GTlsConnectionBase *tls = transport_data;
   GTlsConnectionGnutls *gnutls = transport_data;
   GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
   ssize_t ret;
 
-  /* If priv->read_error is non-%NULL when we're called, it means
-   * that an error previously occurred, but gnutls decided not to
+  /* If read_error is nonnull when we're called, it means
+   * that an error previously occurred, but GnuTLS decided not to
    * propagate it. So it's correct for us to just clear it. (Usually
    * this means it ignored an EAGAIN after a short read, and now
    * we'll return EAGAIN again, which it will obey this time.)
    */
-  g_clear_error (&priv->read_error);
+  g_clear_error (g_tls_connection_base_get_read_error (tls));
 
-  if (g_tls_connection_gnutls_is_dtls (gnutls))
+  if (g_tls_connection_base_is_dtls (tls))
     {
       GInputVector vector = { buf, buflen };
       GInputMessage message = { NULL, &vector, 1, 0, 0, NULL, NULL };
 
-      ret = g_datagram_based_receive_messages (priv->base_socket,
+      ret = g_datagram_based_receive_messages (g_tls_connection_base_get_base_socket (tls),
                                                &message, 1, 0,
-                                               priv->handshaking ? 0 : priv->read_timeout,
-                                               priv->read_cancellable,
-                                               &priv->read_error);
+                                               g_tls_connection_base_is_handshaking (tls) ? 0 : 
g_tls_connection_base_get_read_timeout (tls),
+                                               g_tls_connection_base_get_read_cancellable (tls),
+                                               g_tls_connection_base_get_read_error (tls));
 
       if (ret > 0)
         ret = message.bytes_received;
     }
   else
     {
-      ret = g_pollable_stream_read (G_INPUT_STREAM (priv->base_istream),
+      ret = g_pollable_stream_read (G_INPUT_STREAM (g_tls_connection_base_get_base_istream (tls)),
                                     buf, buflen,
-                                    (priv->read_timeout != 0),
-                                    priv->read_cancellable,
-                                    &priv->read_error);
+                                    g_tls_connection_base_get_read_timeout (tls) != 0,
+                                    g_tls_connection_base_get_read_cancellable (tls),
+                                    g_tls_connection_base_get_read_error (tls));
     }
 
   if (ret < 0)
-    set_gnutls_error (gnutls, priv->read_error);
+    set_gnutls_error (gnutls, *g_tls_connection_base_get_read_error (tls));
 
   return ret;
 }
@@ -1575,38 +561,39 @@ g_tls_connection_gnutls_push_func (gnutls_transport_ptr_t  transport_data,
                                    const void             *buf,
                                    size_t                  buflen)
 {
+  GTlsConnectionbase *tls = transport_data;
   GTlsConnectionGnutls *gnutls = transport_data;
   GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
   ssize_t ret;
 
   /* See comment in pull_func. */
-  g_clear_error (&priv->write_error);
+  g_clear_error (g_tls_connection_base_get_write_error (tls));
 
-  if (g_tls_connection_gnutls_is_dtls (gnutls))
+  if (g_tls_connection_base_is_dtls (tls))
     {
       GOutputVector vector = { buf, buflen };
       GOutputMessage message = { NULL, &vector, 1, 0, NULL, 0 };
 
-      ret = g_datagram_based_send_messages (priv->base_socket,
+      ret = g_datagram_based_send_messages (g_tls_connection_base_get_base_socket (tls),
                                             &message, 1, 0,
-                                            priv->write_timeout,
-                                            priv->write_cancellable,
-                                            &priv->write_error);
+                                            g_tls_connection_base_get_write_timeout (tls),
+                                            g_tls_connection_base_get_write_cancellable (tls),
+                                            g_tls_connection_base_get_write_error (tls));
 
       if (ret > 0)
         ret = message.bytes_sent;
     }
   else
     {
-      ret = g_pollable_stream_write (G_OUTPUT_STREAM (priv->base_ostream),
+      ret = g_pollable_stream_write (G_OUTPUT_STREAM (g_tls_connection_base_get_base_ostream (tls)),
                                      buf, buflen,
-                                     (priv->write_timeout != 0),
-                                     priv->write_cancellable,
-                                     &priv->write_error);
+                                     g_tls_connection_base_get_write_timeout (tls) != 0,
+                                     g_tls_connection_base_get_write_cancellable (tls),
+                                     g_tls_connection_base_get_write_error (tls));
     }
 
   if (ret < 0)
-    set_gnutls_error (gnutls, priv->write_error);
+    set_gnutls_error (gnutls, *g_tls_connection_base_get_write_error (tls));
 
   return ret;
 }
@@ -1616,17 +603,17 @@ g_tls_connection_gnutls_vec_push_func (gnutls_transport_ptr_t  transport_data,
                                        const giovec_t         *iov,
                                        int                     iovcnt)
 {
+  GTlsConnectionBase *tls = transport_data;
   GTlsConnectionGnutls *gnutls = transport_data;
   GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
   ssize_t ret;
   GOutputMessage message = { NULL, };
   GOutputVector *vectors;
 
-  /* This function should only be set if we’re using base_socket. */
-  g_assert (priv->base_socket != NULL);
+  g_assert (g_tls_connection_base_is_dtls (tls));
 
   /* See comment in pull_func. */
-  g_clear_error (&priv->write_error);
+  g_clear_error (g_tls_connection_base_get_write_error (tls));
 
   /* this entire expression will be evaluated at compile time */
   if (sizeof *iov == sizeof *vectors &&
@@ -1655,16 +642,16 @@ g_tls_connection_gnutls_vec_push_func (gnutls_transport_ptr_t  transport_data,
       message.num_vectors = iovcnt;
     }
 
-  ret = g_datagram_based_send_messages (priv->base_socket,
+  ret = g_datagram_based_send_messages (g_tls_connection_base_get_base_socket (tls),
                                         &message, 1, 0,
-                                        priv->write_timeout,
-                                        priv->write_cancellable,
-                                        &priv->write_error);
+                                        g_tls_connection_base_get_write_timeout (tls),
+                                        g_tls_connection_base_get_write_cancellable (tls),
+                                        g_tls_connection_base_get_write_error (tls));
 
   if (ret > 0)
     ret = message.bytes_sent;
   else if (ret < 0)
-    set_gnutls_error (gnutls, priv->write_error);
+    set_gnutls_error (gnutls, *g_tls_connection_base_get_write_error (tls));
 
   return ret;
 }
@@ -1706,12 +693,13 @@ static int
 g_tls_connection_gnutls_pull_timeout_func (gnutls_transport_ptr_t transport_data,
                                            unsigned int           ms)
 {
+  GTlsConnectionBase *tls = transport_data;
   GTlsConnectionGnutls *gnutls = transport_data;
   GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
 
   /* Fast path. */
-  if (g_tls_connection_gnutls_base_check (gnutls, G_IO_IN) ||
-      g_cancellable_is_cancelled (priv->read_cancellable))
+  if (g_tls_connection_base_base_check (tls, G_IO_IN) ||
+      g_cancellable_is_cancelled (g_tls_connection_base_get_read_cancellable (tls)))
     return 1;
 
   /* If @ms is 0, GnuTLS wants an instant response, so there’s no need to
@@ -1732,15 +720,17 @@ g_tls_connection_gnutls_pull_timeout_func (gnutls_transport_ptr_t transport_data
       /* Create a read source. We cannot use g_source_set_ready_time() on this
        * to combine it with the @timeout_source, as that could mess with the
        * internals of the #GDatagramBased’s #GSource implementation. */
-      if (g_tls_connection_gnutls_is_dtls (gnutls))
+      if (g_tls_connection_base_is_dtls (tls))
         {
-          read_source = g_datagram_based_create_source (priv->base_socket, G_IO_IN, NULL);
+          read_source = g_datagram_based_create_source (g_tls_connection_base_get_base_socket (tls),
+                                                        G_IO_IN, NULL);
           g_source_set_callback (read_source, (GSourceFunc)read_datagram_based_cb,
                                  &read_done, NULL);
         }
       else
         {
-          read_source = g_pollable_input_stream_create_source (priv->base_istream, NULL);
+          read_source = g_pollable_input_stream_create_source (g_tls_connection_base_get_base_istream (tls),
+                                                               NULL);
           g_source_set_callback (read_source, (GSourceFunc)read_pollable_cb,
                                  &read_done, NULL);
         }
@@ -1760,14 +750,40 @@ g_tls_connection_gnutls_pull_timeout_func (gnutls_transport_ptr_t transport_data
 
       /* If @read_source was dispatched due to cancellation, the resulting error
        * will be handled in g_tls_connection_gnutls_pull_func(). */
-      if (g_tls_connection_gnutls_base_check (gnutls, G_IO_IN) ||
-          g_cancellable_is_cancelled (priv->read_cancellable))
+      if (g_tls_connection_base_base_check (tls, G_IO_IN) ||
+          g_cancellable_is_cancelled (g_tls_connection_base_get_read_cancellable (tls)))
         return 1;
     }
 
   return 0;
 }
 
+static GTlsConnectionBaseStatus
+g_tls_connection_gnutls_request_rehandshake (GTlsConnectionBase  *tls,
+                                             gint64               timeout,
+                                             GCancellable        *cancellable,
+                                             GError             **error)
+{
+  GTlsConnectionGnutls *gnutls;
+  GTlsConnectionBaseStatus status;
+  int ret;
+
+  /* On a client-side connection, gnutls_handshake() itself will start
+   * a rehandshake, so we only need to do something special here for
+   * server-side connections.
+   */
+  if (!G_IS_TLS_SERVER_CONNECTION (tls))
+    return G_TLS_CONNECTION_BASE_OK;
+
+  gnutls = G_TLS_CONNECTION_GNUTLS (tls);
+
+  BEGIN_GNUTLS_IO (gnutls, G_IO_IN | G_IO_OUT, timeout, cancellable);
+  ret = gnutls_rehandshake (gnutls->priv->session);
+  END_GNUTLS_IO (gnutls, G_IO_IN | G_IO_OUT, ret, status, _("Error performing TLS handshake: %s"), error);
+
+  return status;
+}
+
 static GTlsCertificate *
 get_peer_certificate_from_session (GTlsConnectionGnutls *gnutls)
 {
@@ -1787,6 +803,8 @@ get_peer_certificate_from_session (GTlsConnectionGnutls *gnutls)
   return G_TLS_CERTIFICATE (chain);
 }
 
+//MOVE
+#if 0
 static GTlsCertificateFlags
 verify_peer_certificate (GTlsConnectionGnutls *gnutls,
                          GTlsCertificate      *peer_certificate)
@@ -1842,28 +860,28 @@ static void
 update_peer_certificate_and_compute_errors (GTlsConnectionGnutls *gnutls)
 {
   GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
+  GTlsCertificate *peer_certificate = NULL;
+  GTlsCertificateFlags peer_certificate_errors = 0;
 
   /* This function must be called from the handshake context thread
-   * (probably the main thread, NOT the handshake thread) because it
-   * emits notifies that are application-visible.
+   * (probably the main thread, NOT the handshake thread) because
+   * g_tls_connection_base_set_peer_certificate() emits notifies
+   * that are application-visible.
    *
    * verify_certificate_mutex should be locked.
    */
   g_assert (priv->handshake_context);
   g_assert (g_main_context_is_owner (priv->handshake_context));
 
-  g_clear_object (&priv->peer_certificate);
-  priv->peer_certificate_errors = 0;
-
   if (gnutls_certificate_type_get (priv->session) == GNUTLS_CRT_X509)
     {
-      priv->peer_certificate = get_peer_certificate_from_session (gnutls);
-      if (priv->peer_certificate)
-        priv->peer_certificate_errors = verify_peer_certificate (gnutls, priv->peer_certificate);
+      peer_certificate = get_peer_certificate_from_session (gnutls);
+      if (peer_certificate)
+        peer_certificate_errors = verify_peer_certificate (gnutls, peer_certificate);
     }
 
-  g_object_notify (G_OBJECT (gnutls), "peer-certificate");
-  g_object_notify (G_OBJECT (gnutls), "peer-certificate-errors");
+  g_tls_connection_base_set_peer_certificate (G_TLS_CONNECTION_BASE (gnutls),
+                                              peer_certificate, peer_certificate_errors);
 }
 
 static gboolean
@@ -1951,80 +969,41 @@ verify_certificate_cb (gnutls_session_t session)
   /* Return 0 for the handshake to continue, non-zero to terminate. */
   return !accepted;
 }
+#endif
 
 static void
-handshake_thread (GTask        *task,
-                  gpointer      object,
-                  gpointer      task_data,
-                  GCancellable *cancellable)
+g_tls_connection_gnutls_prepare_handshake (GTlsConnectionBase  *tls,
+                                           gchar              **advertised_protocols)
 {
-  GTlsConnectionGnutls *gnutls = object;
+  GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (tls);
   GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-  GError *error = NULL;
-  int ret;
-  gint64 start_time;
-  gint64 timeout;
-
-  /* A timeout, in microseconds, must be provided as a gint64* task_data. */
-  g_assert (task_data != NULL);
-
-  timeout = *((gint64 *)task_data);
-  start_time = g_get_monotonic_time ();
-  priv->started_handshake = FALSE;
-
-  if (!claim_op (gnutls, G_TLS_CONNECTION_GNUTLS_OP_HANDSHAKE,
-                 timeout, cancellable, &error))
-    {
-      g_task_return_error (task, error);
-      return;
-    }
-
-  g_clear_error (&priv->handshake_error);
 
-  if (priv->ever_handshaked && !priv->implicit_handshake)
+  if (advertised_protocols)
     {
-      if (priv->rehandshake_mode != G_TLS_REHANDSHAKE_UNSAFELY &&
-          !gnutls_safe_renegotiation_status (priv->session))
-        {
-          g_task_return_new_error (task, G_TLS_ERROR, G_TLS_ERROR_MISC,
-                                   _("Peer does not support safe renegotiation"));
-          return;
-        }
+      gnutls_datum_t *protocols;
+      int n_protos, i;
 
-      if (!G_IS_TLS_CLIENT_CONNECTION (gnutls))
+      n_protos = g_strv_length (advertised_protocols);
+      protocols = g_new (gnutls_datum_t, n_protos);
+      for (i = 0; advertised_protocols[i]; i++)
         {
-          /* Adjust the timeout for the next operation in the sequence. */
-          if (timeout > 0)
-            {
-              unsigned int timeout_ms;
-
-              timeout -= (g_get_monotonic_time () - start_time);
-              if (timeout <= 0)
-                timeout = 1;
-
-              /* Convert from microseconds to milliseconds, but ensure the timeout
-               * remains positive. */
-              timeout_ms = (timeout + 999) / 1000;
-
-              gnutls_handshake_set_timeout (priv->session, timeout_ms);
-              gnutls_dtls_set_timeouts (priv->session, 1000 /* default */,
-                                        timeout_ms);
-            }
-
-          BEGIN_GNUTLS_IO (gnutls, G_IO_IN | G_IO_OUT, timeout, cancellable);
-          ret = gnutls_rehandshake (priv->session);
-          END_GNUTLS_IO (gnutls, G_IO_IN | G_IO_OUT, ret,
-                         _("Error performing TLS handshake"), &error);
-
-          if (error)
-            {
-              g_task_return_error (task, error);
-              return;
-            }
+          protocols[i].size = strlen (advertised_protocols[i]);
+          protocols[i].data = g_memdup (advertised_protocols[i], protocols[i].size);
         }
+      gnutls_alpn_set_protocols (priv->session, protocols, n_protos, 0);
+      g_free (protocols);
     }
+}
 
-  priv->started_handshake = TRUE;
+static GTlsConnectionBaseStatus
+g_tls_connection_gnutls_handshake_thread_handshake (GTlsConnectionBase  *tls,
+                                                    gint64               timeout,
+                                                    GCancellable        *cancellable,
+                                                    GError             **error)
+{
+  GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (tls);
+  GTlsConnectionBaseStatus status;
+  int ret;
 
   if (!priv->ever_handshaked)
     g_tls_connection_gnutls_set_handshake_priority (gnutls);
@@ -2057,93 +1036,25 @@ handshake_thread (GTask        *task,
       ret = gnutls_record_recv (priv->session, buf, sizeof (buf));
       if (ret > -1)
         {
-          if (!priv->app_data_buf)
-            priv->app_data_buf = g_byte_array_new ();
-          g_byte_array_append (priv->app_data_buf, buf, ret);
+          g_tls_connection_base_buffer_application_data (tls, buf, ret);
           ret = GNUTLS_E_AGAIN;
         }
     }
-  END_GNUTLS_IO (gnutls, G_IO_IN | G_IO_OUT, ret,
-                 _("Error performing TLS handshake"), &error);
-
-  /* This calls the finish_handshake code of GTlsClientConnectionGnutls
-   * or GTlsServerConnectionGnutls. It has nothing to do with
-   * GTlsConnectionGnutls's own finish_handshake function, which still
-   * needs to be called at this point.
-   */
-  G_TLS_CONNECTION_GNUTLS_GET_CLASS (gnutls)->finish_handshake (gnutls, &error);
-
-  if (error)
-    {
-      g_task_return_error (task, error);
-    }
-  else
-    {
-      priv->ever_handshaked = TRUE;
-      g_task_return_boolean (task, TRUE);
-    }
-}
-
-static void
-begin_handshake (GTlsConnectionGnutls *gnutls)
-{
-#if GLIB_CHECK_VERSION(2, 60, 0)
-  GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-
-  if (priv->advertised_protocols)
-    {
-      gnutls_datum_t *protocols;
-      int n_protos, i;
-
-      n_protos = g_strv_length (priv->advertised_protocols);
-      protocols = g_new (gnutls_datum_t, n_protos);
-      for (i = 0; priv->advertised_protocols[i]; i++)
-        {
-          protocols[i].size = strlen (priv->advertised_protocols[i]);
-          protocols[i].data = g_memdup (priv->advertised_protocols[i], protocols[i].size);
-        }
-      gnutls_alpn_set_protocols (priv->session, protocols, n_protos, 0);
-      g_free (protocols);
-    }
-#endif
+  END_GNUTLS_IO (gnutls, G_IO_IN | G_IO_OUT, ret, status,
+                 _("Error performing TLS handshake"), error);
 
-  G_TLS_CONNECTION_GNUTLS_GET_CLASS (gnutls)->begin_handshake (gnutls);
+  return status;
 }
 
-#if GLIB_CHECK_VERSION(2, 60, 0)
 static void
-update_negotiated_protocol (GTlsConnectionGnutls *gnutls)
+g_tls_connection_gnutls_complete_handshake (GTlsConnectionBase  *tls,
+                                            gchar              **negotiated_protocol,
+                                            GError             **error)
 {
+  GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (tls);
   GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-  gchar *orig_negotiated_protocol;
-  gnutls_datum_t protocol;
-
-  /*
-   * Preserve the prior negotiated protocol before clearing it
-   */
-  orig_negotiated_protocol = g_steal_pointer (&priv->negotiated_protocol);
 
-
-  if (gnutls_alpn_get_selected_protocol (priv->session, &protocol) == 0 && protocol.size > 0)
-    priv->negotiated_protocol = g_strndup ((gchar *)protocol.data, protocol.size);
-
-  /*
-   * Notify only if the negotiated protocol changed
-   */
-  if (g_strcmp0 (orig_negotiated_protocol, priv->negotiated_protocol) != 0)
-    g_object_notify (G_OBJECT (gnutls), "negotiated-protocol");
-
-  g_free (orig_negotiated_protocol);
-}
-#endif
-
-static gboolean
-finish_handshake (GTlsConnectionGnutls  *gnutls,
-                  GTask                 *task,
-                  GError               **error)
-{
-  GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-  g_assert (error != NULL);
+  g_clear_pointer (&priv->handshake_context, g_main_context_unref);
 
   if (gnutls_session_is_resumed (priv->session))
     {
@@ -2169,10 +1080,9 @@ finish_handshake (GTlsConnectionGnutls  *gnutls,
                            _("Unacceptable TLS certificate"));
     }
 
-#if GLIB_CHECK_VERSION(2, 60, 0)
+// FIXME: this needs to remain in GnuTLS class
   if (!*error && priv->advertised_protocols)
     update_negotiated_protocol (gnutls);
-#endif
 
   if (*error && priv->started_handshake)
     priv->handshake_error = g_error_copy (*error);
@@ -2180,44 +1090,6 @@ finish_handshake (GTlsConnectionGnutls  *gnutls,
   return (*error == NULL);
 }
 
-static void
-sync_handshake_thread_completed (GObject      *object,
-                                 GAsyncResult *result,
-                                 gpointer      user_data)
-{
-  GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (object);
-  GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-
-  g_assert (g_main_context_is_owner (priv->handshake_context));
-
-  g_mutex_lock (&priv->op_mutex);
-  priv->sync_handshake_completed = TRUE;
-  g_mutex_unlock (&priv->op_mutex);
-
-  g_main_context_wakeup (priv->handshake_context);
-}
-
-static void
-crank_sync_handshake_context (GTlsConnectionGnutls *gnutls,
-                              GCancellable         *cancellable)
-{
-  GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-
-  /* need_finish_handshake will be set inside sync_handshake_thread_completed(),
-   * which should only ever be invoked while iterating the handshake context
-   * here. So need_finish_handshake should only change on this thread.
-   */
-  g_mutex_lock (&priv->op_mutex);
-  priv->sync_handshake_completed = FALSE;
-  while (!priv->sync_handshake_completed && !g_cancellable_is_cancelled (cancellable))
-    {
-      g_mutex_unlock (&priv->op_mutex);
-      g_main_context_iteration (priv->handshake_context, TRUE);
-      g_mutex_lock (&priv->op_mutex);
-    }
-  g_mutex_unlock (&priv->op_mutex);
-}
-
 static gboolean
 g_tls_connection_gnutls_handshake (GTlsConnection   *conn,
                                    GCancellable     *cancellable,
@@ -3099,142 +1971,27 @@ g_tls_connection_gnutls_dtls_shutdown_finish (GDtlsConnection  *conn,
   return g_task_propagate_boolean (G_TASK (result), error);
 }
 
-#if GLIB_CHECK_VERSION(2, 60, 0)
-static void
-g_tls_connection_gnutls_dtls_set_advertised_protocols (GDtlsConnection     *conn,
-                                                       const gchar * const *protocols)
-{
-  g_object_set (conn, "advertised-protocols", protocols, NULL);
-}
-
-const gchar *
-g_tls_connection_gnutls_dtls_get_negotiated_protocol (GDtlsConnection *conn)
-{
-  GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (conn);
-  GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-
-  return priv->negotiated_protocol;
-}
-#endif
-
 static void
 g_tls_connection_gnutls_class_init (GTlsConnectionGnutlsClass *klass)
 {
   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
-  GTlsConnectionClass *connection_class = G_TLS_CONNECTION_CLASS (klass);
-  GIOStreamClass *iostream_class = G_IO_STREAM_CLASS (klass);
-
-  gobject_class->get_property = g_tls_connection_gnutls_get_property;
-  gobject_class->set_property = g_tls_connection_gnutls_set_property;
-  gobject_class->finalize     = g_tls_connection_gnutls_finalize;
-
-  connection_class->handshake        = g_tls_connection_gnutls_handshake;
-  connection_class->handshake_async  = g_tls_connection_gnutls_handshake_async;
-  connection_class->handshake_finish = g_tls_connection_gnutls_handshake_finish;
-
-  iostream_class->get_input_stream  = g_tls_connection_gnutls_get_input_stream;
-  iostream_class->get_output_stream = g_tls_connection_gnutls_get_output_stream;
-  iostream_class->close_fn          = g_tls_connection_gnutls_close;
-  iostream_class->close_async       = g_tls_connection_gnutls_close_async;
-  iostream_class->close_finish      = g_tls_connection_gnutls_close_finish;
-
-  /* For GTlsConnection and GDtlsConnection: */
-  g_object_class_override_property (gobject_class, PROP_BASE_IO_STREAM, "base-io-stream");
-  g_object_class_override_property (gobject_class, PROP_BASE_SOCKET, "base-socket");
-  g_object_class_override_property (gobject_class, PROP_REQUIRE_CLOSE_NOTIFY, "require-close-notify");
-  g_object_class_override_property (gobject_class, PROP_REHANDSHAKE_MODE, "rehandshake-mode");
-  g_object_class_override_property (gobject_class, PROP_USE_SYSTEM_CERTDB, "use-system-certdb");
-  g_object_class_override_property (gobject_class, PROP_DATABASE, "database");
-  g_object_class_override_property (gobject_class, PROP_CERTIFICATE, "certificate");
-  g_object_class_override_property (gobject_class, PROP_INTERACTION, "interaction");
-  g_object_class_override_property (gobject_class, PROP_PEER_CERTIFICATE, "peer-certificate");
-  g_object_class_override_property (gobject_class, PROP_PEER_CERTIFICATE_ERRORS, "peer-certificate-errors");
-#if GLIB_CHECK_VERSION(2, 60, 0)
-  g_object_class_override_property (gobject_class, PROP_ADVERTISED_PROTOCOLS, "advertised-protocols");
-  g_object_class_override_property (gobject_class, PROP_NEGOTIATED_PROTOCOL, "negotiated-protocol");
-#endif
-}
+  GTlsConnectionBaseClass *base_class = G_TLS_CONNECTION_BASE_CLASS (klass);
 
-static void
-g_tls_connection_gnutls_initable_iface_init (GInitableIface *iface)
-{
-  iface->init = g_tls_connection_gnutls_initable_init;
-}
+  gobject_class->finalize = g_tls_connection_gnutls_finalize;
 
-static void
-g_tls_connection_gnutls_dtls_connection_iface_init (GDtlsConnectionInterface *iface)
-{
-  iface->handshake = g_tls_connection_gnutls_dtls_handshake;
-  iface->handshake_async = g_tls_connection_gnutls_dtls_handshake_async;
-  iface->handshake_finish = g_tls_connection_gnutls_dtls_handshake_finish;
-  iface->shutdown = g_tls_connection_gnutls_dtls_shutdown;
-  iface->shutdown_async = g_tls_connection_gnutls_dtls_shutdown_async;
-  iface->shutdown_finish = g_tls_connection_gnutls_dtls_shutdown_finish;
-#if GLIB_CHECK_VERSION(2, 60, 0)
-  iface->set_advertised_protocols = g_tls_connection_gnutls_dtls_set_advertised_protocols;
-  iface->get_negotiated_protocol = g_tls_connection_gnutls_dtls_get_negotiated_protocol;
-#endif
+  base_class->request_rehandshake        = g_tls_connection_gnutls_request_rehandshake;
+  base_class->prepare_handshake          = g_tls_connection_gnutls_prepare_handshake;
+  base_class->handshake_thread_handshake = g_tls_connection_gnutls_handshake_thread_handshake;
+  base_class->complete_handshake         = g_tls_connection_gnutls_complete_handshake;
+  base_class->read_fn                    = g_tls_connection_gnutls_read;
+  base_class->read_message_fn            = g_tls_connection_gnutls_read_message;
+  base_class->write_fn                   = g_tls_connection_gnutls_write;
+  base_class->write_message_fn           = g_tls_connection_gnutls_write_message;
+  base_class->close_fn                   = g_tls_connection_gnutls_close;
 }
 
 static void
-g_tls_connection_gnutls_datagram_based_iface_init (GDatagramBasedInterface *iface)
-{
-  iface->receive_messages = g_tls_connection_gnutls_receive_messages;
-  iface->send_messages = g_tls_connection_gnutls_send_messages;
-  iface->create_source = g_tls_connection_gnutls_dtls_create_source;
-  iface->condition_check = g_tls_connection_gnutls_condition_check;
-  iface->condition_wait = g_tls_connection_gnutls_condition_wait;
-}
-
-gboolean
-g_tls_connection_gnutls_request_certificate (GTlsConnectionGnutls  *gnutls,
-                                             GError               **error)
-{
-  GTlsInteractionResult res = G_TLS_INTERACTION_UNHANDLED;
-  GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
-  GTlsInteraction *interaction;
-  GTlsConnection *conn;
-
-  g_return_val_if_fail (G_IS_TLS_CONNECTION_GNUTLS (gnutls), FALSE);
-
-  conn = G_TLS_CONNECTION (gnutls);
-
-  interaction = g_tls_connection_get_interaction (conn);
-  if (!interaction)
-    return FALSE;
-
-  res = g_tls_interaction_invoke_request_certificate (interaction, conn, 0,
-                                                      priv->read_cancellable, error);
-  return res != G_TLS_INTERACTION_FAILED;
-}
-
-void
-GTLS_DEBUG (gpointer    gnutls,
-            const char *message,
-            ...)
+g_tls_connection_gnutls_initable_iface_init (GInitableIface *iface)
 {
-  char *result = NULL;
-  int ret;
-
-  g_assert (G_IS_TLS_CONNECTION (gnutls));
-
-  va_list args;
-  va_start (args, message);
-
-  ret = g_vasprintf (&result, message, args);
-  g_assert (ret > 0);
-
-  if (G_IS_TLS_CLIENT_CONNECTION (gnutls))
-    g_printf ("CLIENT %p: ", gnutls);
-  else if (G_IS_TLS_SERVER_CONNECTION (gnutls))
-    g_printf ("SERVER %p: ", gnutls);
-  else
-    g_assert_not_reached ();
-
-  g_printf ("%s\n", result);
-
-  fflush (stdout);
-
-  g_free (result);
-  va_end (args);
+  iface->init = g_tls_connection_gnutls_initable_init;
 }
diff --git a/tls/gnutls/gtlsconnection-gnutls.h b/tls/gnutls/gtlsconnection-gnutls.h
index 028960b..f513a74 100644
--- a/tls/gnutls/gtlsconnection-gnutls.h
+++ b/tls/gnutls/gtlsconnection-gnutls.h
@@ -29,15 +29,17 @@
 #include <gnutls/abstract.h>
 #include <gnutls/gnutls.h>
 
+#include "gtlsconnection-base.h"
+
 G_BEGIN_DECLS
 
 #define G_TYPE_TLS_CONNECTION_GNUTLS            (g_tls_connection_gnutls_get_type ())
 
-G_DECLARE_DERIVABLE_TYPE (GTlsConnectionGnutls, g_tls_connection_gnutls, G, TLS_CONNECTION_GNUTLS, 
GTlsConnection)
+G_DECLARE_DERIVABLE_TYPE (GTlsConnectionGnutls, g_tls_connection_gnutls, G, TLS_CONNECTION_GNUTLS, 
GTlsConnectionBase)
 
 struct _GTlsConnectionGnutlsClass
 {
-  GTlsConnectionClass parent_class;
+  GTlsConnectionBaseClass parent_class;
 
   void     (*failed)           (GTlsConnectionGnutls  *gnutls);
 
@@ -47,6 +49,7 @@ struct _GTlsConnectionGnutlsClass
 };
 
 gnutls_certificate_credentials_t g_tls_connection_gnutls_get_credentials (GTlsConnectionGnutls *connection);
+
 gnutls_session_t                 g_tls_connection_gnutls_get_session     (GTlsConnectionGnutls *connection);
 
 void     g_tls_connection_gnutls_get_certificate     (GTlsConnectionGnutls  *gnutls,
@@ -54,46 +57,6 @@ void     g_tls_connection_gnutls_get_certificate     (GTlsConnectionGnutls  *gnu
                                                       unsigned int          *pcert_length,
                                                       gnutls_privkey_t      *pkey);
 
-gboolean g_tls_connection_gnutls_request_certificate (GTlsConnectionGnutls  *gnutls,
-                                                      GError               **error);
-
-gssize   g_tls_connection_gnutls_read          (GTlsConnectionGnutls  *gnutls,
-                                                void                  *buffer,
-                                                gsize                  size,
-                                                gint64                 timeout,
-                                                GCancellable          *cancellable,
-                                                GError               **error);
-gssize   g_tls_connection_gnutls_write         (GTlsConnectionGnutls  *gnutls,
-                                                const void            *buffer,
-                                                gsize                  size,
-                                                gint64                 timeout,
-                                                GCancellable          *cancellable,
-                                                GError               **error);
-
-gboolean g_tls_connection_gnutls_check         (GTlsConnectionGnutls  *gnutls,
-                                                GIOCondition           condition);
-GSource *g_tls_connection_gnutls_create_source (GTlsConnectionGnutls  *gnutls,
-                                                GIOCondition           condition,
-                                                GCancellable          *cancellable);
-
-typedef enum {
-        G_TLS_DIRECTION_NONE = 0,
-        G_TLS_DIRECTION_READ = 1 << 0,
-        G_TLS_DIRECTION_WRITE = 1 << 1,
-} GTlsDirection;
-
-#define G_TLS_DIRECTION_BOTH (G_TLS_DIRECTION_READ | G_TLS_DIRECTION_WRITE)
-
-gboolean g_tls_connection_gnutls_close_internal (GIOStream            *stream,
-                                                 GTlsDirection         direction,
-                                                 gint64                timeout,
-                                                 GCancellable         *cancellable,
-                                                 GError              **error);
-
-void GTLS_DEBUG (gpointer    gnutls,
-                 const char *message,
-                 ...);
-
 G_END_DECLS
 
 #endif /* __G_TLS_CONNECTION_GNUTLS_H___ */
diff --git a/tls/gnutls/gtlsserverconnection-gnutls.c b/tls/gnutls/gtlsserverconnection-gnutls.c
index b3aebd5..2ed3476 100644
--- a/tls/gnutls/gtlsserverconnection-gnutls.c
+++ b/tls/gnutls/gtlsserverconnection-gnutls.c
@@ -209,10 +209,12 @@ g_tls_server_connection_gnutls_failed (GTlsConnectionGnutls *conn)
   gnutls_db_remove_session (g_tls_connection_gnutls_get_session (conn));
 }
 
-static void
-g_tls_server_connection_gnutls_begin_handshake (GTlsConnectionGnutls *conn)
+static GTlsConnectionBaseStatus
+g_tls_server_connection_gnutls_handshake (GTlsConnectionBase  *tls,
+                                          GCancellable        *cancellable,
+                                          GError             **error)
 {
-  GTlsServerConnectionGnutls *gnutls = G_TLS_SERVER_CONNECTION_GNUTLS (conn);
+  GTlsServerConnectionGnutls *gnutls = G_TLS_SERVER_CONNECTION_GNUTLS (tls);
   gnutls_session_t session;
   gnutls_certificate_request_t req_mode;
 
@@ -230,14 +232,10 @@ g_tls_server_connection_gnutls_begin_handshake (GTlsConnectionGnutls *conn)
       break;
     }
 
-  session = g_tls_connection_gnutls_get_session (conn);
+  session = g_tls_connection_gnutls_get_session (G_TLS_CONNECTION_GNUTLS (tls));
   gnutls_certificate_server_set_request (session, req_mode);
-}
 
-static void
-g_tls_server_connection_gnutls_finish_handshake (GTlsConnectionGnutls  *gnutls,
-                                                 GError               **inout_error)
-{
+  return G_TLS_CONNECTION_BASE_CLASS (g_tls_server_connection_gnutls_parent_class)->handshake (tls, 
cancellable, error);
 }
 
 /* Session cache management */
@@ -302,15 +300,16 @@ static void
 g_tls_server_connection_gnutls_class_init (GTlsServerConnectionGnutlsClass *klass)
 {
   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
-  GTlsConnectionGnutlsClass *connection_gnutls_class = G_TLS_CONNECTION_GNUTLS_CLASS (klass);
+  GTlsConnectionBaseClass *base_class = G_TLS_CONNECTION_BASE_CLASS (klass);
+  GTlsConnectionGnutlsClass *gnutls_class = G_TLS_CONNECTION_GNUTLS_CLASS (klass);
 
   gobject_class->finalize = g_tls_server_connection_gnutls_finalize;
   gobject_class->get_property = g_tls_server_connection_gnutls_get_property;
   gobject_class->set_property = g_tls_server_connection_gnutls_set_property;
 
-  connection_gnutls_class->failed           = g_tls_server_connection_gnutls_failed;
-  connection_gnutls_class->begin_handshake  = g_tls_server_connection_gnutls_begin_handshake;
-  connection_gnutls_class->finish_handshake = g_tls_server_connection_gnutls_finish_handshake;
+  base_class->handshake  = g_tls_server_connection_gnutls_handshake;
+
+  gnutls_class->failed           = g_tls_server_connection_gnutls_failed;
 
   g_object_class_override_property (gobject_class, PROP_AUTHENTICATION_MODE, "authentication-mode");
 }
diff --git a/tls/openssl/gtlsconnection-openssl.c b/tls/openssl/gtlsconnection-openssl.c
index 629f651..76f8740 100644
--- a/tls/openssl/gtlsconnection-openssl.c
+++ b/tls/openssl/gtlsconnection-openssl.c
@@ -320,6 +320,7 @@ verify_ocsp_response (GTlsConnectionOpenssl *openssl,
 #endif
 }
 
+/* FIXME: Share with GnuTLS */
 static GTlsCertificateFlags
 verify_peer_certificate (GTlsConnectionOpenssl *openssl,
                          GTlsCertificate       *peer_certificate)
@@ -371,11 +372,45 @@ verify_peer_certificate (GTlsConnectionOpenssl *openssl,
   return errors;
 }
 
+/* FIXME: Share with GnuTLS */
+static gboolean
+accept_peer_certificate (GTlsConnectionBase   *tls,
+                         GTlsCertificate      *peer_certificate,
+                         GTlsCertificateFlags  peer_certificate_errors)
+{
+  GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
+  gboolean accepted = FALSE;
+
+  if (G_IS_TLS_CLIENT_CONNECTION (tls) && priv->peer_certificate)
+    {
+      GTlsCertificateFlags validation_flags;
+
+      if (!g_tls_connection_base_is_dtls (tls))
+        validation_flags =
+          g_tls_client_connection_get_validation_flags (G_TLS_CLIENT_CONNECTION (tls));
+      else
+        validation_flags =
+          g_dtls_client_connection_get_validation_flags (G_DTLS_CLIENT_CONNECTION (tls));
+
+      if ((peer_certificate_errors & validation_flags) == 0)
+        accepted = TRUE;
+    }
+
+  if (!accepted)
+    {
+      accepted = g_tls_connection_emit_accept_certificate (G_TLS_CONNECTION (tls),
+                                                           peer_certificate,
+                                                           peer_certificate_errors);
+    }
+
+  return accepted;
+}
+
 static GTlsConnectionBaseStatus
-g_tls_connection_openssl_handshake (GTlsConnectionBase  *tls,
-                                    gint64               timeout,
-                                    GCancellable        *cancellable,
-                                    GError             **error)
+g_tls_connection_openssl_handshake_thread_handshake (GTlsConnectionBase  *tls,
+                                                     gint64               timeout,
+                                                     GCancellable        *cancellable,
+                                                     GError             **error)
 {
   GTlsConnectionOpenssl *openssl = G_TLS_CONNECTION_OPENSSL (tls);
   GTlsConnectionOpensslPrivate *priv;
@@ -407,18 +442,20 @@ g_tls_connection_openssl_handshake (GTlsConnectionBase  *tls,
   return status;
 }
 
-static GTlsConnectionBaseStatus
+static void
 g_tls_connection_openssl_complete_handshake (GTlsConnectionBase  *tls,
+                                             gchar              **negotiated_protocol,
                                              GError             **error)
 {
   GTlsConnectionOpenssl *openssl = G_TLS_CONNECTION_OPENSSL (tls);
   GTlsConnectionOpensslPrivate *priv;
   GTlsCertificate *peer_certificate;
   GTlsCertificateFlags peer_certificate_errors = 0;
-  GTlsConnectionBaseStatus status = G_TLS_CONNECTION_BASE_OK;
 
   priv = g_tls_connection_openssl_get_instance_private (openssl);
 
+// FIXME
+#if 0
   peer_certificate = priv->peer_certificate_tmp;
   priv->peer_certificate_tmp = NULL;
   peer_certificate_errors = priv->peer_certificate_errors_tmp;
@@ -426,8 +463,8 @@ g_tls_connection_openssl_complete_handshake (GTlsConnectionBase  *tls,
 
   if (peer_certificate)
     {
-      if (!g_tls_connection_base_accept_peer_certificate (tls, peer_certificate,
-                                                          peer_certificate_errors))
+      /* FIXME: This is too late. Verification should occur during the handshake. */
+      if (!accept_peer_certificate (tls, peer_certificate, peer_certificate_errors))
         {
           g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
                                _("Unacceptable TLS certificate"));
@@ -439,8 +476,7 @@ g_tls_connection_openssl_complete_handshake (GTlsConnectionBase  *tls,
                                                   peer_certificate_errors);
       g_clear_object (&peer_certificate);
     }
-
-  return status;
+#endif
 }
 
 static void
@@ -584,16 +620,16 @@ g_tls_connection_openssl_class_init (GTlsConnectionOpensslClass *klass)
   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
   GTlsConnectionBaseClass *base_class = G_TLS_CONNECTION_BASE_CLASS (klass);
 
-  gobject_class->finalize     = g_tls_connection_openssl_finalize;
+  gobject_class->finalize = g_tls_connection_openssl_finalize;
 
-  base_class->request_rehandshake = g_tls_connection_openssl_request_rehandshake;
-  base_class->handshake           = g_tls_connection_openssl_handshake;
-  base_class->complete_handshake  = g_tls_connection_openssl_complete_handshake;
-  base_class->push_io             = g_tls_connection_openssl_push_io;
-  base_class->pop_io              = g_tls_connection_openssl_pop_io;
-  base_class->read_fn             = g_tls_connection_openssl_read;
-  base_class->write_fn            = g_tls_connection_openssl_write;
-  base_class->close_fn            = g_tls_connection_openssl_close;
+  base_class->request_rehandshake        = g_tls_connection_openssl_request_rehandshake;
+  base_class->handshake_thread_handshake = g_tls_connection_openssl_handshake_thread_handshake;
+  base_class->complete_handshake         = g_tls_connection_openssl_complete_handshake;
+  base_class->push_io                    = g_tls_connection_openssl_push_io;
+  base_class->pop_io                     = g_tls_connection_openssl_pop_io;
+  base_class->read_fn                    = g_tls_connection_openssl_read;
+  base_class->write_fn                   = g_tls_connection_openssl_write;
+  base_class->close_fn                   = g_tls_connection_openssl_close;
 }
 
 static gboolean
diff --git a/tls/tests/connection.c b/tls/tests/connection.c
index 5f25ea4..3ae8993 100644
--- a/tls/tests/connection.c
+++ b/tls/tests/connection.c
@@ -82,9 +82,7 @@ typedef struct {
   gboolean server_should_close;
   gboolean server_running;
   GTlsCertificate *server_certificate;
-#if GLIB_CHECK_VERSION(2, 60, 0)
   const gchar * const *server_protocols;
-#endif
 
   char buf[128];
   gssize nread, nwrote;
@@ -306,13 +304,11 @@ on_incoming_connection (GSocketService     *service,
   if (test->database)
     g_tls_connection_set_database (G_TLS_CONNECTION (test->server_connection), test->database);
 
-#if GLIB_CHECK_VERSION(2, 60, 0)
   if (test->server_protocols)
     {
       g_tls_connection_set_advertised_protocols (G_TLS_CONNECTION (test->server_connection),
                                                  test->server_protocols);
     }
-#endif
 
   stream = g_io_stream_get_output_stream (test->server_connection);
 
@@ -1941,11 +1937,7 @@ test_fallback (TestConnection *test,
 #pragma GCC diagnostic pop
 #endif
 
-#if GLIB_CHECK_VERSION(2, 60, 0)
   g_set_error_literal (&test->expected_server_error, G_TLS_ERROR, G_TLS_ERROR_INAPPROPRIATE_FALLBACK, "");
-#else
-  g_set_error_literal (&test->expected_server_error, G_TLS_ERROR, G_TLS_ERROR_MISC, "");
-#endif
 
   g_tls_connection_handshake_async (tlsconn, G_PRIORITY_DEFAULT, NULL,
                                     quit_on_handshake_complete, test);
@@ -2110,7 +2102,6 @@ test_alpn (TestConnection *test,
            const char * const *server_protocols,
            const char *negotiated_protocol)
 {
-#if GLIB_CHECK_VERSION(2, 60, 0)
   GIOStream *connection;
   GError *error = NULL;
 
@@ -2141,9 +2132,6 @@ test_alpn (TestConnection *test,
 
   g_assert_cmpstr (g_tls_connection_get_negotiated_protocol (G_TLS_CONNECTION (test->server_connection)), 
==, negotiated_protocol);
   g_assert_cmpstr (g_tls_connection_get_negotiated_protocol (G_TLS_CONNECTION (test->client_connection)), 
==, negotiated_protocol);
-#else
-  g_test_skip ("no support for ALPN in this GLib version");
-#endif
 }
 
 static void
diff --git a/tls/tests/dtls-connection.c b/tls/tests/dtls-connection.c
index 1304d96..7006340 100644
--- a/tls/tests/dtls-connection.c
+++ b/tls/tests/dtls-connection.c
@@ -89,9 +89,7 @@ typedef struct {
   gboolean expect_server_error;
   GError *server_error;
   gboolean server_running;
-#if GLIB_CHECK_VERSION(2, 60, 0)
   const gchar * const *server_protocols;
-#endif
 
   char buf[128];
   gssize nread, nwrote;
@@ -400,13 +398,11 @@ on_incoming_connection (GSocket       *socket,
   if (test->database)
     g_dtls_connection_set_database (G_DTLS_CONNECTION (test->server_connection), test->database);
 
-#if GLIB_CHECK_VERSION(2, 60, 0)
   if (test->server_protocols)
     {
       g_dtls_connection_set_advertised_protocols (G_DTLS_CONNECTION (test->server_connection),
                                                   test->server_protocols);
     }
-#endif
 
   if (test->test_data->server_should_disappear)
     {
@@ -743,7 +739,6 @@ test_alpn (TestConnection *test,
            const char * const *server_protocols,
            const char *negotiated_protocol)
 {
-#if GLIB_CHECK_VERSION(2, 60, 0)
   GDatagramBased *connection;
   GError *error = NULL;
 
@@ -775,9 +770,6 @@ test_alpn (TestConnection *test,
 
   g_assert_cmpstr (g_dtls_connection_get_negotiated_protocol (G_DTLS_CONNECTION (test->server_connection)), 
==, negotiated_protocol);
   g_assert_cmpstr (g_dtls_connection_get_negotiated_protocol (G_DTLS_CONNECTION (test->client_connection)), 
==, negotiated_protocol);
-#else
-  g_test_skip ("no support for ALPN in this GLib version");
-#endif
 }
 
 static void


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]