g_tls_connection_base_close_internal (GIOStream      *stream,
                                      GTlsDirection   direction,
                                      gint64          timeout,
                                      GCancellable   *cancellable,
                                      GError        **error)
{
  GTlsConnectionBase *tls = G_TLS_CONNECTION_BASE (stream);
  GTlsConnectionBasePrivate *priv = g_tls_connection_base_get_instance_private (tls);
  GTlsConnectionBaseOp op;
  GTlsConnectionBaseStatus status;
  gboolean success = TRUE;
  GError *close_error = NULL, *stream_error = NULL;

  /* This can be called from g_io_stream_close(), g_input_stream_close(),
   * g_output_stream_close(), or g_tls_connection_close(). In all cases, we only
   * do the close_fn() for writing. The difference is how we set the flags on
   * this class and how the underlying stream is closed.
   */

  g_return_val_if_fail (direction != G_TLS_DIRECTION_NONE, FALSE);

  if (direction == G_TLS_DIRECTION_BOTH)
    op = G_TLS_CONNECTION_BASE_OP_CLOSE_BOTH;
  else if (direction == G_TLS_DIRECTION_READ)
    op = G_TLS_CONNECTION_BASE_OP_CLOSE_READ;
  else
    op = G_TLS_CONNECTION_BASE_OP_CLOSE_WRITE;

  if (!claim_op (tls, op, timeout, cancellable, error))
    return FALSE;

  if (priv->ever_handshaked && !priv->write_closed &&
      direction & G_TLS_DIRECTION_WRITE)
    {
      status = G_TLS_CONNECTION_BASE_GET_CLASS (tls)->
        close_fn (tls, timeout, cancellable, &close_error);

      priv->write_closed = TRUE;
    }
  else
    status = G_TLS_CONNECTION_BASE_OK;

  if (!priv->read_closed && direction & G_TLS_DIRECTION_READ)
    priv->read_closed = TRUE;

  /* Close the underlying streams. Do this even if the close_fn() call failed,
   * as the parent GIOStream will have set its internal closed flag and hence
   * this implementation will never be called again. */
  if (priv->base_io_stream)
    {
      if (direction == G_TLS_DIRECTION_BOTH)
        success = g_io_stream_close (priv->base_io_stream,
                                     cancellable, &stream_error);
      else if (direction & G_TLS_DIRECTION_READ)
        success = g_input_stream_close (g_io_stream_get_input_stream (priv->base_io_stream),
                                        cancellable, &stream_error);
      else if (direction & G_TLS_DIRECTION_WRITE)
        success = g_output_stream_close (g_io_stream_get_output_stream (priv->base_io_stream),
                                         cancellable, &stream_error);
    }
  else if (g_tls_connection_base_is_dtls (tls))
    {
      /* We do not close underlying #GDatagramBaseds. There is no
       * g_datagram_based_close() method since different datagram-based
       * protocols vary wildly in how they close. */
      success = TRUE;
    }
  else
    {
      g_assert_not_reached ();
    }

  yield_op (tls, op, status);

  /* Propagate errors. */
  if (status != G_TLS_CONNECTION_BASE_OK)
    {
      g_propagate_error (error, close_error);
      g_clear_error (&stream_error);
    }
  else if (!success)
    {
      g_propagate_error (error, stream_error);
      g_clear_error (&close_error);
    }

  return success && status == G_TLS_CONNECTION_BASE_OK;
}