[glib-networking/wip/tlssplit: 7/11] tls: split out non-gnutls-specific code from gnutls backend



commit 6611176543335c5520cf910eb334d86599976589
Author: Dan Winship <danw gnome org>
Date:   Thu Nov 10 18:50:06 2011 -0500

    tls: split out non-gnutls-specific code from gnutls backend
    
    Create GTlsConnectionBase, GTlsInputStreamBase, GTlsOutputStreamBase,
    which implement the generic "translate from a BSD-sockets-y API to the
    GTlsConnection API" semantics, and rebase GTlsConnectionGnutls on top
    of that.

 Makefile.am                              |    2 +
 configure.ac                             |    1 +
 po/POTFILES.in                           |    1 +
 tls/base/Makefile.am                     |   15 +
 tls/base/gtlsconnection-base.c           | 1148 ++++++++++++++++++++++++++
 tls/base/gtlsconnection-base.h           |  187 +++++
 tls/base/gtlsinputstream-base.c          |  142 ++++
 tls/base/gtlsinputstream-base.h          |   51 ++
 tls/base/gtlsoutputstream-base.c         |  142 ++++
 tls/base/gtlsoutputstream-base.h         |   51 ++
 tls/gnutls/Makefile.am                   |    6 +-
 tls/gnutls/gtlsclientconnection-gnutls.c |   74 +-
 tls/gnutls/gtlsconnection-gnutls.c       | 1290 ++++--------------------------
 tls/gnutls/gtlsconnection-gnutls.h       |   29 +-
 tls/gnutls/gtlsinputstream-gnutls.c      |  142 ----
 tls/gnutls/gtlsinputstream-gnutls.h      |   51 --
 tls/gnutls/gtlsoutputstream-gnutls.c     |  142 ----
 tls/gnutls/gtlsoutputstream-gnutls.h     |   51 --
 tls/gnutls/gtlsserverconnection-gnutls.c |   26 +-
 19 files changed, 1958 insertions(+), 1593 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index a9826c0..8069519 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -15,6 +15,8 @@ endif
 
 SUBDIRS += proxy/tests
 
+SUBDIRS += tls/base
+
 if HAVE_PKCS11
 SUBDIRS += tls/pkcs11
 endif
diff --git a/configure.ac b/configure.ac
index e970fd8..ab09de2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -241,6 +241,7 @@ AC_CONFIG_FILES([Makefile
                  proxy/libproxy/Makefile
                  proxy/gnome/Makefile
                  proxy/tests/Makefile
+                 tls/base/Makefile
                  tls/gnutls/Makefile
                  tls/pkcs11/Makefile
                  tls/tests/Makefile
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 4323e7f..5608e05 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -1,4 +1,5 @@
 proxy/libproxy/glibproxyresolver.c
+tls/base/gtlsconnection-base.c
 tls/gnutls/gtlscertificate-gnutls.c
 tls/gnutls/gtlsclientconnection-gnutls.c
 tls/gnutls/gtlsconnection-gnutls.c
diff --git a/tls/base/Makefile.am b/tls/base/Makefile.am
new file mode 100644
index 0000000..52c5055
--- /dev/null
+++ b/tls/base/Makefile.am
@@ -0,0 +1,15 @@
+include $(top_srcdir)/glib-networking.mk
+
+noinst_LTLIBRARIES += libtlsbase.la
+
+libtlsbase_la_SOURCES =                \
+       gtlsconnection-base.c           \
+       gtlsconnection-base.h           \
+       gtlsinputstream-base.c          \
+       gtlsinputstream-base.h          \
+       gtlsoutputstream-base.c         \
+       gtlsoutputstream-base.h         \
+       $(NULL)
+
+libtlsbase_la_LDFLAGS = $(module_flags)
+libtlsbase_la_LIBADD = $(GLIB_LIBS)
diff --git a/tls/base/gtlsconnection-base.c b/tls/base/gtlsconnection-base.c
new file mode 100644
index 0000000..e332a28
--- /dev/null
+++ b/tls/base/gtlsconnection-base.c
@@ -0,0 +1,1148 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2009-2011 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ */
+
+#include "config.h"
+#include "glib.h"
+
+#include <errno.h>
+
+#include "gtlsconnection-base.h"
+#include "gtlsinputstream-base.h"
+#include "gtlsoutputstream-base.h"
+
+#include <glib/gi18n-lib.h>
+
+static gboolean do_implicit_handshake (GTlsConnectionBase  *tls,
+                                      gboolean             blocking,
+                                      GCancellable        *cancellable,
+                                      GError             **error);
+static gboolean finish_handshake (GTlsConnectionBase  *tls,
+                                 GTask               *task,
+                                 GError             **error);
+
+G_DEFINE_ABSTRACT_TYPE (GTlsConnectionBase, g_tls_connection_base, G_TYPE_TLS_CONNECTION);
+
+enum
+{
+  PROP_0,
+  PROP_BASE_IO_STREAM,
+  PROP_REQUIRE_CLOSE_NOTIFY,
+  PROP_REHANDSHAKE_MODE,
+  PROP_USE_SYSTEM_CERTDB,
+  PROP_DATABASE,
+  PROP_CERTIFICATE,
+  PROP_INTERACTION,
+  PROP_PEER_CERTIFICATE,
+  PROP_PEER_CERTIFICATE_ERRORS
+};
+
+static void
+g_tls_connection_base_init (GTlsConnectionBase *tls)
+{
+  tls->need_handshake = TRUE;
+  tls->database_is_unset = TRUE;
+  tls->is_system_certdb = TRUE;
+
+  g_mutex_init (&tls->op_mutex);
+  tls->waiting_for_op = g_cancellable_new ();
+  g_cancellable_cancel (tls->waiting_for_op);
+}
+
+static void
+g_tls_connection_base_finalize (GObject *object)
+{
+  GTlsConnectionBase *tls = G_TLS_CONNECTION_BASE (object);
+
+  g_clear_object (&tls->base_io_stream);
+
+  g_clear_object (&tls->tls_istream);
+  g_clear_object (&tls->tls_ostream);
+
+  g_clear_object (&tls->database);
+  g_clear_object (&tls->certificate);
+  g_clear_error (&tls->certificate_error);
+  g_clear_object (&tls->peer_certificate);
+
+  g_clear_object (&tls->interaction);
+
+  /* This must always be NULL at this, as it holds a referehce to @gnutls as
+   * its source object. However, we clear it anyway just in case this changes
+   * in future. */
+  g_clear_object (&tls->implicit_handshake);
+
+  g_clear_error (&tls->handshake_error);
+  g_clear_error (&tls->read_error);
+  g_clear_error (&tls->write_error);
+  g_clear_object (&tls->read_cancellable);
+  g_clear_object (&tls->write_cancellable);
+
+  g_clear_object (&tls->waiting_for_op);
+  g_mutex_clear (&tls->op_mutex);
+
+  g_clear_pointer (&tls->app_data_buf, g_byte_array_unref);
+
+  G_OBJECT_CLASS (g_tls_connection_base_parent_class)->finalize (object);
+}
+
+static void
+g_tls_connection_base_get_property (GObject    *object,
+                                   guint       prop_id,
+                                   GValue     *value,
+                                   GParamSpec *pspec)
+{
+  GTlsConnectionBase *tls = G_TLS_CONNECTION_BASE (object);
+  GTlsBackend *backend;
+
+  switch (prop_id)
+    {
+    case PROP_BASE_IO_STREAM:
+      g_value_set_object (value, tls->base_io_stream);
+      break;
+
+    case PROP_REQUIRE_CLOSE_NOTIFY:
+      g_value_set_boolean (value, tls->require_close_notify);
+      break;
+
+    case PROP_REHANDSHAKE_MODE:
+      g_value_set_enum (value, tls->rehandshake_mode);
+      break;
+
+    case PROP_USE_SYSTEM_CERTDB:
+      g_value_set_boolean (value, tls->is_system_certdb);
+      break;
+
+    case PROP_DATABASE:
+      if (tls->database_is_unset)
+        {
+          backend = g_tls_backend_get_default ();
+          tls->database =  g_tls_backend_get_default_database (backend);
+          tls->database_is_unset = FALSE;
+        }
+      g_value_set_object (value, tls->database);
+      break;
+
+    case PROP_CERTIFICATE:
+      g_value_set_object (value, tls->certificate);
+      break;
+
+    case PROP_INTERACTION:
+      g_value_set_object (value, tls->interaction);
+      break;
+
+    case PROP_PEER_CERTIFICATE:
+      g_value_set_object (value, tls->peer_certificate);
+      break;
+
+    case PROP_PEER_CERTIFICATE_ERRORS:
+      g_value_set_flags (value, tls->peer_certificate_errors);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+g_tls_connection_base_set_property (GObject      *object,
+                                   guint         prop_id,
+                                   const GValue *value,
+                                   GParamSpec   *pspec)
+{
+  GTlsConnectionBase *tls = G_TLS_CONNECTION_BASE (object);
+  GInputStream *istream;
+  GOutputStream *ostream;
+  gboolean system_certdb;
+  GTlsBackend *backend;
+
+  switch (prop_id)
+    {
+    case PROP_BASE_IO_STREAM:
+      if (tls->base_io_stream)
+       {
+         g_object_unref (tls->base_io_stream);
+         tls->base_istream = NULL;
+         tls->base_ostream = NULL;
+       }
+      tls->base_io_stream = g_value_dup_object (value);
+      if (!tls->base_io_stream)
+       return;
+
+      istream = g_io_stream_get_input_stream (tls->base_io_stream);
+      ostream = g_io_stream_get_output_stream (tls->base_io_stream);
+
+      if (G_IS_POLLABLE_INPUT_STREAM (istream) &&
+         g_pollable_input_stream_can_poll (G_POLLABLE_INPUT_STREAM (istream)))
+       {
+         tls->base_istream = G_POLLABLE_INPUT_STREAM (istream);
+         tls->tls_istream = g_tls_input_stream_base_new (tls);
+       }
+      if (G_IS_POLLABLE_OUTPUT_STREAM (ostream) &&
+         g_pollable_output_stream_can_poll (G_POLLABLE_OUTPUT_STREAM (ostream)))
+       {
+         tls->base_ostream = G_POLLABLE_OUTPUT_STREAM (ostream);
+         tls->tls_ostream = g_tls_output_stream_base_new (tls);
+       }
+      break;
+
+    case PROP_REQUIRE_CLOSE_NOTIFY:
+      tls->require_close_notify = g_value_get_boolean (value);
+      break;
+
+    case PROP_REHANDSHAKE_MODE:
+      tls->rehandshake_mode = g_value_get_enum (value);
+      break;
+
+    case PROP_USE_SYSTEM_CERTDB:
+      system_certdb = g_value_get_boolean (value);
+      if (system_certdb != tls->is_system_certdb)
+        {
+          g_clear_object (&tls->database);
+          if (system_certdb)
+            {
+              backend = g_tls_backend_get_default ();
+              tls->database = g_tls_backend_get_default_database (backend);
+            }
+          tls->is_system_certdb = system_certdb;
+         tls->database_is_unset = FALSE;
+        }
+      break;
+
+    case PROP_DATABASE:
+      g_clear_object (&tls->database);
+      tls->database = g_value_dup_object (value);
+      tls->is_system_certdb = FALSE;
+      tls->database_is_unset = FALSE;
+      break;
+
+    case PROP_CERTIFICATE:
+      if (tls->certificate)
+       g_object_unref (tls->certificate);
+      tls->certificate = g_value_dup_object (value);
+      break;
+
+    case PROP_INTERACTION:
+      g_clear_object (&tls->interaction);
+      tls->interaction = g_value_dup_object (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+typedef enum {
+  G_TLS_CONNECTION_BASE_OP_HANDSHAKE,
+  G_TLS_CONNECTION_BASE_OP_READ,
+  G_TLS_CONNECTION_BASE_OP_WRITE,
+  G_TLS_CONNECTION_BASE_OP_CLOSE,
+} GTlsConnectionBaseOp;
+
+static gboolean
+claim_op (GTlsConnectionBase    *tls,
+         GTlsConnectionBaseOp   op,
+         gboolean               blocking,
+         GCancellable          *cancellable,
+         GError               **error)
+{
+ try_again:
+  if (g_cancellable_set_error_if_cancelled (cancellable, error))
+    return FALSE;
+
+  g_mutex_lock (&tls->op_mutex);
+
+  if (tls->closing || tls->closed)
+    {
+      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_CLOSED,
+                          _("Connection is closed"));
+      g_mutex_unlock (&tls->op_mutex);
+      return FALSE;
+    }
+
+  if (tls->handshake_error && op != G_TLS_CONNECTION_BASE_OP_CLOSE)
+    {
+      if (error)
+       *error = g_error_copy (tls->handshake_error);
+      g_mutex_unlock (&tls->op_mutex);
+      return FALSE;
+    }
+
+  if (op != G_TLS_CONNECTION_BASE_OP_HANDSHAKE &&
+      op != G_TLS_CONNECTION_BASE_OP_CLOSE)
+    {
+      if (tls->need_handshake && !tls->handshaking)
+       {
+         tls->handshaking = TRUE;
+         if (!do_implicit_handshake (tls, blocking, cancellable, error))
+           {
+             g_cancellable_reset (tls->waiting_for_op);
+             g_mutex_unlock (&tls->op_mutex);
+             return FALSE;
+           }
+       }
+
+      if (tls->need_finish_handshake &&
+         tls->implicit_handshake)
+       {
+         GError *my_error = NULL;
+         gboolean success;
+
+         tls->need_finish_handshake = FALSE;
+
+         g_mutex_unlock (&tls->op_mutex);
+         success = finish_handshake (tls, tls->implicit_handshake, &my_error);
+         g_clear_object (&tls->implicit_handshake);
+         g_mutex_lock (&tls->op_mutex);
+
+         if (!success || g_cancellable_set_error_if_cancelled (cancellable, &my_error))
+           {
+             g_propagate_error (error, my_error);
+             g_mutex_unlock (&tls->op_mutex);
+             return FALSE;
+           }
+       }
+    }
+
+  if ((op != G_TLS_CONNECTION_BASE_OP_WRITE && tls->reading) ||
+      (op != G_TLS_CONNECTION_BASE_OP_READ && tls->writing) ||
+      (op != G_TLS_CONNECTION_BASE_OP_HANDSHAKE && tls->handshaking))
+    {
+      GPollFD fds[2];
+      int nfds;
+
+      g_cancellable_reset (tls->waiting_for_op);
+
+      g_mutex_unlock (&tls->op_mutex);
+
+      if (!blocking)
+       {
+         g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK,
+                              _("Operation would block"));
+         return FALSE;
+       }
+
+      g_cancellable_make_pollfd (tls->waiting_for_op, &fds[0]);
+      if (g_cancellable_make_pollfd (cancellable, &fds[1]))
+       nfds = 2;
+      else
+       nfds = 1;
+      g_poll (fds, nfds, -1);
+      g_cancellable_release_fd (cancellable);
+
+      goto try_again;
+    }
+
+  if (op == G_TLS_CONNECTION_BASE_OP_HANDSHAKE)
+    tls->handshaking = TRUE;
+  if (op == G_TLS_CONNECTION_BASE_OP_CLOSE)
+    tls->closing = TRUE;
+
+  if (op != G_TLS_CONNECTION_BASE_OP_WRITE)
+    tls->reading = TRUE;
+  if (op != G_TLS_CONNECTION_BASE_OP_READ)
+    tls->writing = TRUE;
+
+  g_mutex_unlock (&tls->op_mutex);
+  return TRUE;
+}
+
+static void
+yield_op (GTlsConnectionBase       *tls,
+         GTlsConnectionBaseOp      op,
+         GTlsConnectionBaseStatus  status)
+{
+  g_mutex_lock (&tls->op_mutex);
+
+  if (op == G_TLS_CONNECTION_BASE_OP_HANDSHAKE)
+    tls->handshaking = FALSE;
+  else if (status == G_TLS_CONNECTION_BASE_REHANDSHAKE && !tls->handshaking)
+    tls->need_handshake = TRUE;
+
+  if (op == G_TLS_CONNECTION_BASE_OP_CLOSE)
+    tls->closing = FALSE;
+
+  if (op != G_TLS_CONNECTION_BASE_OP_WRITE)
+    tls->reading = FALSE;
+  if (op != G_TLS_CONNECTION_BASE_OP_READ)
+    tls->writing = FALSE;
+
+  g_cancellable_cancel (tls->waiting_for_op);
+  g_mutex_unlock (&tls->op_mutex);
+}
+
+void
+g_tls_connection_base_push_io (GTlsConnectionBase *tls,
+                              GIOCondition        direction,
+                              gboolean            blocking,
+                              GCancellable       *cancellable)
+{
+  g_assert (direction & (G_IO_IN | G_IO_OUT));
+
+  if (direction & G_IO_IN)
+    {
+      tls->read_blocking = blocking;
+      tls->read_cancellable = cancellable;
+      g_clear_error (&tls->read_error);
+    }
+
+  if (direction & G_IO_OUT)
+    {
+      tls->write_blocking = blocking;
+      tls->write_cancellable = cancellable;
+      g_clear_error (&tls->write_error);
+    }
+}
+
+GTlsConnectionBaseStatus
+g_tls_connection_base_pop_io (GTlsConnectionBase  *tls,
+                             GIOCondition         direction,
+                             gboolean             success,
+                             GError             **error)
+{
+  GError *my_error = NULL;
+
+  g_assert (direction & (G_IO_IN | G_IO_OUT));
+  g_assert (!error || !*error);
+
+  if (direction & G_IO_IN)
+    {
+      tls->read_cancellable = NULL;
+      if (!success)
+       {
+         my_error = tls->read_error;
+         tls->read_error = NULL;
+       }
+      else
+       g_clear_error (&tls->read_error);
+    }
+  if (direction & G_IO_OUT)
+    {
+      tls->write_cancellable = NULL;
+      if (!success && !my_error)
+       {
+         my_error = tls->write_error;
+         tls->write_error = NULL;
+       }
+      else
+       g_clear_error (&tls->write_error);
+    }
+
+  if (success)
+    return G_TLS_CONNECTION_BASE_OK;
+
+  if (g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
+    {
+      g_propagate_error (error, my_error);
+      return G_TLS_CONNECTION_BASE_WOULD_BLOCK;
+    }
+  else if (g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT))
+    {
+      g_propagate_error (error, my_error);
+      return G_TLS_CONNECTION_BASE_TIMED_OUT;
+    }
+  else if (my_error)
+    g_propagate_error (error, my_error);
+
+  return G_TLS_CONNECTION_BASE_ERROR;
+}
+
+gboolean
+g_tls_connection_base_check (GTlsConnectionBase  *tls,
+                            GIOCondition         condition)
+{
+  /* Racy, but worst case is that we just get WOULD_BLOCK back */
+  if (tls->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 (tls->handshaking || tls->closing)
+    return FALSE;
+
+  if (condition & G_IO_IN)
+    return g_pollable_input_stream_is_readable (tls->base_istream);
+  else
+    return g_pollable_output_stream_is_writable (tls->base_ostream);
+}
+
+typedef struct {
+  GSource             source;
+
+  GTlsConnectionBase *tls;
+  GObject            *stream;
+
+  GSource            *child_source;
+  GIOCondition        condition;
+
+  gboolean            io_waiting;
+  gboolean            op_waiting;
+} GTlsConnectionBaseSource;
+
+static gboolean
+tls_source_prepare (GSource *source,
+                    gint    *timeout)
+{
+  *timeout = -1;
+  return FALSE;
+}
+
+static gboolean
+tls_source_check (GSource *source)
+{
+  return FALSE;
+}
+
+static void
+tls_source_sync (GTlsConnectionBaseSource *tls_source)
+{
+  GTlsConnectionBase *tls = tls_source->tls;
+  gboolean io_waiting, op_waiting;
+
+  /* Was the source destroyed earlier in this main context iteration? */
+  if (g_source_is_destroyed ((GSource *) tls_source))
+    return;
+
+  g_mutex_lock (&tls->op_mutex);
+  if (((tls_source->condition & G_IO_IN) && tls->reading) ||
+      ((tls_source->condition & G_IO_OUT) && tls->writing) ||
+      (tls->handshaking && !tls->need_finish_handshake))
+    op_waiting = TRUE;
+  else
+    op_waiting = FALSE;
+
+  if (!op_waiting && !tls->need_handshake &&
+      !tls->need_finish_handshake)
+    io_waiting = TRUE;
+  else
+    io_waiting = FALSE;
+  g_mutex_unlock (&tls->op_mutex);
+
+  if (op_waiting == tls_source->op_waiting &&
+      io_waiting == tls_source->io_waiting)
+    return;
+  tls_source->op_waiting = op_waiting;
+  tls_source->io_waiting = io_waiting;
+
+  if (tls_source->child_source)
+    {
+      g_source_remove_child_source ((GSource *)tls_source,
+                                   tls_source->child_source);
+      g_source_unref (tls_source->child_source);
+    }
+
+  if (op_waiting)
+    tls_source->child_source = g_cancellable_source_new (tls->waiting_for_op);
+  else if (io_waiting && G_IS_POLLABLE_INPUT_STREAM (tls_source->stream))
+    tls_source->child_source = g_pollable_input_stream_create_source (tls->base_istream, NULL);
+  else if (io_waiting && G_IS_POLLABLE_OUTPUT_STREAM (tls_source->stream))
+    tls_source->child_source = g_pollable_output_stream_create_source (tls->base_ostream, NULL);
+  else
+    tls_source->child_source = g_timeout_source_new (0);
+
+  g_source_set_dummy_callback (tls_source->child_source);
+  g_source_add_child_source ((GSource *)tls_source, tls_source->child_source);
+}
+
+static gboolean
+tls_source_dispatch (GSource     *source,
+                     GSourceFunc  callback,
+                     gpointer     user_data)
+{
+  GPollableSourceFunc func = (GPollableSourceFunc)callback;
+  GTlsConnectionBaseSource *tls_source = (GTlsConnectionBaseSource *)source;
+  gboolean ret;
+
+  ret = (*func) (tls_source->stream, user_data);
+  if (ret)
+    tls_source_sync (tls_source);
+
+  return ret;
+}
+
+static void
+tls_source_finalize (GSource *source)
+{
+  GTlsConnectionBaseSource *tls_source = (GTlsConnectionBaseSource *)source;
+
+  g_object_unref (tls_source->tls);
+  g_source_unref (tls_source->child_source);
+}
+
+static gboolean
+g_tls_connection_tls_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 GSourceFuncs tls_source_funcs =
+{
+  tls_source_prepare,
+  tls_source_check,
+  tls_source_dispatch,
+  tls_source_finalize,
+  (GSourceFunc)g_tls_connection_tls_source_closure_callback,
+  (GSourceDummyMarshal)g_cclosure_marshal_generic
+};
+
+GSource *
+g_tls_connection_base_create_source (GTlsConnectionBase  *tls,
+                                    GIOCondition         condition,
+                                    GCancellable        *cancellable)
+{
+  GSource *source, *cancellable_source;
+  GTlsConnectionBaseSource *tls_source;
+
+  source = g_source_new (&tls_source_funcs, sizeof (GTlsConnectionBaseSource));
+  g_source_set_name (source, "GTlsConnectionBaseSource");
+  tls_source = (GTlsConnectionBaseSource *)source;
+  tls_source->tls = g_object_ref (tls);
+  tls_source->condition = condition;
+  if (condition & G_IO_IN)
+    tls_source->stream = G_OBJECT (tls->tls_istream);
+  else if (condition & G_IO_OUT)
+    tls_source->stream = G_OBJECT (tls->tls_ostream);
+
+  tls_source->op_waiting = (gboolean) -1;
+  tls_source->io_waiting = (gboolean) -1;
+  tls_source_sync (tls_source);
+
+  if (cancellable)
+    {
+      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);
+    }
+
+  return source;
+}
+
+void
+g_tls_connection_base_set_peer_certificate (GTlsConnectionBase   *tls,
+                                           GTlsCertificate      *peer_certificate,
+                                           GTlsCertificateFlags  peer_certificate_errors)
+{
+  if (tls->peer_certificate)
+    g_object_unref (tls->peer_certificate);
+  tls->peer_certificate = peer_certificate;
+  if (tls->peer_certificate)
+    g_object_ref (tls->peer_certificate);
+
+  tls->peer_certificate_errors = peer_certificate_errors;
+
+  g_object_notify (G_OBJECT (tls), "peer-certificate");
+  g_object_notify (G_OBJECT (tls), "peer-certificate-errors");
+}
+
+static void
+handshake_thread (GTask        *task,
+                 gpointer      object,
+                 gpointer      task_data,
+                 GCancellable *cancellable)
+{
+  GTlsConnectionBase *tls = object;
+  GTlsConnectionBaseClass *tls_class = G_TLS_CONNECTION_BASE_GET_CLASS (tls);
+  GError *error = NULL;
+
+  tls->started_handshake = FALSE;
+  tls->certificate_requested = FALSE;
+
+  if (!claim_op (tls, G_TLS_CONNECTION_BASE_OP_HANDSHAKE,
+                TRUE, cancellable, &error))
+    {
+      g_task_return_error (task, error);
+      return;
+    }
+
+  g_clear_error (&tls->handshake_error);
+
+  if (tls->ever_handshaked && !tls->need_handshake)
+    {
+      GTlsConnectionBaseStatus status;
+
+      status = tls_class->request_rehandshake (tls, cancellable, &error);
+      if (status != G_TLS_CONNECTION_BASE_OK)
+       {
+         g_task_return_error (task, error);
+         return;
+       }
+    }
+
+  g_clear_object (&tls->peer_certificate);
+  tls->peer_certificate_errors = 0;
+
+  tls->started_handshake = TRUE;
+  tls_class->handshake (tls, cancellable, &error);
+  tls->need_handshake = FALSE;
+
+  if (error)
+    {
+      if ((g_error_matches (error, G_IO_ERROR, G_IO_ERROR_FAILED) ||
+#if GLIB_CHECK_VERSION (2, 35, 3)
+          g_error_matches (error, G_IO_ERROR, G_IO_ERROR_BROKEN_PIPE) ||
+#endif
+          g_error_matches (error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS)) &&
+         tls->certificate_requested)
+       {
+         g_clear_error (&error);
+         if (tls->certificate_error)
+           {
+             error = tls->certificate_error;
+             tls->certificate_error = NULL;
+           }
+         else
+           {
+             g_set_error_literal (&error, G_TLS_ERROR, G_TLS_ERROR_CERTIFICATE_REQUIRED,
+                                  _("Server required TLS certificate"));
+           }
+       }
+      g_task_return_error (task, error);
+    }
+  else
+    {
+      tls->ever_handshaked = TRUE;
+      g_task_return_boolean (task, TRUE);
+    }
+}
+
+static gboolean
+finish_handshake (GTlsConnectionBase  *tls,
+                 GTask               *task,
+                 GError             **error)
+{
+  GTlsConnectionBaseClass *tls_class = G_TLS_CONNECTION_BASE_GET_CLASS (tls);
+  GError *my_error = NULL;
+
+  if (g_task_propagate_boolean (task, &my_error))
+    tls_class->complete_handshake (tls, &my_error);
+
+  if (my_error && tls->started_handshake)
+    tls->handshake_error = g_error_copy (my_error);
+
+  if (!my_error)
+    return TRUE;
+
+  g_propagate_error (error, my_error);
+  return FALSE;
+}
+
+static gboolean
+g_tls_connection_base_handshake (GTlsConnection   *conn,
+                                GCancellable     *cancellable,
+                                GError          **error)
+{
+  GTlsConnectionBase *tls = G_TLS_CONNECTION_BASE (conn);
+  GTask *task;
+  gboolean success;
+  GError *my_error = NULL;
+
+  task = g_task_new (conn, cancellable, NULL, NULL);
+  g_task_set_source_tag (task, g_tls_connection_base_handshake);
+  g_task_run_in_thread_sync (task, handshake_thread);
+  success = finish_handshake (tls, task, &my_error);
+  g_object_unref (task);
+
+  yield_op (tls, G_TLS_CONNECTION_BASE_OP_HANDSHAKE,
+           G_TLS_CONNECTION_BASE_OK);
+
+  if (my_error)
+    g_propagate_error (error, my_error);
+  return success;
+}
+
+/* In the async version we use two GTasks; one to run
+ * handshake_thread() and then call handshake_thread_completed(), and
+ * a second to call the caller's original callback after we call
+ * finish_handshake().
+ */
+
+static void
+handshake_thread_completed (GObject      *object,
+                           GAsyncResult *result,
+                           gpointer      user_data)
+{
+  GTask *caller_task = user_data;
+  GTlsConnectionBase *tls = g_task_get_source_object (caller_task);
+  GError *error = NULL;
+  gboolean need_finish_handshake, success;
+
+  g_mutex_lock (&tls->op_mutex);
+  if (tls->need_finish_handshake)
+    {
+      need_finish_handshake = TRUE;
+      tls->need_finish_handshake = FALSE;
+    }
+  else
+    need_finish_handshake = FALSE;
+  g_mutex_unlock (&tls->op_mutex);
+
+  if (need_finish_handshake)
+    {
+      success = finish_handshake (tls, G_TASK (result), &error);
+      if (success)
+       g_task_return_boolean (caller_task, TRUE);
+      else
+       g_task_return_error (caller_task, error);
+    }
+  else if (tls->handshake_error)
+    g_task_return_error (caller_task, g_error_copy (tls->handshake_error));
+  else
+    g_task_return_boolean (caller_task, TRUE);
+
+  g_object_unref (caller_task);
+}
+
+static void
+async_handshake_thread (GTask        *task,
+                       gpointer      object,
+                       gpointer      task_data,
+                       GCancellable *cancellable)
+{
+  GTlsConnectionBase *tls = object;
+
+  handshake_thread (task, object, task_data, cancellable);
+
+  g_mutex_lock (&tls->op_mutex);
+  tls->need_finish_handshake = TRUE;
+  /* yield_op will clear handshaking too, but we don't want the
+   * connection to be briefly "handshaking && need_finish_handshake"
+   * after we unlock the mutex.
+   */
+  tls->handshaking = FALSE;
+  g_mutex_unlock (&tls->op_mutex);
+
+  yield_op (tls, G_TLS_CONNECTION_BASE_OP_HANDSHAKE,
+           G_TLS_CONNECTION_BASE_OK);
+}
+
+static void
+g_tls_connection_base_handshake_async (GTlsConnection       *conn,
+                                      int                   io_priority,
+                                      GCancellable         *cancellable,
+                                      GAsyncReadyCallback   callback,
+                                      gpointer              user_data)
+{
+  GTask *thread_task, *caller_task;
+
+  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);
+  thread_task = g_task_new (conn, cancellable, handshake_thread_completed, caller_task);
+  g_task_set_source_tag (thread_task, g_tls_connection_base_handshake_async);
+  g_task_set_priority (thread_task, io_priority);
+
+  g_task_run_in_thread (thread_task, async_handshake_thread);
+  g_object_unref (thread_task);
+}
+
+static gboolean
+g_tls_connection_base_handshake_finish (GTlsConnection       *conn,
+                                       GAsyncResult         *result,
+                                       GError              **error)
+{
+  g_return_val_if_fail (g_task_is_valid (result, conn), FALSE);
+
+  return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+static void
+implicit_handshake_completed (GObject      *object,
+                             GAsyncResult *result,
+                             gpointer      user_data)
+{
+  GTlsConnectionBase *tls = G_TLS_CONNECTION_BASE (object);
+
+  g_mutex_lock (&tls->op_mutex);
+  tls->need_finish_handshake = TRUE;
+  g_mutex_unlock (&tls->op_mutex);
+
+  yield_op (tls, G_TLS_CONNECTION_BASE_OP_HANDSHAKE,
+           G_TLS_CONNECTION_BASE_OK);
+}
+
+static gboolean
+do_implicit_handshake (GTlsConnectionBase  *tls,
+                      gboolean             blocking,
+                      GCancellable        *cancellable,
+                      GError             **error)
+{
+  /* We have op_mutex */
+
+  tls->implicit_handshake = g_task_new (tls, cancellable,
+                                       implicit_handshake_completed,
+                                       NULL);
+  g_task_set_source_tag (tls->implicit_handshake, do_implicit_handshake);
+
+  if (blocking)
+    {
+      GError *my_error = NULL;
+      gboolean success;
+
+      g_mutex_unlock (&tls->op_mutex);
+      g_task_run_in_thread_sync (tls->implicit_handshake,
+                                handshake_thread);
+      success = finish_handshake (tls,
+                                 tls->implicit_handshake,
+                                 &my_error);
+      g_clear_object (&tls->implicit_handshake);
+      yield_op (tls, G_TLS_CONNECTION_BASE_OP_HANDSHAKE,
+               G_TLS_CONNECTION_BASE_OK);
+      g_mutex_lock (&tls->op_mutex);
+
+      if (my_error)
+       g_propagate_error (error, my_error);
+      return success;
+    }
+  else
+    {
+      g_task_run_in_thread (tls->implicit_handshake,
+                           handshake_thread);
+
+      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK,
+                          _("Operation would block"));
+      return FALSE;
+    }
+}
+
+gssize
+g_tls_connection_base_read (GTlsConnectionBase  *tls,
+                           void                *buffer,
+                           gsize                count,
+                           gboolean             blocking,
+                           GCancellable        *cancellable,
+                           GError             **error)
+{
+  GTlsConnectionBaseStatus status;
+  gssize nread;
+
+  do
+    {
+      if (!claim_op (tls, G_TLS_CONNECTION_BASE_OP_READ,
+                    blocking, cancellable, error))
+       return -1;
+
+      if (tls->app_data_buf && !tls->handshaking)
+       {
+         nread = MIN (count, tls->app_data_buf->len);
+         memcpy (buffer, tls->app_data_buf->data, nread);
+         if (nread == tls->app_data_buf->len)
+           g_clear_pointer (&tls->app_data_buf, g_byte_array_unref);
+         else
+           g_byte_array_remove_range (tls->app_data_buf, 0, nread);
+         status = G_TLS_CONNECTION_BASE_OK;
+       }
+      else
+       {
+         status = G_TLS_CONNECTION_BASE_GET_CLASS (tls)->
+           read_fn (tls, buffer, count, blocking, &nread, cancellable, error);
+       }
+
+      yield_op (tls, G_TLS_CONNECTION_BASE_OP_READ, status);
+    }
+  while (status == G_TLS_CONNECTION_BASE_REHANDSHAKE);
+
+  if (status == G_TLS_CONNECTION_BASE_OK)
+    return nread;
+  else
+    return -1;
+}
+
+gssize
+g_tls_connection_base_write (GTlsConnectionBase  *tls,
+                            const void          *buffer,
+                            gsize                count,
+                            gboolean             blocking,
+                            GCancellable        *cancellable,
+                            GError             **error)
+{
+  GTlsConnectionBaseStatus status;
+  gssize nwrote;
+
+  do
+    {
+      if (!claim_op (tls, G_TLS_CONNECTION_BASE_OP_WRITE,
+                    blocking, cancellable, error))
+       return -1;
+
+      status = G_TLS_CONNECTION_BASE_GET_CLASS (tls)->
+       write_fn (tls, buffer, count, blocking, &nwrote, cancellable, error);
+
+      yield_op (tls, G_TLS_CONNECTION_BASE_OP_WRITE, status);
+    }
+  while (status == G_TLS_CONNECTION_BASE_REHANDSHAKE);
+
+  if (status == G_TLS_CONNECTION_BASE_OK)
+    return nwrote;
+  else
+    return -1;
+}
+
+static GInputStream *
+g_tls_connection_base_get_input_stream (GIOStream *stream)
+{
+  GTlsConnectionBase *tls = G_TLS_CONNECTION_BASE (stream);
+
+  return tls->tls_istream;
+}
+
+static GOutputStream *
+g_tls_connection_base_get_output_stream (GIOStream *stream)
+{
+  GTlsConnectionBase *tls = G_TLS_CONNECTION_BASE (stream);
+
+  return tls->tls_ostream;
+}
+
+static gboolean
+g_tls_connection_base_close (GIOStream     *stream,
+                            GCancellable  *cancellable,
+                            GError       **error)
+{
+  GTlsConnectionBase *tls = G_TLS_CONNECTION_BASE (stream);
+  GTlsConnectionBaseStatus status;
+  gboolean success;
+
+  if (!claim_op (tls, G_TLS_CONNECTION_BASE_OP_CLOSE,
+                TRUE, cancellable, error))
+    return FALSE;
+
+  if (tls->closed)
+    {
+      yield_op (tls, G_TLS_CONNECTION_BASE_OP_CLOSE,
+               G_TLS_CONNECTION_BASE_ERROR);
+      return TRUE;
+    }
+
+  if (tls->ever_handshaked)
+    {
+      status = G_TLS_CONNECTION_BASE_GET_CLASS (tls)->
+       close_fn (tls, cancellable, error);
+    }
+  else
+    status = G_TLS_CONNECTION_BASE_OK;
+
+  tls->closed = TRUE;
+  if (status != G_TLS_CONNECTION_BASE_OK)
+    {
+      yield_op (tls, G_TLS_CONNECTION_BASE_OP_CLOSE, status);
+      return FALSE;
+    }
+
+  success = g_io_stream_close (tls->base_io_stream, cancellable, error);
+  yield_op (tls, G_TLS_CONNECTION_BASE_OP_CLOSE, status);
+  return success;
+}
+
+/* We do async close as synchronous-in-a-thread so we don't need to
+ * implement G_IO_IN/G_IO_OUT flip-flopping just for this one case
+ * (since handshakes are also done synchronously now).
+ */
+static void
+close_thread (GTask        *task,
+             gpointer      object,
+             gpointer      task_data,
+             GCancellable *cancellable)
+{
+  GIOStream *stream = object;
+  GError *error = NULL;
+
+  if (!g_tls_connection_base_close (stream, cancellable, &error))
+    g_task_return_error (task, error);
+  else
+    g_task_return_boolean (task, TRUE);
+}
+
+static void
+g_tls_connection_base_close_async (GIOStream           *stream,
+                                  int                  io_priority,
+                                  GCancellable        *cancellable,
+                                  GAsyncReadyCallback  callback,
+                                  gpointer             user_data)
+{
+  GTask *task;
+
+  task = g_task_new (stream, cancellable, callback, user_data);
+  g_task_set_source_tag (task, g_tls_connection_base_close_async);
+  g_task_set_priority (task, io_priority);
+  g_task_run_in_thread (task, close_thread);
+  g_object_unref (task);
+}
+
+static gboolean
+g_tls_connection_base_close_finish (GIOStream           *stream,
+                                   GAsyncResult        *result,
+                                   GError             **error)
+{
+  g_return_val_if_fail (g_task_is_valid (result, stream), FALSE);
+
+  return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+static void
+g_tls_connection_base_class_init (GTlsConnectionBaseClass *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_base_get_property;
+  gobject_class->set_property = g_tls_connection_base_set_property;
+  gobject_class->finalize     = g_tls_connection_base_finalize;
+
+  connection_class->handshake        = g_tls_connection_base_handshake;
+  connection_class->handshake_async  = g_tls_connection_base_handshake_async;
+  connection_class->handshake_finish = g_tls_connection_base_handshake_finish;
+
+  iostream_class->get_input_stream  = g_tls_connection_base_get_input_stream;
+  iostream_class->get_output_stream = g_tls_connection_base_get_output_stream;
+  iostream_class->close_fn          = g_tls_connection_base_close;
+  iostream_class->close_async       = g_tls_connection_base_close_async;
+  iostream_class->close_finish      = g_tls_connection_base_close_finish;
+
+  g_object_class_override_property (gobject_class, PROP_BASE_IO_STREAM, "base-io-stream");
+  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");
+}
diff --git a/tls/base/gtlsconnection-base.h b/tls/base/gtlsconnection-base.h
new file mode 100644
index 0000000..9f941b8
--- /dev/null
+++ b/tls/base/gtlsconnection-base.h
@@ -0,0 +1,187 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2009-2011 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2 of the licence or (at
+ * your option) any later version.
+ *
+ * See the included COPYING file for more information.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ */
+
+#ifndef __G_TLS_CONNECTION_BASE_H__
+#define __G_TLS_CONNECTION_BASE_H__
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_TLS_CONNECTION_BASE            (g_tls_connection_base_get_type ())
+#define G_TLS_CONNECTION_BASE(inst)           (G_TYPE_CHECK_INSTANCE_CAST ((inst), 
G_TYPE_TLS_CONNECTION_BASE, GTlsConnectionBase))
+#define G_TLS_CONNECTION_BASE_CLASS(class)    (G_TYPE_CHECK_CLASS_CAST ((class), G_TYPE_TLS_CONNECTION_BASE, 
GTlsConnectionBaseClass))
+#define G_IS_TLS_CONNECTION_BASE(inst)        (G_TYPE_CHECK_INSTANCE_TYPE ((inst), 
G_TYPE_TLS_CONNECTION_BASE))
+#define G_IS_TLS_CONNECTION_BASE_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), G_TYPE_TLS_CONNECTION_BASE))
+#define G_TLS_CONNECTION_BASE_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), 
G_TYPE_TLS_CONNECTION_BASE, GTlsConnectionBaseClass))
+
+typedef struct _GTlsConnectionBasePrivate                   GTlsConnectionBasePrivate;
+typedef struct _GTlsConnectionBaseClass                     GTlsConnectionBaseClass;
+typedef struct _GTlsConnectionBase                          GTlsConnectionBase;
+
+typedef enum {
+  G_TLS_CONNECTION_BASE_OK,
+  G_TLS_CONNECTION_BASE_WOULD_BLOCK,
+  G_TLS_CONNECTION_BASE_TIMED_OUT,
+  G_TLS_CONNECTION_BASE_REHANDSHAKE,
+  G_TLS_CONNECTION_BASE_TRY_AGAIN,
+  G_TLS_CONNECTION_BASE_ERROR,
+} GTlsConnectionBaseStatus;
+
+struct _GTlsConnectionBaseClass
+{
+  GTlsConnectionClass parent_class;
+
+  GTlsConnectionBaseStatus (*request_rehandshake)  (GTlsConnectionBase  *tls,
+                                                   GCancellable        *cancellable,
+                                                   GError             **error);
+  GTlsConnectionBaseStatus (*handshake)            (GTlsConnectionBase  *tls,
+                                                   GCancellable        *cancellable,
+                                                   GError             **error);
+  GTlsConnectionBaseStatus (*complete_handshake)   (GTlsConnectionBase  *tls,
+                                                   GError             **error);
+
+  GTlsConnectionBaseStatus (*read_fn)              (GTlsConnectionBase  *tls,
+                                                   void                *buffer,
+                                                   gsize                count,
+                                                   gboolean             blocking,
+                                                   gssize              *nread,
+                                                   GCancellable        *cancellable,
+                                                   GError             **error);
+  GTlsConnectionBaseStatus (*write_fn)             (GTlsConnectionBase  *tls,
+                                                   const void          *buffer,
+                                                   gsize                count,
+                                                   gboolean             blocking,
+                                                   gssize              *nwrote,
+                                                   GCancellable        *cancellable,
+                                                   GError             **error);
+
+  GTlsConnectionBaseStatus (*close_fn)             (GTlsConnectionBase  *tls,
+                                                   GCancellable        *cancellable,
+                                                   GError             **error);
+};
+
+struct _GTlsConnectionBase
+{
+  GTlsConnection         parent_instance;
+
+  GIOStream             *base_io_stream;
+  GPollableInputStream  *base_istream;
+  GPollableOutputStream *base_ostream;
+
+  GTlsDatabase          *database;
+  GTlsInteraction       *interaction;
+
+  GTlsCertificate       *certificate;
+  gboolean               certificate_requested;
+  GError                *certificate_error;
+  GTlsCertificate       *peer_certificate;
+  GTlsCertificateFlags   peer_certificate_errors;
+
+  gboolean               require_close_notify;
+  GTlsRehandshakeMode    rehandshake_mode;
+
+  /* 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 sending the first handshake packet (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;
+  gboolean       need_finish_handshake;
+  gboolean       started_handshake;
+  gboolean       handshaking;
+  gboolean       ever_handshaked;
+  GTask         *implicit_handshake;
+  GError        *handshake_error;
+  GByteArray    *app_data_buf;
+
+  gboolean       closing;
+  gboolean       closed;
+
+  gboolean       reading;
+  gboolean       read_blocking;
+  GError        *read_error;
+  GCancellable  *read_cancellable;
+
+  gboolean       writing;
+  gboolean       write_blocking;
+  GError        *write_error;
+  GCancellable  *write_cancellable;
+
+  /*< private >*/
+  gboolean       is_system_certdb;
+  gboolean       database_is_unset;
+
+  GInputStream  *tls_istream;
+  GOutputStream *tls_ostream;
+
+  GMutex         op_mutex;
+  GCancellable  *waiting_for_op;
+};
+
+GType g_tls_connection_base_get_type (void) G_GNUC_CONST;
+
+void g_tls_connection_base_set_peer_certificate (GTlsConnectionBase   *tls,
+                                                GTlsCertificate      *peer_certificate,
+                                                GTlsCertificateFlags  peer_certificate_errors);
+
+void     g_tls_connection_base_push_io       (GTlsConnectionBase *tls,
+                                             GIOCondition        direction,
+                                             gboolean            blocking,
+                                             GCancellable       *cancellable);
+GTlsConnectionBaseStatus
+         g_tls_connection_base_pop_io        (GTlsConnectionBase  *tls,
+                                             GIOCondition         direction,
+                                             gboolean             success,
+                                             GError             **error);
+
+gssize   g_tls_connection_base_read          (GTlsConnectionBase  *tls,
+                                             void                *buffer,
+                                             gsize                size,
+                                             gboolean             blocking,
+                                             GCancellable        *cancellable,
+                                             GError             **error);
+gssize   g_tls_connection_base_write         (GTlsConnectionBase  *tls,
+                                             const void          *buffer,
+                                             gsize                size,
+                                             gboolean             blocking,
+                                             GCancellable        *cancellable,
+                                             GError             **error);
+
+gboolean g_tls_connection_base_check         (GTlsConnectionBase  *tls,
+                                             GIOCondition         condition);
+GSource *g_tls_connection_base_create_source (GTlsConnectionBase  *tls,
+                                             GIOCondition         condition,
+                                             GCancellable        *cancellable);
+
+G_END_DECLS
+
+#endif /* __G_TLS_CONNECTION_BASE_H___ */
diff --git a/tls/base/gtlsinputstream-base.c b/tls/base/gtlsinputstream-base.c
new file mode 100644
index 0000000..e871d8a
--- /dev/null
+++ b/tls/base/gtlsinputstream-base.c
@@ -0,0 +1,142 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2010 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ */
+
+#include "config.h"
+#include "gtlsinputstream-base.h"
+
+static void g_tls_input_stream_base_pollable_iface_init (GPollableInputStreamInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GTlsInputStreamBase, g_tls_input_stream_base, G_TYPE_INPUT_STREAM,
+                        G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_INPUT_STREAM, 
g_tls_input_stream_base_pollable_iface_init)
+                        )
+
+struct _GTlsInputStreamBasePrivate
+{
+  GTlsConnectionBase *conn;
+};
+
+static void
+g_tls_input_stream_base_dispose (GObject *object)
+{
+  GTlsInputStreamBase *stream = G_TLS_INPUT_STREAM_BASE (object);
+
+  if (stream->priv->conn)
+    {
+      g_object_remove_weak_pointer (G_OBJECT (stream->priv->conn),
+                                   (gpointer *)&stream->priv->conn);
+      stream->priv->conn = NULL;
+    }
+
+  G_OBJECT_CLASS (g_tls_input_stream_base_parent_class)->dispose (object);
+}
+
+static gssize
+g_tls_input_stream_base_read (GInputStream  *stream,
+                             void          *buffer,
+                             gsize          count,
+                             GCancellable  *cancellable,
+                             GError       **error)
+{
+  GTlsInputStreamBase *tls_stream = G_TLS_INPUT_STREAM_BASE (stream);
+
+  g_return_val_if_fail (tls_stream->priv->conn != NULL, -1);
+
+  return g_tls_connection_base_read (tls_stream->priv->conn,
+                                    buffer, count, TRUE,
+                                    cancellable, error);
+}
+
+static gboolean
+g_tls_input_stream_base_pollable_is_readable (GPollableInputStream *pollable)
+{
+  GTlsInputStreamBase *tls_stream = G_TLS_INPUT_STREAM_BASE (pollable);
+
+  g_return_val_if_fail (tls_stream->priv->conn != NULL, FALSE);
+
+  return g_tls_connection_base_check (tls_stream->priv->conn, G_IO_IN);
+}
+
+static GSource *
+g_tls_input_stream_base_pollable_create_source (GPollableInputStream *pollable,
+                                               GCancellable         *cancellable)
+{
+  GTlsInputStreamBase *tls_stream = G_TLS_INPUT_STREAM_BASE (pollable);
+
+  g_return_val_if_fail (tls_stream->priv->conn != NULL, NULL);
+
+  return g_tls_connection_base_create_source (tls_stream->priv->conn,
+                                             G_IO_IN,
+                                             cancellable);
+}
+
+static gssize
+g_tls_input_stream_base_pollable_read_nonblocking (GPollableInputStream  *pollable,
+                                                  void                  *buffer,
+                                                  gsize                  size,
+                                                  GError               **error)
+{
+  GTlsInputStreamBase *tls_stream = G_TLS_INPUT_STREAM_BASE (pollable);
+
+  return g_tls_connection_base_read (tls_stream->priv->conn,
+                                    buffer, size, FALSE,
+                                    NULL, error);
+}
+
+static void
+g_tls_input_stream_base_class_init (GTlsInputStreamBaseClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GInputStreamClass *input_stream_class = G_INPUT_STREAM_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (GTlsInputStreamBasePrivate));
+
+  gobject_class->dispose = g_tls_input_stream_base_dispose;
+
+  input_stream_class->read_fn = g_tls_input_stream_base_read;
+}
+
+static void
+g_tls_input_stream_base_pollable_iface_init (GPollableInputStreamInterface *iface)
+{
+  iface->is_readable = g_tls_input_stream_base_pollable_is_readable;
+  iface->create_source = g_tls_input_stream_base_pollable_create_source;
+  iface->read_nonblocking = g_tls_input_stream_base_pollable_read_nonblocking;
+}
+
+static void
+g_tls_input_stream_base_init (GTlsInputStreamBase *stream)
+{
+  stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream, G_TYPE_TLS_INPUT_STREAM_BASE, 
GTlsInputStreamBasePrivate);
+}
+
+GInputStream *
+g_tls_input_stream_base_new (GTlsConnectionBase *conn)
+{
+  GTlsInputStreamBase *tls_stream;
+
+  tls_stream = g_object_new (G_TYPE_TLS_INPUT_STREAM_BASE, NULL);
+  tls_stream->priv->conn = conn;
+  g_object_add_weak_pointer (G_OBJECT (conn),
+                            (gpointer *)&tls_stream->priv->conn);
+
+  return G_INPUT_STREAM (tls_stream);
+}
diff --git a/tls/base/gtlsinputstream-base.h b/tls/base/gtlsinputstream-base.h
new file mode 100644
index 0000000..5f0694e
--- /dev/null
+++ b/tls/base/gtlsinputstream-base.h
@@ -0,0 +1,51 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2010 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2 of the licence or (at
+ * your option) any later version.
+ *
+ * See the included COPYING file for more information.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ */
+
+#ifndef __G_TLS_INPUT_STREAM_BASE_H__
+#define __G_TLS_INPUT_STREAM_BASE_H__
+
+#include <gio/gio.h>
+#include "gtlsconnection-base.h"
+
+G_BEGIN_DECLS
+
+#define G_TYPE_TLS_INPUT_STREAM_BASE            (g_tls_input_stream_base_get_type ())
+#define G_TLS_INPUT_STREAM_BASE(inst)           (G_TYPE_CHECK_INSTANCE_CAST ((inst), 
G_TYPE_TLS_INPUT_STREAM_BASE, GTlsInputStreamBase))
+#define G_TLS_INPUT_STREAM_BASE_CLASS(class)    (G_TYPE_CHECK_CLASS_CAST ((class), 
G_TYPE_TLS_INPUT_STREAM_BASE, GTlsInputStreamBaseClass))
+#define G_IS_TLS_INPUT_STREAM_BASE(inst)        (G_TYPE_CHECK_INSTANCE_TYPE ((inst), 
G_TYPE_TLS_INPUT_STREAM_BASE))
+#define G_IS_TLS_INPUT_STREAM_BASE_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), 
G_TYPE_TLS_INPUT_STREAM_BASE))
+#define G_TLS_INPUT_STREAM_BASE_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), 
G_TYPE_TLS_INPUT_STREAM_BASE, GTlsInputStreamBaseClass))
+
+typedef struct _GTlsInputStreamBasePrivate GTlsInputStreamBasePrivate;
+typedef struct _GTlsInputStreamBaseClass   GTlsInputStreamBaseClass;
+typedef struct _GTlsInputStreamBase        GTlsInputStreamBase;
+
+struct _GTlsInputStreamBaseClass
+{
+  GInputStreamClass parent_class;
+};
+
+struct _GTlsInputStreamBase
+{
+  GInputStream parent_instance;
+  GTlsInputStreamBasePrivate *priv;
+};
+
+GType         g_tls_input_stream_base_get_type (void) G_GNUC_CONST;
+GInputStream *g_tls_input_stream_base_new      (GTlsConnectionBase *conn);
+
+G_END_DECLS
+
+#endif /* __G_TLS_INPUT_STREAM_BASE_H___ */
diff --git a/tls/base/gtlsoutputstream-base.c b/tls/base/gtlsoutputstream-base.c
new file mode 100644
index 0000000..8b57579
--- /dev/null
+++ b/tls/base/gtlsoutputstream-base.c
@@ -0,0 +1,142 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2010 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ */
+
+#include "config.h"
+#include "gtlsoutputstream-base.h"
+
+static void g_tls_output_stream_base_pollable_iface_init (GPollableOutputStreamInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GTlsOutputStreamBase, g_tls_output_stream_base, G_TYPE_OUTPUT_STREAM,
+                        G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_OUTPUT_STREAM, 
g_tls_output_stream_base_pollable_iface_init)
+                        )
+
+struct _GTlsOutputStreamBasePrivate
+{
+  GTlsConnectionBase *conn;
+};
+
+static void
+g_tls_output_stream_base_dispose (GObject *object)
+{
+  GTlsOutputStreamBase *stream = G_TLS_OUTPUT_STREAM_BASE (object);
+
+  if (stream->priv->conn)
+    {
+      g_object_remove_weak_pointer (G_OBJECT (stream->priv->conn),
+                                   (gpointer *)&stream->priv->conn);
+      stream->priv->conn = NULL;
+    }
+
+  G_OBJECT_CLASS (g_tls_output_stream_base_parent_class)->dispose (object);
+}
+
+static gssize
+g_tls_output_stream_base_write (GOutputStream  *stream,
+                               const void     *buffer,
+                               gsize           count,
+                               GCancellable   *cancellable,
+                               GError        **error)
+{
+  GTlsOutputStreamBase *tls_stream = G_TLS_OUTPUT_STREAM_BASE (stream);
+
+  g_return_val_if_fail (tls_stream->priv->conn != NULL, -1);
+
+  return g_tls_connection_base_write (tls_stream->priv->conn,
+                                     buffer, count, TRUE,
+                                     cancellable, error);
+}
+
+static gboolean
+g_tls_output_stream_base_pollable_is_writable (GPollableOutputStream *pollable)
+{
+  GTlsOutputStreamBase *tls_stream = G_TLS_OUTPUT_STREAM_BASE (pollable);
+
+  g_return_val_if_fail (tls_stream->priv->conn != NULL, FALSE);
+
+  return g_tls_connection_base_check (tls_stream->priv->conn, G_IO_OUT);
+}
+
+static GSource *
+g_tls_output_stream_base_pollable_create_source (GPollableOutputStream *pollable,
+                                                GCancellable         *cancellable)
+{
+  GTlsOutputStreamBase *tls_stream = G_TLS_OUTPUT_STREAM_BASE (pollable);
+
+  g_return_val_if_fail (tls_stream->priv->conn != NULL, NULL);
+
+  return g_tls_connection_base_create_source (tls_stream->priv->conn,
+                                             G_IO_OUT,
+                                             cancellable);
+}
+
+static gssize
+g_tls_output_stream_base_pollable_write_nonblocking (GPollableOutputStream  *pollable,
+                                                    const void             *buffer,
+                                                    gsize                   size,
+                                                    GError                **error)
+{
+  GTlsOutputStreamBase *tls_stream = G_TLS_OUTPUT_STREAM_BASE (pollable);
+
+  return g_tls_connection_base_write (tls_stream->priv->conn,
+                                     buffer, size, FALSE,
+                                     NULL, error);
+}
+
+static void
+g_tls_output_stream_base_class_init (GTlsOutputStreamBaseClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GOutputStreamClass *output_stream_class = G_OUTPUT_STREAM_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (GTlsOutputStreamBasePrivate));
+
+  gobject_class->dispose = g_tls_output_stream_base_dispose;
+
+  output_stream_class->write_fn = g_tls_output_stream_base_write;
+}
+
+static void
+g_tls_output_stream_base_pollable_iface_init (GPollableOutputStreamInterface *iface)
+{
+  iface->is_writable = g_tls_output_stream_base_pollable_is_writable;
+  iface->create_source = g_tls_output_stream_base_pollable_create_source;
+  iface->write_nonblocking = g_tls_output_stream_base_pollable_write_nonblocking;
+}
+
+static void
+g_tls_output_stream_base_init (GTlsOutputStreamBase *stream)
+{
+  stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream, G_TYPE_TLS_OUTPUT_STREAM_BASE, 
GTlsOutputStreamBasePrivate);
+}
+
+GOutputStream *
+g_tls_output_stream_base_new (GTlsConnectionBase *conn)
+{
+  GTlsOutputStreamBase *tls_stream;
+
+  tls_stream = g_object_new (G_TYPE_TLS_OUTPUT_STREAM_BASE, NULL);
+  tls_stream->priv->conn = conn;
+  g_object_add_weak_pointer (G_OBJECT (conn),
+                            (gpointer *)&tls_stream->priv->conn);
+
+  return G_OUTPUT_STREAM (tls_stream);
+}
diff --git a/tls/base/gtlsoutputstream-base.h b/tls/base/gtlsoutputstream-base.h
new file mode 100644
index 0000000..b0a25e1
--- /dev/null
+++ b/tls/base/gtlsoutputstream-base.h
@@ -0,0 +1,51 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2010 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2 of the licence or (at
+ * your option) any later version.
+ *
+ * See the included COPYING file for more information.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ */
+
+#ifndef __G_TLS_OUTPUT_STREAM_BASE_H__
+#define __G_TLS_OUTPUT_STREAM_BASE_H__
+
+#include <gio/gio.h>
+#include "gtlsconnection-base.h"
+
+G_BEGIN_DECLS
+
+#define G_TYPE_TLS_OUTPUT_STREAM_BASE            (g_tls_output_stream_base_get_type ())
+#define G_TLS_OUTPUT_STREAM_BASE(inst)           (G_TYPE_CHECK_INSTANCE_CAST ((inst), 
G_TYPE_TLS_OUTPUT_STREAM_BASE, GTlsOutputStreamBase))
+#define G_TLS_OUTPUT_STREAM_BASE_CLASS(class)    (G_TYPE_CHECK_CLASS_CAST ((class), 
G_TYPE_TLS_OUTPUT_STREAM_BASE, GTlsOutputStreamBaseClass))
+#define G_IS_TLS_OUTPUT_STREAM_BASE(inst)        (G_TYPE_CHECK_INSTANCE_TYPE ((inst), 
G_TYPE_TLS_OUTPUT_STREAM_BASE))
+#define G_IS_TLS_OUTPUT_STREAM_BASE_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), 
G_TYPE_TLS_OUTPUT_STREAM_BASE))
+#define G_TLS_OUTPUT_STREAM_BASE_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), 
G_TYPE_TLS_OUTPUT_STREAM_BASE, GTlsOutputStreamBaseClass))
+
+typedef struct _GTlsOutputStreamBasePrivate GTlsOutputStreamBasePrivate;
+typedef struct _GTlsOutputStreamBaseClass   GTlsOutputStreamBaseClass;
+typedef struct _GTlsOutputStreamBase        GTlsOutputStreamBase;
+
+struct _GTlsOutputStreamBaseClass
+{
+  GOutputStreamClass parent_class;
+};
+
+struct _GTlsOutputStreamBase
+{
+  GOutputStream parent_instance;
+  GTlsOutputStreamBasePrivate *priv;
+};
+
+GType          g_tls_output_stream_base_get_type (void) G_GNUC_CONST;
+GOutputStream *g_tls_output_stream_base_new      (GTlsConnectionBase *conn);
+
+G_END_DECLS
+
+#endif /* __G_TLS_OUTPUT_STREAM_BASE_H___ */
diff --git a/tls/gnutls/Makefile.am b/tls/gnutls/Makefile.am
index 2fb482f..38f7547 100644
--- a/tls/gnutls/Makefile.am
+++ b/tls/gnutls/Makefile.am
@@ -38,22 +38,20 @@ libgiognutls_la_SOURCES =           \
        gtlsdatabase-gnutls.h           \
        gtlsfiledatabase-gnutls.c       \
        gtlsfiledatabase-gnutls.h       \
-       gtlsinputstream-gnutls.c        \
-       gtlsinputstream-gnutls.h        \
-       gtlsoutputstream-gnutls.c       \
-       gtlsoutputstream-gnutls.h       \
        gtlsserverconnection-gnutls.c   \
        gtlsserverconnection-gnutls.h   \
        $(P11_SRCS)                     \
        $(NULL)
 
 AM_CPPFLAGS +=                         \
+       -I$(top_srcdir)/tls/base        \
        $(P11_CFLAGS)                   \
        $(GNUTLS_CFLAGS)                \
        $(NULL)
 
 libgiognutls_la_LDFLAGS = $(module_flags)
 libgiognutls_la_LIBADD =               \
+       ../base/libtlsbase.la           \
        $(P11_LIBADD)                   \
        $(GLIB_LIBS)                    \
        $(GNUTLS_LIBS)                  \
diff --git a/tls/gnutls/gtlsclientconnection-gnutls.c b/tls/gnutls/gtlsclientconnection-gnutls.c
index cc64f9e..4d36c59 100644
--- a/tls/gnutls/gtlsclientconnection-gnutls.c
+++ b/tls/gnutls/gtlsclientconnection-gnutls.c
@@ -28,6 +28,7 @@
 #include <gnutls/x509.h>
 #include <string.h>
 
+#include "gtlsconnection-base.h"
 #include "gtlsclientconnection-gnutls.h"
 #include "gtlsbackend-gnutls.h"
 #include "gtlscertificate-gnutls.h"
@@ -65,8 +66,6 @@ struct _GTlsClientConnectionGnutlsPrivate
   GBytes *session_id;
   GBytes *session_data;
 
-  gboolean cert_requested;
-  GError *cert_error;
   GPtrArray *accepted_cas;
 };
 
@@ -147,7 +146,6 @@ g_tls_client_connection_gnutls_finalize (GObject *object)
   g_clear_pointer (&gnutls->priv->accepted_cas, g_ptr_array_unref);
   g_clear_pointer (&gnutls->priv->session_id, g_bytes_unref);
   g_clear_pointer (&gnutls->priv->session_data, g_bytes_unref);
-  g_clear_error (&gnutls->priv->cert_error);
 
   G_OBJECT_CLASS (g_tls_client_connection_gnutls_parent_class)->finalize (object);
 }
@@ -243,12 +241,13 @@ g_tls_client_connection_gnutls_retrieve_function (gnutls_session_t             s
                                                  gnutls_retr2_st             *st)
 {
   GTlsClientConnectionGnutls *gnutls = gnutls_transport_get_ptr (session);
+  GTlsConnectionBase *tls = gnutls_transport_get_ptr (session);
   GTlsConnectionGnutls *conn = G_TLS_CONNECTION_GNUTLS (gnutls);
   GPtrArray *accepted_cas;
   GByteArray *dn;
   int i;
 
-  gnutls->priv->cert_requested = TRUE;
+  tls->certificate_requested = TRUE;
 
   accepted_cas = g_ptr_array_new_with_free_func ((GDestroyNotify)g_byte_array_unref);
   for (i = 0; i < nreqs; i++)
@@ -267,8 +266,8 @@ g_tls_client_connection_gnutls_retrieve_function (gnutls_session_t             s
 
   if (st->ncerts == 0)
     {
-      g_clear_error (&gnutls->priv->cert_error);
-      if (g_tls_connection_gnutls_request_certificate (conn, &gnutls->priv->cert_error))
+      g_clear_error (&tls->certificate_error);
+      if (g_tls_connection_gnutls_request_certificate (conn, &tls->certificate_error))
         g_tls_connection_gnutls_get_certificate (conn, st);
     }
 
@@ -286,15 +285,17 @@ g_tls_client_connection_gnutls_failed (GTlsConnectionGnutls *conn)
     g_tls_backend_gnutls_remove_session (GNUTLS_CLIENT, gnutls->priv->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);
 
   /* Try to get a cached session */
   if (gnutls->priv->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->priv->session_data, NULL),
                                g_bytes_get_size (gnutls->priv->session_data));
     }
@@ -305,7 +306,7 @@ g_tls_client_connection_gnutls_begin_handshake (GTlsConnectionGnutls *conn)
       session_data = g_tls_backend_gnutls_lookup_session (GNUTLS_CLIENT, gnutls->priv->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->priv->session_data, g_bytes_unref);
@@ -313,36 +314,23 @@ g_tls_client_connection_gnutls_begin_handshake (GTlsConnectionGnutls *conn)
        }
     }
 
-  gnutls->priv->cert_requested = 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 (g_error_matches (*inout_error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS) &&
-      gnutls->priv->cert_requested)
-    {
-      g_clear_error (inout_error);
-      if (gnutls->priv->cert_error)
-       {
-         *inout_error = gnutls->priv->cert_error;
-         gnutls->priv->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->priv->session_data_override = FALSE;
@@ -351,11 +339,11 @@ g_tls_client_connection_gnutls_finish_handshake (GTlsConnectionGnutls  *conn,
         g_tls_backend_gnutls_remove_session (GNUTLS_CLIENT, gnutls->priv->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->priv->session_data = g_bytes_new_with_free_func (session_datum.data,
@@ -368,6 +356,8 @@ g_tls_client_connection_gnutls_finish_handshake (GTlsConnectionGnutls  *conn,
                                               gnutls->priv->session_data);
         }
     }
+
+    return status;
 }
 
 static void
@@ -393,7 +383,8 @@ 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);
 
   g_type_class_add_private (klass, sizeof (GTlsClientConnectionGnutlsPrivate));
 
@@ -402,9 +393,10 @@ g_tls_client_connection_gnutls_class_init (GTlsClientConnectionGnutlsClass *klas
   gobject_class->constructed  = g_tls_client_connection_gnutls_constructed;
   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->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 8fc71f6..be5fcb4 100644
--- a/tls/gnutls/gtlsconnection-gnutls.c
+++ b/tls/gnutls/gtlsconnection-gnutls.c
@@ -31,9 +31,6 @@
 #include "gtlsconnection-gnutls.h"
 #include "gtlsbackend-gnutls.h"
 #include "gtlscertificate-gnutls.h"
-#include "gtlsinputstream-gnutls.h"
-#include "gtlsoutputstream-gnutls.h"
-#include "gtlsserverconnection-gnutls.h"
 
 #ifdef HAVE_PKCS11
 #include <p11-kit/pin.h>
@@ -49,11 +46,6 @@ static ssize_t g_tls_connection_gnutls_pull_func (gnutls_transport_ptr_t  transp
                                                  void                   *buf,
                                                  size_t                  buflen);
 
-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);
-
 #ifdef HAVE_PKCS11
 static P11KitPin*    on_pin_prompt_callback  (const char     *pinfile,
                                               P11KitUri      *pin_uri,
@@ -62,108 +54,29 @@ static P11KitPin*    on_pin_prompt_callback  (const char     *pinfile,
                                               void           *callback_data);
 #endif
 
-static void g_tls_connection_gnutls_init_priorities (void);
+static void g_tls_connection_gnutls_initable_iface_init (GInitableIface *iface);
 
-static gboolean do_implicit_handshake (GTlsConnectionGnutls  *gnutls,
-                                      gboolean               blocking,
-                                      GCancellable          *cancellable,
-                                      GError               **error);
-static gboolean finish_handshake (GTlsConnectionGnutls  *gnutls,
-                                 GTask                 *thread_task,
-                                 GError               **error);
+static void g_tls_connection_gnutls_init_priorities (void);
 
-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_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
                                                         g_tls_connection_gnutls_initable_iface_init);
                                  g_tls_connection_gnutls_init_priorities ();
-                                 );
-
-
-enum
-{
-  PROP_0,
-  PROP_BASE_IO_STREAM,
-  PROP_REQUIRE_CLOSE_NOTIFY,
-  PROP_REHANDSHAKE_MODE,
-  PROP_USE_SYSTEM_CERTDB,
-  PROP_DATABASE,
-  PROP_CERTIFICATE,
-  PROP_INTERACTION,
-  PROP_PEER_CERTIFICATE,
-  PROP_PEER_CERTIFICATE_ERRORS
-};
+                                 )
 
 struct _GTlsConnectionGnutlsPrivate
 {
-  GIOStream *base_io_stream;
-  GPollableInputStream *base_istream;
-  GPollableOutputStream *base_ostream;
-
   gnutls_certificate_credentials_t creds;
   gnutls_session_t session;
 
-  GTlsCertificate *certificate, *peer_certificate;
-  GTlsCertificateFlags peer_certificate_errors;
-  GTlsCertificate *peer_certificate_tmp;
-  GTlsCertificateFlags peer_certificate_errors_tmp;
-
-  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;
-  gboolean started_handshake, handshaking, ever_handshaked;
-  GTask *implicit_handshake;
-  GError *handshake_error;
-  GByteArray *app_data_buf;
-
-  gboolean closing, closed;
-
-  GInputStream *tls_istream;
-  GOutputStream *tls_ostream;
-
-  GTlsInteraction *interaction;
   gchar *interaction_id;
 
-  GMutex        op_mutex;
-  GCancellable *waiting_for_op;
-
-  gboolean      reading;
-  gboolean      read_blocking;
-  GError       *read_error;
-  GCancellable *read_cancellable;
-
-  gboolean      writing;
-  gboolean      write_blocking;
-  GError       *write_error;
-  GCancellable *write_cancellable;
-
 #ifndef GNUTLS_E_PREMATURE_TERMINATION
   gboolean eof;
 #endif
+
+  GTlsCertificate *peer_certificate_tmp;
+  GTlsCertificateFlags peer_certificate_errors_tmp;
 };
 
 static gint unique_interaction_id = 0;
@@ -179,11 +92,6 @@ g_tls_connection_gnutls_init (GTlsConnectionGnutls *gnutls)
   gnutls_certificate_set_verify_flags (gnutls->priv->creds,
                                       GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
 
-  gnutls->priv->need_handshake = TRUE;
-
-  gnutls->priv->database_is_unset = TRUE;
-  gnutls->priv->is_system_certdb = TRUE;
-
   unique_id = g_atomic_int_add (&unique_interaction_id, 1);
   gnutls->priv->interaction_id = g_strdup_printf ("gtls:%d", unique_id);
 
@@ -191,10 +99,6 @@ g_tls_connection_gnutls_init (GTlsConnectionGnutls *gnutls)
   p11_kit_pin_register_callback (gnutls->priv->interaction_id,
                                  on_pin_prompt_callback, gnutls, NULL);
 #endif
-
-  gnutls->priv->waiting_for_op = g_cancellable_new ();
-  g_cancellable_cancel (gnutls->priv->waiting_for_op);
-  g_mutex_init (&gnutls->priv->op_mutex);
 }
 
 /* First field is "fallback", second is "allow unsafe rehandshaking" */
@@ -281,7 +185,7 @@ g_tls_connection_gnutls_set_handshake_priority (GTlsConnectionGnutls *gnutls)
     fallback = g_tls_client_connection_get_use_ssl3 (G_TLS_CLIENT_CONNECTION (gnutls));
   else
     fallback = FALSE;
-  unsafe_rehandshake = (gnutls->priv->rehandshake_mode == G_TLS_REHANDSHAKE_UNSAFELY);
+  unsafe_rehandshake = (G_TLS_CONNECTION_BASE (gnutls)->rehandshake_mode == G_TLS_REHANDSHAKE_UNSAFELY);
   gnutls_priority_set (gnutls->priv->session,
                       priorities[fallback][unsafe_rehandshake]);
 }
@@ -292,10 +196,11 @@ g_tls_connection_gnutls_initable_init (GInitable     *initable,
                                       GError       **error)
 {
   GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (initable);
+  GTlsConnectionBase *tls = G_TLS_CONNECTION_BASE (initable);
   int status;
 
-  g_return_val_if_fail (gnutls->priv->base_istream != NULL &&
-                       gnutls->priv->base_ostream != NULL, FALSE);
+  g_return_val_if_fail (tls->base_istream != NULL &&
+                       tls->base_ostream != NULL, FALSE);
 
   /* Make sure gnutls->priv->session has been initialized (it may have
    * already been initialized by a construct-time property setter).
@@ -319,9 +224,6 @@ g_tls_connection_gnutls_initable_init (GInitable     *initable,
                                      g_tls_connection_gnutls_pull_func);
   gnutls_transport_set_ptr (gnutls->priv->session, gnutls);
 
-  gnutls->priv->tls_istream = g_tls_input_stream_gnutls_new (gnutls);
-  gnutls->priv->tls_ostream = g_tls_output_stream_gnutls_new (gnutls);
-
   return TRUE;
 }
 
@@ -330,188 +232,22 @@ g_tls_connection_gnutls_finalize (GObject *object)
 {
   GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (object);
 
-  g_clear_object (&gnutls->priv->base_io_stream);
-
-  g_clear_object (&gnutls->priv->tls_istream);
-  g_clear_object (&gnutls->priv->tls_ostream);
-
   if (gnutls->priv->session)
     gnutls_deinit (gnutls->priv->session);
   if (gnutls->priv->creds)
     gnutls_certificate_free_credentials (gnutls->priv->creds);
 
-  g_clear_object (&gnutls->priv->database);
-  g_clear_object (&gnutls->priv->certificate);
-  g_clear_object (&gnutls->priv->peer_certificate);
-  g_clear_object (&gnutls->priv->peer_certificate_tmp);
-
-  g_clear_pointer (&gnutls->priv->app_data_buf, g_byte_array_unref);
-
 #ifdef HAVE_PKCS11
   p11_kit_pin_unregister_callback (gnutls->priv->interaction_id,
                                    on_pin_prompt_callback, gnutls);
 #endif
   g_free (gnutls->priv->interaction_id);
-  g_clear_object (&gnutls->priv->interaction);
 
-  g_clear_error (&gnutls->priv->handshake_error);
-  g_clear_error (&gnutls->priv->read_error);
-  g_clear_error (&gnutls->priv->write_error);
-
-  /* This must always be NULL at this, as it holds a referehce to @gnutls as
-   * its source object. However, we clear it anyway just in case this changes
-   * in future. */
-  g_clear_object (&gnutls->priv->implicit_handshake);
-
-  g_clear_object (&gnutls->priv->read_cancellable);
-  g_clear_object (&gnutls->priv->write_cancellable);
-
-  g_clear_object (&gnutls->priv->waiting_for_op);
-  g_mutex_clear (&gnutls->priv->op_mutex);
+  g_clear_object (&gnutls->priv->peer_certificate_tmp);
 
   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);
-  GTlsBackend *backend;
-
-  switch (prop_id)
-    {
-    case PROP_BASE_IO_STREAM:
-      g_value_set_object (value, gnutls->priv->base_io_stream);
-      break;
-
-    case PROP_REQUIRE_CLOSE_NOTIFY:
-      g_value_set_boolean (value, gnutls->priv->require_close_notify);
-      break;
-
-    case PROP_REHANDSHAKE_MODE:
-      g_value_set_enum (value, gnutls->priv->rehandshake_mode);
-      break;
-
-    case PROP_USE_SYSTEM_CERTDB:
-      g_value_set_boolean (value, gnutls->priv->is_system_certdb);
-      break;
-
-    case PROP_DATABASE:
-      if (gnutls->priv->database_is_unset)
-        {
-          backend = g_tls_backend_get_default ();
-          gnutls->priv->database =  g_tls_backend_get_default_database (backend);
-          gnutls->priv->database_is_unset = FALSE;
-        }
-      g_value_set_object (value, gnutls->priv->database);
-      break;
-
-    case PROP_CERTIFICATE:
-      g_value_set_object (value, gnutls->priv->certificate);
-      break;
-
-    case PROP_INTERACTION:
-      g_value_set_object (value, gnutls->priv->interaction);
-      break;
-
-    case PROP_PEER_CERTIFICATE:
-      g_value_set_object (value, gnutls->priv->peer_certificate);
-      break;
-
-    case PROP_PEER_CERTIFICATE_ERRORS:
-      g_value_set_flags (value, gnutls->priv->peer_certificate_errors);
-      break;
-
-    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);
-  GInputStream *istream;
-  GOutputStream *ostream;
-  gboolean system_certdb;
-  GTlsBackend *backend;
-
-  switch (prop_id)
-    {
-    case PROP_BASE_IO_STREAM:
-      if (gnutls->priv->base_io_stream)
-       {
-         g_object_unref (gnutls->priv->base_io_stream);
-         gnutls->priv->base_istream = NULL;
-         gnutls->priv->base_ostream = NULL;
-       }
-      gnutls->priv->base_io_stream = g_value_dup_object (value);
-      if (!gnutls->priv->base_io_stream)
-       return;
-
-      istream = g_io_stream_get_input_stream (gnutls->priv->base_io_stream);
-      ostream = g_io_stream_get_output_stream (gnutls->priv->base_io_stream);
-
-      if (G_IS_POLLABLE_INPUT_STREAM (istream) &&
-         g_pollable_input_stream_can_poll (G_POLLABLE_INPUT_STREAM (istream)))
-       gnutls->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)))
-       gnutls->priv->base_ostream = G_POLLABLE_OUTPUT_STREAM (ostream);
-      break;
-
-    case PROP_REQUIRE_CLOSE_NOTIFY:
-      gnutls->priv->require_close_notify = g_value_get_boolean (value);
-      break;
-
-    case PROP_REHANDSHAKE_MODE:
-      gnutls->priv->rehandshake_mode = g_value_get_enum (value);
-      break;
-
-    case PROP_USE_SYSTEM_CERTDB:
-      system_certdb = g_value_get_boolean (value);
-      if (system_certdb != gnutls->priv->is_system_certdb)
-        {
-          g_clear_object (&gnutls->priv->database);
-          if (system_certdb)
-            {
-              backend = g_tls_backend_get_default ();
-              gnutls->priv->database = g_tls_backend_get_default_database (backend);
-            }
-          gnutls->priv->is_system_certdb = system_certdb;
-          gnutls->priv->database_is_unset = FALSE;
-        }
-      break;
-
-    case PROP_DATABASE:
-      g_clear_object (&gnutls->priv->database);
-      gnutls->priv->database = g_value_dup_object (value);
-      gnutls->priv->is_system_certdb = FALSE;
-      gnutls->priv->database_is_unset = FALSE;
-      break;
-
-    case PROP_CERTIFICATE:
-      if (gnutls->priv->certificate)
-       g_object_unref (gnutls->priv->certificate);
-      gnutls->priv->certificate = g_value_dup_object (value);
-      break;
-
-    case PROP_INTERACTION:
-      g_clear_object (&gnutls->priv->interaction);
-      gnutls->priv->interaction = g_value_dup_object (value);
-      break;
-
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-    }
-}
-
 gnutls_certificate_credentials_t
 g_tls_connection_gnutls_get_credentials (GTlsConnectionGnutls *gnutls)
 {
@@ -524,7 +260,7 @@ g_tls_connection_gnutls_get_session (GTlsConnectionGnutls *gnutls)
   /* Ideally we would initialize gnutls->priv->session from
    * g_tls_connection_gnutls_init(), but we can't tell if it's a
    * client or server connection at that point... And
-   * g_tls_connection_gnutls_initiable_init() is too late, because
+   * g_tls_connection_gnutls_initable_init() is too late, because
    * construct-time property setters may need to modify it.
    */
   if (!gnutls->priv->session)
@@ -552,291 +288,103 @@ g_tls_connection_gnutls_get_certificate (GTlsConnectionGnutls *gnutls,
                                      gnutls->priv->interaction_id, st);
 }
 
-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,
-} GTlsConnectionGnutlsOp;
-
-static gboolean
-claim_op (GTlsConnectionGnutls    *gnutls,
-         GTlsConnectionGnutlsOp   op,
-         gboolean                 blocking,
-         GCancellable            *cancellable,
-         GError                 **error)
-{
- try_again:
-  if (g_cancellable_set_error_if_cancelled (cancellable, error))
-    return FALSE;
-
-  g_mutex_lock (&gnutls->priv->op_mutex);
-
-  if (gnutls->priv->closing || gnutls->priv->closed)
-    {
-      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_CLOSED,
-                          _("Connection is closed"));
-      g_mutex_unlock (&gnutls->priv->op_mutex);
-      return FALSE;
-    }
-
-  if (gnutls->priv->handshake_error && op != G_TLS_CONNECTION_GNUTLS_OP_CLOSE)
-    {
-      if (error)
-       *error = g_error_copy (gnutls->priv->handshake_error);
-      g_mutex_unlock (&gnutls->priv->op_mutex);
-      return FALSE;
-    }
-
-  if (op != G_TLS_CONNECTION_GNUTLS_OP_HANDSHAKE &&
-      op != G_TLS_CONNECTION_GNUTLS_OP_CLOSE)
-    {
-      if (gnutls->priv->need_handshake)
-       {
-         gnutls->priv->need_handshake = FALSE;
-         gnutls->priv->handshaking = TRUE;
-         if (!do_implicit_handshake (gnutls, blocking, cancellable, error))
-           {
-             g_mutex_unlock (&gnutls->priv->op_mutex);
-             return FALSE;
-           }
-       }
-
-      if (gnutls->priv->need_finish_handshake &&
-         gnutls->priv->implicit_handshake)
-       {
-         GError *my_error = NULL;
-         gboolean success;
-
-         gnutls->priv->need_finish_handshake = FALSE;
-
-         g_mutex_unlock (&gnutls->priv->op_mutex);
-         success = finish_handshake (gnutls, gnutls->priv->implicit_handshake, &my_error);
-         g_clear_object (&gnutls->priv->implicit_handshake);
-         g_mutex_lock (&gnutls->priv->op_mutex);
-
-         if (!success || g_cancellable_set_error_if_cancelled (cancellable, &my_error))
-           {
-             g_propagate_error (error, my_error);
-             g_mutex_unlock (&gnutls->priv->op_mutex);
-             return FALSE;
-           }
-       }
-    }
-
-  if ((op != G_TLS_CONNECTION_GNUTLS_OP_WRITE && gnutls->priv->reading) ||
-      (op != G_TLS_CONNECTION_GNUTLS_OP_READ && gnutls->priv->writing) ||
-      (op != G_TLS_CONNECTION_GNUTLS_OP_HANDSHAKE && gnutls->priv->handshaking))
-    {
-      GPollFD fds[2];
-      int nfds;
-
-      g_cancellable_reset (gnutls->priv->waiting_for_op);
-
-      g_mutex_unlock (&gnutls->priv->op_mutex);
-
-      if (!blocking)
-       {
-         g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK,
-                              _("Operation would block"));
-         return FALSE;
-       }
-
-      g_cancellable_make_pollfd (gnutls->priv->waiting_for_op, &fds[0]);
-      if (g_cancellable_make_pollfd (cancellable, &fds[1]))
-       nfds = 2;
-      else
-       nfds = 1;
-      g_poll (fds, nfds, -1);
-      g_cancellable_release_fd (cancellable);
-
-      goto try_again;
-    }
-
-  if (op == G_TLS_CONNECTION_GNUTLS_OP_HANDSHAKE)
-    {
-      gnutls->priv->handshaking = TRUE;
-      gnutls->priv->need_handshake = FALSE;
-    }
-  if (op == G_TLS_CONNECTION_GNUTLS_OP_CLOSE)
-    gnutls->priv->closing = TRUE;
-
-  if (op != G_TLS_CONNECTION_GNUTLS_OP_WRITE)
-    gnutls->priv->reading = TRUE;
-  if (op != G_TLS_CONNECTION_GNUTLS_OP_READ)
-    gnutls->priv->writing = TRUE;
-
-  g_mutex_unlock (&gnutls->priv->op_mutex);
-  return TRUE;
-}
-
-static void
-yield_op (GTlsConnectionGnutls   *gnutls,
-         GTlsConnectionGnutlsOp  op)
-{
-  g_mutex_lock (&gnutls->priv->op_mutex);
-
-  if (op == G_TLS_CONNECTION_GNUTLS_OP_HANDSHAKE)
-    gnutls->priv->handshaking = FALSE;
-  if (op == G_TLS_CONNECTION_GNUTLS_OP_CLOSE)
-    gnutls->priv->closing = FALSE;
-
-  if (op != G_TLS_CONNECTION_GNUTLS_OP_WRITE)
-    gnutls->priv->reading = FALSE;
-  if (op != G_TLS_CONNECTION_GNUTLS_OP_READ)
-    gnutls->priv->writing = FALSE;
-
-  g_cancellable_cancel (gnutls->priv->waiting_for_op);
-  g_mutex_unlock (&gnutls->priv->op_mutex);
-}
-
-static void
-begin_gnutls_io (GTlsConnectionGnutls  *gnutls,
-                GIOCondition           direction,
-                gboolean               blocking,
-                GCancellable          *cancellable)
-{
-  g_assert (direction & (G_IO_IN | G_IO_OUT));
-
-  if (direction & G_IO_IN)
-    {
-      gnutls->priv->read_blocking = blocking;
-      gnutls->priv->read_cancellable = cancellable;
-      g_clear_error (&gnutls->priv->read_error);
-    }
-
-  if (direction & G_IO_OUT)
-    {
-      gnutls->priv->write_blocking = blocking;
-      gnutls->priv->write_cancellable = cancellable;
-      g_clear_error (&gnutls->priv->write_error);
-    }
-}
-
-static int
+static GTlsConnectionBaseStatus
 end_gnutls_io (GTlsConnectionGnutls  *gnutls,
                GIOCondition           direction,
-               int                    status,
+               int                    ret,
                GError               **error,
                const char            *err_fmt,
                ...) G_GNUC_PRINTF(5, 6);
 
-static int
+static GTlsConnectionBaseStatus
 end_gnutls_io (GTlsConnectionGnutls  *gnutls,
                GIOCondition           direction,
-               int                    status,
+               int                    ret,
                GError               **error,
                const char            *err_fmt,
                ...)
 {
+  GTlsConnectionBase *tls = G_TLS_CONNECTION_BASE (gnutls);
   GError *my_error = NULL;
+  GTlsConnectionBaseStatus status;
 
-  g_assert (direction & (G_IO_IN | G_IO_OUT));
-  g_assert (!error || !*error);
-
-  if (status == GNUTLS_E_AGAIN ||
-      status == GNUTLS_E_WARNING_ALERT_RECEIVED)
-    return GNUTLS_E_AGAIN;
+  if (ret == GNUTLS_E_AGAIN ||
+      ret == GNUTLS_E_WARNING_ALERT_RECEIVED)
+    return G_TLS_CONNECTION_BASE_TRY_AGAIN;
 
-  if (direction & G_IO_IN)
+  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)
     {
-      gnutls->priv->read_cancellable = NULL;
-      if (status < 0)
-       {
-         my_error = gnutls->priv->read_error;
-         gnutls->priv->read_error = NULL;
-       }
-      else
-       g_clear_error (&gnutls->priv->read_error);
-    }
-  if (direction & G_IO_OUT)
-    {
-      gnutls->priv->write_cancellable = NULL;
-      if (status < 0 && !my_error)
-       {
-         my_error = gnutls->priv->write_error;
-         gnutls->priv->write_error = NULL;
-       }
-      else
-       g_clear_error (&gnutls->priv->write_error);
+      if (my_error)
+       g_propagate_error (error, my_error);
+      return status;
     }
 
-  if (status >= 0)
-    return status;
+  /* status == G_TLS_CONNECTION_BASE_ERROR */
 
-  if (gnutls->priv->handshaking && !gnutls->priv->ever_handshaked)
+  if (tls->handshaking && !tls->ever_handshaked)
     {
-      if (g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_FAILED) ||
-#if GLIB_CHECK_VERSION (2, 35, 3)
-         g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_BROKEN_PIPE) ||
-#endif
-         status == GNUTLS_E_UNEXPECTED_PACKET_LENGTH ||
-         status == GNUTLS_E_FATAL_ALERT_RECEIVED ||
-         status == GNUTLS_E_DECRYPTION_FAILED ||
-         status == GNUTLS_E_UNSUPPORTED_VERSION_PACKET)
+      if (ret == GNUTLS_E_UNEXPECTED_PACKET_LENGTH ||
+         ret == GNUTLS_E_FATAL_ALERT_RECEIVED ||
+         ret == GNUTLS_E_DECRYPTION_FAILED ||
+         ret == GNUTLS_E_UNSUPPORTED_VERSION_PACKET)
        {
-         g_clear_error (&my_error);
+         g_error_free (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;
+         return G_TLS_CONNECTION_BASE_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 (ret == GNUTLS_E_REHANDSHAKE)
     {
-      if (gnutls->priv->rehandshake_mode == G_TLS_REHANDSHAKE_NEVER)
+      if (tls->rehandshake_mode == G_TLS_REHANDSHAKE_NEVER)
        {
+         g_error_free (my_error);
          g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_MISC,
                               _("Peer requested illegal TLS rehandshake"));
-         return GNUTLS_E_PULL_ERROR;
+         G_TLS_CONNECTION_GNUTLS_GET_CLASS (gnutls)->failed (gnutls);
+         return G_TLS_CONNECTION_BASE_ERROR;
        }
 
-      g_mutex_lock (&gnutls->priv->op_mutex);
-      if (!gnutls->priv->handshaking)
-       gnutls->priv->need_handshake = TRUE;
-      g_mutex_unlock (&gnutls->priv->op_mutex);
-      return status;
+      return G_TLS_CONNECTION_BASE_REHANDSHAKE;
     }
   else if (
 #ifdef GNUTLS_E_PREMATURE_TERMINATION
-          status == GNUTLS_E_PREMATURE_TERMINATION
+          ret == GNUTLS_E_PREMATURE_TERMINATION
 #else
-          status == GNUTLS_E_UNEXPECTED_PACKET_LENGTH && gnutls->priv->eof
+          ret == GNUTLS_E_UNEXPECTED_PACKET_LENGTH && gnutls->priv->eof
 #endif
           )
     {
-      if (gnutls->priv->handshaking && !gnutls->priv->ever_handshaked)
+      if (tls->handshaking && !tls->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 (gnutls->priv->require_close_notify)
+      else if (tls->require_close_notify)
        {
+         g_error_free (my_error);
          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;
+         return G_TLS_CONNECTION_BASE_ERROR;
        }
       else
-       return 0;
+       return G_TLS_CONNECTION_BASE_OK;
     }
-  else if (status == GNUTLS_E_NO_CERTIFICATE_FOUND)
+  else if (ret == GNUTLS_E_NO_CERTIFICATE_FOUND)
     {
       g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_CERTIFICATE_REQUIRED,
                            _("TLS connection peer did not send a certificate"));
       return status;
     }
 
-  if (error)
+  g_propagate_error (error, my_error);
+  if (error && !*error)
     {
       va_list ap;
 
@@ -844,205 +392,18 @@ end_gnutls_io (GTlsConnectionGnutls  *gnutls,
       *error = g_error_new_valist (G_TLS_ERROR, G_TLS_ERROR_MISC, err_fmt, ap);
       va_end (ap);
     }
-  return status;
+
+  return G_TLS_CONNECTION_BASE_ERROR;
 }
 
 #define BEGIN_GNUTLS_IO(gnutls, direction, blocking, cancellable)      \
-  begin_gnutls_io (gnutls, direction, blocking, cancellable);          \
+  g_tls_connection_base_push_io (G_TLS_CONNECTION_BASE (gnutls),       \
+                                direction, blocking, cancellable);     \
   do {
 
-#define END_GNUTLS_IO(gnutls, direction, ret, errmsg, err)             \
-  } while ((ret = end_gnutls_io (gnutls, direction, ret, err, errmsg, gnutls_strerror (ret))) == 
GNUTLS_E_AGAIN);
-
-gboolean
-g_tls_connection_gnutls_check (GTlsConnectionGnutls  *gnutls,
-                              GIOCondition           condition)
-{
-  /* Racy, but worst case is that we just get WOULD_BLOCK back */
-  if (gnutls->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 (gnutls->priv->handshaking || gnutls->priv->closing)
-    return FALSE;
-
-  if (condition & G_IO_IN)
-    return g_pollable_input_stream_is_readable (gnutls->priv->base_istream);
-  else
-    return g_pollable_output_stream_is_writable (gnutls->priv->base_ostream);
-}
-
-typedef struct {
-  GSource               source;
-
-  GTlsConnectionGnutls *gnutls;
-  GObject              *stream;
-
-  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;
-}
-
-static void
-gnutls_source_sync (GTlsConnectionGnutlsSource *gnutls_source)
-{
-  GTlsConnectionGnutls *gnutls = gnutls_source->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 (&gnutls->priv->op_mutex);
-  if (((gnutls_source->condition & G_IO_IN) && gnutls->priv->reading) ||
-      ((gnutls_source->condition & G_IO_OUT) && gnutls->priv->writing) ||
-      (gnutls->priv->handshaking && !gnutls->priv->need_finish_handshake))
-    op_waiting = TRUE;
-  else
-    op_waiting = FALSE;
-
-  if (!op_waiting && !gnutls->priv->need_handshake &&
-      !gnutls->priv->need_finish_handshake)
-    io_waiting = TRUE;
-  else
-    io_waiting = FALSE;
-  g_mutex_unlock (&gnutls->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 (gnutls->priv->waiting_for_op);
-  else if (io_waiting && G_IS_POLLABLE_INPUT_STREAM (gnutls_source->stream))
-    gnutls_source->child_source = g_pollable_input_stream_create_source (gnutls->priv->base_istream, NULL);
-  else if (io_waiting && G_IS_POLLABLE_OUTPUT_STREAM (gnutls_source->stream))
-    gnutls_source->child_source = g_pollable_output_stream_create_source (gnutls->priv->base_ostream, NULL);
-  else
-    gnutls_source->child_source = g_timeout_source_new (0);
-
-  g_source_set_dummy_callback (gnutls_source->child_source);
-  g_source_add_child_source ((GSource *)gnutls_source, gnutls_source->child_source);
-}
-
-static gboolean
-gnutls_source_dispatch (GSource     *source,
-                       GSourceFunc  callback,
-                       gpointer     user_data)
-{
-  GPollableSourceFunc func = (GPollableSourceFunc)callback;
-  GTlsConnectionGnutlsSource *gnutls_source = (GTlsConnectionGnutlsSource *)source;
-  gboolean ret;
-
-  ret = (*func) (gnutls_source->stream, 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 GSourceFuncs gnutls_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
-};
-
-GSource *
-g_tls_connection_gnutls_create_source (GTlsConnectionGnutls  *gnutls,
-                                      GIOCondition           condition,
-                                      GCancellable          *cancellable)
-{
-  GSource *source, *cancellable_source;
-  GTlsConnectionGnutlsSource *gnutls_source;
-
-  source = g_source_new (&gnutls_source_funcs, sizeof (GTlsConnectionGnutlsSource));
-  g_source_set_name (source, "GTlsConnectionGnutlsSource");
-  gnutls_source = (GTlsConnectionGnutlsSource *)source;
-  gnutls_source->gnutls = g_object_ref (gnutls);
-  gnutls_source->condition = condition;
-  if (condition & G_IO_IN)
-    gnutls_source->stream = G_OBJECT (gnutls->priv->tls_istream);
-  else if (condition & G_IO_OUT)
-    gnutls_source->stream = G_OBJECT (gnutls->priv->tls_ostream);
-
-  gnutls_source->op_waiting = (gboolean) -1;
-  gnutls_source->io_waiting = (gboolean) -1;
-  gnutls_source_sync (gnutls_source);
-
-  if (cancellable)
-    {
-      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);
-    }
-
-  return source;
-}
+#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,
@@ -1070,25 +431,24 @@ 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;
   ssize_t ret;
 
-  /* If gnutls->priv->read_error is non-%NULL when we're called, it means
+  /* If tls->read_error is non-%NULL 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 (&gnutls->priv->read_error);
+  g_clear_error (&tls->read_error);
 
-  ret = g_pollable_stream_read (G_INPUT_STREAM (gnutls->priv->base_istream),
-                               buf, buflen,
-                               gnutls->priv->read_blocking,
-                               gnutls->priv->read_cancellable,
-                               &gnutls->priv->read_error);
+  ret = g_pollable_stream_read (G_INPUT_STREAM (tls->base_istream),
+                               buf, buflen, tls->read_blocking,
+                               tls->read_cancellable, &tls->read_error);
 
   if (ret < 0)
-    set_gnutls_error (gnutls, gnutls->priv->read_error);
+    set_gnutls_error (gnutls, tls->read_error);
 #ifndef GNUTLS_E_PREMATURE_TERMINATION
   else if (ret == 0)
     gnutls->priv->eof = TRUE;
@@ -1102,23 +462,48 @@ 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;
   ssize_t ret;
 
   /* See comment in pull_func. */
-  g_clear_error (&gnutls->priv->write_error);
+  g_clear_error (&tls->write_error);
 
-  ret = g_pollable_stream_write (G_OUTPUT_STREAM (gnutls->priv->base_ostream),
-                                buf, buflen,
-                                gnutls->priv->write_blocking,
-                                gnutls->priv->write_cancellable,
-                                &gnutls->priv->write_error);
+  ret = g_pollable_stream_write (G_OUTPUT_STREAM (tls->base_ostream),
+                                buf, buflen, tls->write_blocking,
+                                tls->write_cancellable, &tls->write_error);
   if (ret < 0)
-    set_gnutls_error (gnutls, gnutls->priv->write_error);
+    set_gnutls_error (gnutls, tls->write_error);
 
   return ret;
 }
 
+static GTlsConnectionBaseStatus
+g_tls_connection_gnutls_request_rehandshake (GTlsConnectionBase  *tls,
+                                            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, TRUE, 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)
 {
@@ -1185,50 +570,15 @@ verify_peer_certificate (GTlsConnectionGnutls *gnutls,
   return errors;
 }
 
-static void
-handshake_thread (GTask        *task,
-                 gpointer      object,
-                 gpointer      task_data,
-                 GCancellable *cancellable)
+static GTlsConnectionBaseStatus
+g_tls_connection_gnutls_handshake (GTlsConnectionBase  *tls,
+                                  GCancellable        *cancellable,
+                                  GError             **error)
 {
-  GTlsConnectionGnutls *gnutls = object;
-  gboolean is_client;
-  GError *error = NULL;
+  GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (tls);
+  GTlsConnectionBaseStatus status;
   int ret;
 
-  gnutls->priv->started_handshake = FALSE;
-
-  if (!claim_op (gnutls, G_TLS_CONNECTION_GNUTLS_OP_HANDSHAKE,
-                TRUE, cancellable, &error))
-    {
-      g_task_return_error (task, error);
-      return;
-    }
-
-  g_clear_error (&gnutls->priv->handshake_error);
-
-  is_client = G_IS_TLS_CLIENT_CONNECTION (gnutls);
-
-  if (!is_client && gnutls->priv->ever_handshaked &&
-      !gnutls->priv->implicit_handshake)
-    {
-      BEGIN_GNUTLS_IO (gnutls, G_IO_IN | G_IO_OUT, TRUE, cancellable);
-      ret = gnutls_rehandshake (gnutls->priv->session);
-      END_GNUTLS_IO (gnutls, G_IO_IN | G_IO_OUT, ret,
-                    _("Error performing TLS handshake: %s"), &error);
-
-      if (error)
-       {
-         g_task_return_error (task, error);
-         return;
-       }
-    }
-
-  gnutls->priv->started_handshake = TRUE;
-
-  g_clear_object (&gnutls->priv->peer_certificate);
-  gnutls->priv->peer_certificate_errors = 0;
-
   g_tls_connection_gnutls_set_handshake_priority (gnutls);
 
   BEGIN_GNUTLS_IO (gnutls, G_IO_IN | G_IO_OUT, TRUE, cancellable);
@@ -1241,14 +591,14 @@ handshake_thread (GTask        *task,
       ret = gnutls_record_recv (gnutls->priv->session, buf, sizeof (buf));
       if (ret > -1)
        {
-         if (!gnutls->priv->app_data_buf)
-           gnutls->priv->app_data_buf = g_byte_array_new ();
-         g_byte_array_append (gnutls->priv->app_data_buf, buf, ret);
+         if (!tls->app_data_buf)
+           tls->app_data_buf = g_byte_array_new ();
+         g_byte_array_append (tls->app_data_buf, buf, ret);
          ret = GNUTLS_E_AGAIN;
        }
     }
-  END_GNUTLS_IO (gnutls, G_IO_IN | G_IO_OUT, ret,
-                _("Error performing TLS handshake: %s"), &error);
+  END_GNUTLS_IO (gnutls, G_IO_IN | G_IO_OUT, ret, status,
+                _("Error performing TLS handshake: %s"), error);
 
   if (ret == 0 && gnutls_certificate_type_get (gnutls->priv->session) == GNUTLS_CRT_X509)
     {
@@ -1257,22 +607,12 @@ handshake_thread (GTask        *task,
        gnutls->priv->peer_certificate_errors_tmp = verify_peer_certificate (gnutls, 
gnutls->priv->peer_certificate_tmp);
       else if (G_IS_TLS_CLIENT_CONNECTION (gnutls))
        {
-         g_set_error_literal (&error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+         g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
                               _("Server did not return a valid TLS certificate"));
        }
     }
 
-  G_TLS_CONNECTION_GNUTLS_GET_CLASS (gnutls)->finish_handshake (gnutls, &error);
-
-  if (error)
-    {
-      g_task_return_error (task, error);
-    }
-  else
-    {
-      gnutls->priv->ever_handshaked = TRUE;
-      g_task_return_boolean (task, TRUE);
-    }
+  return status;
 }
 
 static gboolean
@@ -1301,382 +641,98 @@ accept_peer_certificate (GTlsConnectionGnutls *gnutls,
   return accepted;
 }
 
-static void
-begin_handshake (GTlsConnectionGnutls *gnutls)
-{
-  G_TLS_CONNECTION_GNUTLS_GET_CLASS (gnutls)->begin_handshake (gnutls);
-}
-
-static gboolean
-finish_handshake (GTlsConnectionGnutls  *gnutls,
-                 GTask                 *task,
-                 GError               **error)
+static GTlsConnectionBaseStatus
+g_tls_connection_gnutls_complete_handshake (GTlsConnectionBase  *tls,
+                                           GError             **error)
 {
+  GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (tls);
   GTlsCertificate *peer_certificate;
-  GTlsCertificateFlags peer_certificate_errors;
-
-  g_assert (error != NULL);
+  GTlsCertificateFlags peer_certificate_errors = 0;
 
   peer_certificate = gnutls->priv->peer_certificate_tmp;
   gnutls->priv->peer_certificate_tmp = NULL;
   peer_certificate_errors = gnutls->priv->peer_certificate_errors_tmp;
   gnutls->priv->peer_certificate_errors_tmp = 0;
 
-  if (g_task_propagate_boolean (task, error) && peer_certificate)
+  if (peer_certificate)
     {
       if (!accept_peer_certificate (gnutls, peer_certificate,
                                    peer_certificate_errors))
        {
          g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
                               _("Unacceptable TLS certificate"));
+         return G_TLS_CONNECTION_BASE_ERROR;
        }
 
-      gnutls->priv->peer_certificate = peer_certificate;
-      gnutls->priv->peer_certificate_errors = peer_certificate_errors;
-      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);
     }
 
-  if (*error && gnutls->priv->started_handshake)
-    gnutls->priv->handshake_error = g_error_copy (*error);
-
-  return (*error == NULL);
+  return G_TLS_CONNECTION_BASE_OK;
 }
 
-static gboolean
-g_tls_connection_gnutls_handshake (GTlsConnection   *conn,
-                                  GCancellable     *cancellable,
-                                  GError          **error)
-{
-  GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (conn);
-  GTask *task;
-  gboolean success;
-  GError *my_error = NULL;
-
-  task = g_task_new (conn, cancellable, NULL, NULL);
-  g_task_set_source_tag (task, g_tls_connection_gnutls_handshake);
-  begin_handshake (gnutls);
-  g_task_run_in_thread_sync (task, handshake_thread);
-  success = finish_handshake (gnutls, task, &my_error);
-  g_object_unref (task);
-
-  yield_op (gnutls, G_TLS_CONNECTION_GNUTLS_OP_HANDSHAKE);
-
-  if (my_error)
-    g_propagate_error (error, my_error);
-  return success;
-}
-
-/* In the async version we use two GTasks; one to run handshake_thread() and
- * then call handshake_thread_completed(), and a second to call the caller's
- * original callback after we call finish_handshake().
- */
-
-static void
-handshake_thread_completed (GObject      *object,
-                           GAsyncResult *result,
-                           gpointer      user_data)
-{
-  GTask *caller_task = user_data;
-  GTlsConnectionGnutls *gnutls = g_task_get_source_object (caller_task);
-  GError *error = NULL;
-  gboolean need_finish_handshake, success;
-
-  g_mutex_lock (&gnutls->priv->op_mutex);
-  if (gnutls->priv->need_finish_handshake)
-    {
-      need_finish_handshake = TRUE;
-      gnutls->priv->need_finish_handshake = FALSE;
-    }
-  else
-    need_finish_handshake = FALSE;
-  g_mutex_unlock (&gnutls->priv->op_mutex);
-
-  if (need_finish_handshake)
-    {
-      success = finish_handshake (gnutls, G_TASK (result), &error);
-      if (success)
-       g_task_return_boolean (caller_task, TRUE);
-      else
-       g_task_return_error (caller_task, error);
-    }
-  else if (gnutls->priv->handshake_error)
-    g_task_return_error (caller_task, g_error_copy (gnutls->priv->handshake_error));
-  else
-    g_task_return_boolean (caller_task, TRUE);
-
-  g_object_unref (caller_task);
-}
-
-static void
-async_handshake_thread (GTask        *task,
-                       gpointer      object,
-                       gpointer      task_data,
-                       GCancellable *cancellable)
-{
-  GTlsConnectionGnutls *gnutls = object;
-
-  handshake_thread (task, object, task_data, cancellable);
-
-  g_mutex_lock (&gnutls->priv->op_mutex);
-  gnutls->priv->need_finish_handshake = TRUE;
-  /* yield_op will clear handshaking too, but we don't want the
-   * connection to be briefly "handshaking && need_finish_handshake"
-   * after we unlock the mutex.
-   */
-  gnutls->priv->handshaking = FALSE;
-  g_mutex_unlock (&gnutls->priv->op_mutex);
-
-  yield_op (gnutls, G_TLS_CONNECTION_GNUTLS_OP_HANDSHAKE);
-}
-
-static void
-g_tls_connection_gnutls_handshake_async (GTlsConnection       *conn,
-                                        int                   io_priority,
-                                        GCancellable         *cancellable,
-                                        GAsyncReadyCallback   callback,
-                                        gpointer              user_data)
-{
-  GTask *thread_task, *caller_task;
-
-  caller_task = g_task_new (conn, cancellable, callback, user_data);
-  g_task_set_source_tag (caller_task, g_tls_connection_gnutls_handshake_async);
-  g_task_set_priority (caller_task, io_priority);
-
-  begin_handshake (G_TLS_CONNECTION_GNUTLS (conn));
-
-  thread_task = g_task_new (conn, cancellable,
-                           handshake_thread_completed, caller_task);
-  g_task_set_source_tag (thread_task, g_tls_connection_gnutls_handshake_async);
-  g_task_set_priority (thread_task, io_priority);
-  g_task_run_in_thread (thread_task, async_handshake_thread);
-  g_object_unref (thread_task);
-}
-
-static gboolean
-g_tls_connection_gnutls_handshake_finish (GTlsConnection       *conn,
-                                         GAsyncResult         *result,
-                                         GError              **error)
-{
-  g_return_val_if_fail (g_task_is_valid (result, conn), FALSE);
-
-  return g_task_propagate_boolean (G_TASK (result), error);
-}
-
-static gboolean
-do_implicit_handshake (GTlsConnectionGnutls  *gnutls,
-                      gboolean               blocking,
-                      GCancellable          *cancellable,
-                      GError               **error)
-{
-  /* We have op_mutex */
-
-  gnutls->priv->implicit_handshake = g_task_new (gnutls, cancellable, NULL, NULL);
-  g_task_set_source_tag (gnutls->priv->implicit_handshake,
-                         do_implicit_handshake);
-
-  begin_handshake (gnutls);
-
-  if (blocking)
-    {
-      GError *my_error = NULL;
-      gboolean success;
-
-      g_mutex_unlock (&gnutls->priv->op_mutex);
-      g_task_run_in_thread_sync (gnutls->priv->implicit_handshake,
-                                handshake_thread);
-      success = finish_handshake (gnutls,
-                                 gnutls->priv->implicit_handshake,
-                                 &my_error);
-      g_clear_object (&gnutls->priv->implicit_handshake);
-      yield_op (gnutls, G_TLS_CONNECTION_GNUTLS_OP_HANDSHAKE);
-      g_mutex_lock (&gnutls->priv->op_mutex);
-
-      if (my_error)
-       g_propagate_error (error, my_error);
-      return success;
-    }
-  else
-    {
-      g_task_run_in_thread (gnutls->priv->implicit_handshake,
-                           async_handshake_thread);
-
-      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK,
-                          _("Operation would block"));
-
-      return FALSE;
-    }
-}
-
-gssize
-g_tls_connection_gnutls_read (GTlsConnectionGnutls  *gnutls,
+static GTlsConnectionBaseStatus
+g_tls_connection_gnutls_read (GTlsConnectionBase    *tls,
                              void                  *buffer,
                              gsize                  count,
                              gboolean               blocking,
+                             gssize                *nread,
                              GCancellable          *cancellable,
                              GError               **error)
 {
+  GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (tls);
+  GTlsConnectionBaseStatus status;
   gssize ret;
 
-  if (gnutls->priv->app_data_buf && !gnutls->priv->handshaking)
-    {
-      ret = MIN (count, gnutls->priv->app_data_buf->len);
-      memcpy (buffer, gnutls->priv->app_data_buf->data, ret);
-      if (ret == gnutls->priv->app_data_buf->len)
-       g_clear_pointer (&gnutls->priv->app_data_buf, g_byte_array_unref);
-      else
-       g_byte_array_remove_range (gnutls->priv->app_data_buf, 0, ret);
-      return ret;
-    }
-
- again:
-  if (!claim_op (gnutls, G_TLS_CONNECTION_GNUTLS_OP_READ,
-                blocking, cancellable, error))
-    return -1;
-
   BEGIN_GNUTLS_IO (gnutls, G_IO_IN, blocking, cancellable);
   ret = gnutls_record_recv (gnutls->priv->session, buffer, count);
-  END_GNUTLS_IO (gnutls, G_IO_IN, ret, _("Error reading data from TLS socket: %s"), error);
-
-  yield_op (gnutls, G_TLS_CONNECTION_GNUTLS_OP_READ);
+  END_GNUTLS_IO (gnutls, G_IO_IN, ret, status,
+                _("Error reading data from TLS socket: %s"), error);
 
   if (ret >= 0)
-    return ret;
-  else if (ret == GNUTLS_E_REHANDSHAKE)
-    goto again;
-  else
-    return -1;
+    *nread = ret;
+  return status;
 }
 
-gssize
-g_tls_connection_gnutls_write (GTlsConnectionGnutls  *gnutls,
+static GTlsConnectionBaseStatus
+g_tls_connection_gnutls_write (GTlsConnectionBase    *tls,
                               const void            *buffer,
                               gsize                  count,
                               gboolean               blocking,
+                              gssize                *nwrote,
                               GCancellable          *cancellable,
                               GError               **error)
 {
+  GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (tls);
+  GTlsConnectionBaseStatus status;
   gssize ret;
 
- again:
-  if (!claim_op (gnutls, G_TLS_CONNECTION_GNUTLS_OP_WRITE,
-                blocking, cancellable, error))
-    return -1;
-
   BEGIN_GNUTLS_IO (gnutls, G_IO_OUT, blocking, cancellable);
   ret = gnutls_record_send (gnutls->priv->session, buffer, count);
-  END_GNUTLS_IO (gnutls, G_IO_OUT, ret, _("Error writing data to TLS socket: %s"), error);
-
-  yield_op (gnutls, G_TLS_CONNECTION_GNUTLS_OP_WRITE);
+  END_GNUTLS_IO (gnutls, G_IO_OUT, ret, status,
+                _("Error writing data to TLS socket: %s"), error);
 
   if (ret >= 0)
-    return ret;
-  else if (ret == GNUTLS_E_REHANDSHAKE)
-    goto again;
-  else
-    return -1;
-}
-
-static GInputStream  *
-g_tls_connection_gnutls_get_input_stream (GIOStream *stream)
-{
-  GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (stream);
-
-  return gnutls->priv->tls_istream;
-}
-
-static GOutputStream *
-g_tls_connection_gnutls_get_output_stream (GIOStream *stream)
-{
-  GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (stream);
-
-  return gnutls->priv->tls_ostream;
-}
-
-static gboolean
-g_tls_connection_gnutls_close (GIOStream     *stream,
-                              GCancellable  *cancellable,
-                              GError       **error)
-{
-  GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (stream);
-  gboolean success;
-  int ret = 0;
-
-  if (!claim_op (gnutls, G_TLS_CONNECTION_GNUTLS_OP_CLOSE,
-                TRUE, cancellable, error))
-    return FALSE;
-
-  if (gnutls->priv->closed)
-    {
-      yield_op (gnutls, G_TLS_CONNECTION_GNUTLS_OP_CLOSE);
-      return TRUE;
-    }
-
-  if (gnutls->priv->ever_handshaked)
-    {
-      BEGIN_GNUTLS_IO (gnutls, G_IO_IN | G_IO_OUT, TRUE, cancellable);
-      ret = gnutls_bye (gnutls->priv->session, GNUTLS_SHUT_WR);
-      END_GNUTLS_IO (gnutls, G_IO_IN | G_IO_OUT, ret,
-                    _("Error performing TLS close: %s"), error);
-    }
-
-  gnutls->priv->closed = TRUE;
-
-  if (ret != 0)
-    {
-      yield_op (gnutls, G_TLS_CONNECTION_GNUTLS_OP_CLOSE);
-      return FALSE;
-    }
-
-  success = g_io_stream_close (gnutls->priv->base_io_stream,
-                              cancellable, error);
-  yield_op (gnutls, G_TLS_CONNECTION_GNUTLS_OP_CLOSE);
-  return success;
-}
-
-/* We do async close as synchronous-in-a-thread so we don't need to
- * implement G_IO_IN/G_IO_OUT flip-flopping just for this one case
- * (since handshakes are also done synchronously now).
- */
-static void
-close_thread (GTask        *task,
-             gpointer      object,
-             gpointer      task_data,
-             GCancellable *cancellable)
-{
-  GIOStream *stream = object;
-  GError *error = NULL;
-
-  if (!g_tls_connection_gnutls_close (stream, cancellable, &error))
-    g_task_return_error (task, error);
-  else
-    g_task_return_boolean (task, TRUE);
+    *nwrote = ret;
+  return status;
 }
 
-static void
-g_tls_connection_gnutls_close_async (GIOStream           *stream,
-                                    int                  io_priority,
-                                    GCancellable        *cancellable,
-                                    GAsyncReadyCallback  callback,
-                                    gpointer             user_data)
+static GTlsConnectionBaseStatus
+g_tls_connection_gnutls_close (GTlsConnectionBase  *tls,
+                              GCancellable        *cancellable,
+                              GError             **error)
 {
-  GTask *task;
-
-  task = g_task_new (stream, cancellable, callback, user_data);
-  g_task_set_source_tag (task, g_tls_connection_gnutls_close_async);
-  g_task_set_priority (task, io_priority);
-  g_task_run_in_thread (task, close_thread);
-  g_object_unref (task);
-}
+  GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (tls);
+  GTlsConnectionBaseStatus status;
+  int ret;
 
-static gboolean
-g_tls_connection_gnutls_close_finish (GIOStream           *stream,
-                                     GAsyncResult        *result,
-                                     GError             **error)
-{
-  g_return_val_if_fail (g_task_is_valid (result, stream), FALSE);
+  BEGIN_GNUTLS_IO (gnutls, G_IO_IN | G_IO_OUT, TRUE, cancellable);
+  ret = gnutls_bye (gnutls->priv->session, GNUTLS_SHUT_WR);
+  END_GNUTLS_IO (gnutls, G_IO_IN | G_IO_OUT, ret, status,
+                _("Error performing TLS close: %s"), error);
 
-  return g_task_propagate_boolean (G_TASK (result), error);
+  return status;
 }
 
 #ifdef HAVE_PKCS11
@@ -1688,14 +744,14 @@ on_pin_prompt_callback (const char     *pinfile,
                         P11KitPinFlags  pin_flags,
                         void           *callback_data)
 {
-  GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (callback_data);
+  GTlsConnectionBase *tls = G_TLS_CONNECTION_BASE (callback_data);
   GTlsInteractionResult result;
   GTlsPasswordFlags flags = 0;
   GTlsPassword *password;
   P11KitPin *pin = NULL;
   GError *error = NULL;
 
-  if (!gnutls->priv->interaction)
+  if (!tls->interaction)
     return NULL;
 
   if (pin_flags & P11_KIT_PIN_FLAGS_RETRY)
@@ -1707,7 +763,7 @@ on_pin_prompt_callback (const char     *pinfile,
 
   password = g_pkcs11_pin_new (flags, pin_description);
 
-  result = g_tls_interaction_ask_password (gnutls->priv->interaction, password,
+  result = g_tls_interaction_ask_password (tls->interaction, password,
                                            g_cancellable_get_current (), &error);
 
   switch (result)
@@ -1736,34 +792,18 @@ 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);
+  GTlsConnectionBaseClass *base_class = G_TLS_CONNECTION_BASE_CLASS (klass);
 
   g_type_class_add_private (klass, sizeof (GTlsConnectionGnutlsPrivate));
 
-  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;
-
-  g_object_class_override_property (gobject_class, PROP_BASE_IO_STREAM, "base-io-stream");
-  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");
+  base_class->request_rehandshake = g_tls_connection_gnutls_request_rehandshake;
+  base_class->handshake           = g_tls_connection_gnutls_handshake;
+  base_class->complete_handshake  = g_tls_connection_gnutls_complete_handshake;
+  base_class->read_fn             = g_tls_connection_gnutls_read;
+  base_class->write_fn            = g_tls_connection_gnutls_write;
+  base_class->close_fn            = g_tls_connection_gnutls_close;
 }
 
 static void
@@ -1779,16 +819,18 @@ g_tls_connection_gnutls_request_certificate (GTlsConnectionGnutls  *self,
   GTlsInteractionResult res = G_TLS_INTERACTION_UNHANDLED;
   GTlsInteraction *interaction;
   GTlsConnection *conn;
+  GTlsConnectionBase *tls;
 
   g_return_val_if_fail (G_IS_TLS_CONNECTION_GNUTLS (self), FALSE);
 
   conn = G_TLS_CONNECTION (self);
+  tls = G_TLS_CONNECTION_BASE (self);
 
   interaction = g_tls_connection_get_interaction (conn);
   if (!interaction)
     return FALSE;
 
   res = g_tls_interaction_invoke_request_certificate (interaction, conn, 0,
-                                                     self->priv->read_cancellable, error);
+                                                     tls->read_cancellable, error);
   return res != G_TLS_INTERACTION_FAILED;
 }
diff --git a/tls/gnutls/gtlsconnection-gnutls.h b/tls/gnutls/gtlsconnection-gnutls.h
index a78469d..4d4d9a4 100644
--- a/tls/gnutls/gtlsconnection-gnutls.h
+++ b/tls/gnutls/gtlsconnection-gnutls.h
@@ -19,6 +19,8 @@
 #include <gio/gio.h>
 #include <gnutls/gnutls.h>
 
+#include "gtlsconnection-base.h"
+
 G_BEGIN_DECLS
 
 #define G_TYPE_TLS_CONNECTION_GNUTLS            (g_tls_connection_gnutls_get_type ())
@@ -34,18 +36,14 @@ typedef struct _GTlsConnectionGnutls                          GTlsConnectionGnut
 
 struct _GTlsConnectionGnutlsClass
 {
-  GTlsConnectionClass parent_class;
+  GTlsConnectionBaseClass parent_class;
 
   void     (*failed)           (GTlsConnectionGnutls  *gnutls);
-
-  void     (*begin_handshake)  (GTlsConnectionGnutls  *gnutls);
-  void     (*finish_handshake) (GTlsConnectionGnutls  *gnutls,
-                               GError               **inout_error);
 };
 
 struct _GTlsConnectionGnutls
 {
-  GTlsConnection parent_instance;
+  GTlsConnectionBase parent_instance;
   GTlsConnectionGnutlsPrivate *priv;
 };
 
@@ -60,25 +58,6 @@ void     g_tls_connection_gnutls_get_certificate     (GTlsConnectionGnutls  *gnu
 gboolean g_tls_connection_gnutls_request_certificate (GTlsConnectionGnutls  *gnutls,
                                                      GError               **error);
 
-gssize   g_tls_connection_gnutls_read          (GTlsConnectionGnutls  *gnutls,
-                                               void                  *buffer,
-                                               gsize                  size,
-                                               gboolean               blocking,
-                                               GCancellable          *cancellable,
-                                               GError               **error);
-gssize   g_tls_connection_gnutls_write         (GTlsConnectionGnutls  *gnutls,
-                                               const void            *buffer,
-                                               gsize                  size,
-                                               gboolean               blocking,
-                                               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);
-
 G_END_DECLS
 
 #endif /* __G_TLS_CONNECTION_GNUTLS_H___ */
diff --git a/tls/gnutls/gtlsserverconnection-gnutls.c b/tls/gnutls/gtlsserverconnection-gnutls.c
index 01def4c..7fe94ce 100644
--- a/tls/gnutls/gtlsserverconnection-gnutls.c
+++ b/tls/gnutls/gtlsserverconnection-gnutls.c
@@ -166,10 +166,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;
 
@@ -187,14 +189,11 @@ 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 */
@@ -259,16 +258,17 @@ 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);
 
   g_type_class_add_private (klass, sizeof (GTlsServerConnectionGnutlsPrivate));
 
   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");
 }


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