Merge commit 'scormi3/master'
authorThadeu Lima de Souza Cascardo <cascardo@minaslivre.org>
Mon, 10 Mar 2008 12:38:45 +0000 (09:38 -0300)
committerThadeu Lima de Souza Cascardo <cascardo@minaslivre.org>
Mon, 10 Mar 2008 12:38:45 +0000 (09:38 -0300)
Conflicts:

gnio/gsocket.c
gnio/gsocket.h

1  2 
gnio/gsocket.c
gnio/gsocket.h
gnio/gtcpclient.c
test/test-server.c

diff --combined gnio/gsocket.c
@@@ -24,9 -24,7 +24,7 @@@
  #include <config.h>
  #include <glib.h>
  #include <gio/gio.h>
- #include <gnio/gnio.h>
  #include "gasynchelper.h"
- #include "gnioenumtypes.h"
  
  #include <string.h>
  #ifndef G_OS_WIN32
@@@ -53,9 -51,6 +51,9 @@@ G_DEFINE_TYPE (GSocket, g_socket, G_TYP
  enum
  {
    PROP_0,
 +  PROP_DOMAIN,
 +  PROP_TYPE,
 +  PROP_PROTOCOL,
    PROP_FD,
    PROP_BLOCKING,
    PROP_BACKLOG,
  
  struct _GSocketPrivate
  {
 +  GSocketDomain domain;
 +  GSocketType type;
 +  gchar *protocol;
    gint fd;
    gboolean blocking;
    gint backlog;
    gboolean reuse_address;
 +  GError *error;
    GSocketAddress *local_address;
    GSocketAddress *remote_address;
  };
  
 +static void
 +g_socket_constructed (GObject *object)
 +{
 +  GSocket *sock = G_SOCKET (object);
 +  GError *error = NULL;
 +  static GStaticMutex getprotobyname_mutex = G_STATIC_MUTEX_INIT;
 +  gint fd, native_domain, native_type, native_protocol;
 +
 +  if (sock->priv->fd >= 0)
 +    {
 +      // we've been constructed from an existing file descriptor
 +      glong arg;
 +      gboolean blocking;
 +
 +      // TODO: set the socket type with getsockopt (SO_TYPE)
 +      // TODO: what should we do about domain and protocol?
 +
 +      if ((arg = fcntl (sock->priv->fd, F_GETFL, NULL)) < 0)
 +        g_warning ("Error getting socket status flags: %s", g_strerror (errno));
 +
 +      blocking = ((arg & O_NONBLOCK) == 0);
 +
 +      return;
 +    }
 +
 +  switch (sock->priv->domain)
 +    {
 +      case G_SOCKET_DOMAIN_INET:
 +        native_domain = PF_INET;
 +        break;
 +
 +      case G_SOCKET_DOMAIN_INET6:
 +        native_domain = PF_INET6;
 +        break;
 +
 +      case G_SOCKET_DOMAIN_UNIX:
 +        native_domain = PF_UNIX;
 +        break;
 +
 +      default:
 +        g_set_error (&error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "unsupported socket domain");
 +        return;
 +    }
 +
 +  switch (sock->priv->type)
 +    {
 +      case G_SOCKET_TYPE_STREAM:
 +        native_type = SOCK_STREAM;
 +        break;
 +
 +      case G_SOCKET_TYPE_DATAGRAM:
 +        native_type = SOCK_DGRAM;
 +        break;
 +
 +      case G_SOCKET_TYPE_SEQPACKET:
 +        native_type = SOCK_SEQPACKET;
 +        break;
 +
 +      default:
 +        g_set_error (&error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "unsupported socket type");
 +        return;
 +    }
 +
 +  if (sock->priv->protocol == NULL)
 +    native_protocol = 0;
 +  else
 +    {
 +      struct protoent *ent;
 +      g_static_mutex_lock (&getprotobyname_mutex);
 +      if (!(ent = getprotobyname (sock->priv->protocol)))
 +        {
 +          g_static_mutex_unlock (&getprotobyname_mutex);
 +          g_set_error (&error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "unsupported socket protocol");
 +          return;
 +        }
 +      native_protocol = ent->p_proto;
 +      g_static_mutex_unlock (&getprotobyname_mutex);
 +    }
 +
 +  fd = socket (native_domain, native_type, native_protocol);
 +
 +  if (fd < 0)
 +    {
 +      g_set_error (&error, G_IO_ERROR, g_io_error_from_errno (errno), "unable to create socket: %s", g_strerror (errno));
 +      return;
 +    }
 +
 +  sock->priv->fd = fd;
 +}
 +
  static void
  g_socket_get_property (GObject    *object,
                         guint       prop_id,
  
    switch (prop_id)
      {
 +      case PROP_DOMAIN:
 +        g_value_set_enum (value, socket->priv->domain);
 +        break;
 +
 +      case PROP_TYPE:
 +        g_value_set_enum (value, socket->priv->type);
 +        break;
 +
 +      case PROP_PROTOCOL:
 +        g_value_set_string (value, socket->priv->protocol);
 +        break;
 +
        case PROP_FD:
          g_value_set_int (value, socket->priv->fd);
          break;
@@@ -229,18 -118,6 +227,18 @@@ g_socket_set_property (GObject      *ob
  
    switch (prop_id)
      {
 +      case PROP_DOMAIN:
 +        socket->priv->domain = g_value_get_enum (value);
 +        break;
 +
 +      case PROP_TYPE:
 +        socket->priv->type = g_value_get_enum (value);
 +        break;
 +
 +      case PROP_PROTOCOL:
 +        socket->priv->protocol = g_value_dup_string (value);
 +        break;
 +
        case PROP_FD:
          socket->priv->fd = g_value_get_int (value);
          break;
@@@ -282,10 -159,6 +280,10 @@@ g_socket_dispose (GObject *object
  {
    GSocket *socket = G_SOCKET (object);
  
 +  g_free (socket->priv->protocol);
 +
 +  g_clear_error (&socket->priv->error);
 +
    g_socket_close (socket);
  
    if (G_OBJECT_CLASS (g_socket_parent_class)->dispose)
@@@ -303,33 -176,9 +301,33 @@@ g_socket_class_init (GSocketClass *klas
  
    gobject_class->finalize = g_socket_finalize;
    gobject_class->dispose = g_socket_dispose;
 +  gobject_class->constructed = g_socket_constructed;
    gobject_class->set_property = g_socket_set_property;
    gobject_class->get_property = g_socket_get_property;
  
 +  g_object_class_install_property (gobject_class, PROP_DOMAIN,
 +                                   g_param_spec_enum ("domain",
 +                                                      "socket domain",
 +                                                      "the socket's domain",
 +                                                      G_TYPE_SOCKET_DOMAIN,
 +                                                      G_SOCKET_DOMAIN_INET,
 +                                                      G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB | G_PARAM_STATIC_NICK));
 +
 +  g_object_class_install_property (gobject_class, PROP_TYPE,
 +                                   g_param_spec_enum ("type",
 +                                                      "socket type",
 +                                                      "the socket's type",
 +                                                      G_TYPE_SOCKET_TYPE,
 +                                                      G_SOCKET_TYPE_STREAM,
 +                                                      G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB | G_PARAM_STATIC_NICK));
 +
 +  g_object_class_install_property (gobject_class, PROP_PROTOCOL,
 +                                   g_param_spec_string ("protocol",
 +                                                        "socket protocol",
 +                                                        "the socket's protocol",
 +                                                        NULL,
 +                                                        G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB | G_PARAM_STATIC_NICK));
 +
    g_object_class_install_property (gobject_class, PROP_FD,
                                     g_param_spec_int ("fd",
                                                       "file descriptor",
@@@ -385,22 -234,93 +383,22 @@@ g_socket_init (GSocket *socket
    socket->priv->fd = -1;
    socket->priv->blocking = TRUE;
    socket->priv->backlog = 10;
 +  socket->priv->reuse_address = FALSE;
 +  socket->priv->error = NULL;
    socket->priv->remote_address = NULL;
    socket->priv->local_address = NULL;
  }
  
  GSocket *
 -g_socket_new (GSocketDomain domain, GSocketType type, const gchar *protocol, GError **error)
 +g_socket_new (GSocketDomain domain, GSocketType type, const gchar *protocol)
  {
 -  static GStaticMutex getprotobyname_mutex = G_STATIC_MUTEX_INIT;
 -  gint fd, native_domain, native_type, native_protocol;
 -
 -  switch (domain)
 -    {
 -      case G_SOCKET_DOMAIN_INET:
 -        native_domain = PF_INET;
 -        break;
 -
 -      case G_SOCKET_DOMAIN_INET6:
 -        native_domain = PF_INET6;
 -        break;
 -
 -      case G_SOCKET_DOMAIN_UNIX:
 -        native_domain = PF_UNIX;
 -        break;
 -
 -      default:
 -        g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "unsupported socket domain");
 -        return NULL;
 -    }
 -
 -  switch (type)
 -    {
 -      case G_SOCKET_TYPE_STREAM:
 -        native_type = SOCK_STREAM;
 -        break;
 -
 -      case G_SOCKET_TYPE_DATAGRAM:
 -        native_type = SOCK_DGRAM;
 -        break;
 -
 -      case G_SOCKET_TYPE_SEQPACKET:
 -        native_type = SOCK_SEQPACKET;
 -        break;
 -
 -      default:
 -        g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "unsupported socket type");
 -        return NULL;
 -    }
 -
 -  if (protocol == NULL)
 -    native_protocol = 0;
 -  else
 -    {
 -      struct protoent *ent;
 -      g_static_mutex_lock (&getprotobyname_mutex);
 -      if (!(ent = getprotobyname (protocol)))
 -        {
 -          g_static_mutex_unlock (&getprotobyname_mutex);
 -          g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "unsupported socket protocol");
 -          return NULL;
 -        }
 -      native_protocol = ent->p_proto;
 -      g_static_mutex_unlock (&getprotobyname_mutex);
 -    }
 -
 -  fd = socket(native_domain, native_type, native_protocol);
 -
 -  if (fd < 0)
 -    {
 -      g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), "unable to create socket: %s", g_strerror (errno));
 -      return NULL;
 -    }
 -
 -  return G_SOCKET (g_object_new (G_TYPE_SOCKET, "fd", fd, "blocking", TRUE, NULL));
 +  return G_SOCKET (g_object_new (G_TYPE_SOCKET, "domain", domain, "type", type, "protocol", protocol, NULL));
  }
  
  GSocket *
  g_socket_new_from_fd (gint fd)
  {
 -  glong arg;
 -  gboolean blocking;
 -
 -  if ((arg = fcntl (fd, F_GETFL, NULL)) < 0)
 -    g_warning ("Error getting socket status flags: %s", g_strerror (errno));
 -
 -  blocking = ((arg & O_NONBLOCK) == 0);
 -
 -  return G_SOCKET (g_object_new (G_TYPE_SOCKET, "fd", fd, "blocking", blocking, NULL));
 +  return G_SOCKET (g_object_new (G_TYPE_SOCKET, "fd", fd, NULL));
  }
  
  void
@@@ -420,6 -340,8 +418,8 @@@ g_socket_set_blocking (GSocket  *socket
      g_warning ("Error setting socket status flags: %s", g_strerror (errno));
  
    socket->priv->blocking = blocking;
+   g_object_notify (G_OBJECT (socket), "blocking");
  }
  
  gboolean
@@@ -442,6 -364,8 +442,8 @@@ g_socket_set_reuse_address (GSocket  *s
      g_warning ("error setting reuse address: %s", g_strerror (errno));
  
    socket->priv->reuse_address = reuse;
+   g_object_notify (G_OBJECT (socket), "reuse-address");
  }
  
  gboolean
@@@ -452,12 -376,36 +454,36 @@@ g_socket_get_reuse_address (GSocket *so
    return socket->priv->reuse_address;
  }
  
+ gboolean
+ g_socket_has_socket_error (GSocket  *socket,
+                            GError  **error)
+ {
+   gint sockerr;
+   guint32 sockerr_size = sizeof (sockerr);
+   g_return_val_if_fail (G_IS_SOCKET (socket), FALSE);
+   if (getsockopt (socket->priv->fd, SOL_SOCKET, SO_ERROR, (gpointer) &sockerr, &sockerr_size) < 0)
+     {
+       g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), "could not get socket error: %s", g_strerror (errno));
+       return TRUE;
+     }
+   if (sockerr != 0)
+     {
+       g_set_error (error, G_IO_ERROR, g_io_error_from_errno (sockerr), "error connecting: %s", g_strerror (sockerr));
+       return TRUE;
+     }
+   return FALSE;
+ }
  GSocketAddress *
  g_socket_get_local_address (GSocket  *socket,
                              GError  **error)
  {
    gchar buffer[256];
-   gsize len = 256;
+   guint32 len = 256;
  
    g_return_val_if_fail (G_IS_SOCKET (socket), NULL);
  
@@@ -478,7 -426,7 +504,7 @@@ g_socket_get_remote_address (GSocket  *
                               GError  **error)
  {
    gchar buffer[256];
-   gsize len = 256;
+   guint32 len = 256;
  
    g_return_val_if_fail (G_IS_SOCKET (socket), NULL);
  
    return (socket->priv->remote_address = g_object_ref_sink (g_socket_address_from_native (buffer, len)));
  }
  
 +gboolean
 +g_socket_has_error (GSocket  *socket,
 +                    GError  **error)
 +{
 +  g_return_val_if_fail (G_IS_SOCKET (socket), FALSE);
 +
 +  if (!socket->priv->error)
 +    return FALSE;
 +
 +  return TRUE;
 +}
 +
  gboolean
  g_socket_listen (GSocket  *socket,
                   GError  **error)
  {
    g_return_val_if_fail (G_IS_SOCKET (socket), FALSE);
  
 +  if (g_socket_has_error (socket, error))
 +    return FALSE;
 +
    if (listen (socket->priv->fd, socket->priv->backlog) < 0)
      {
        g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), "could not listen: %s", g_strerror (errno));
@@@ -531,9 -464,6 +557,9 @@@ g_socket_bind (GSocket         *socket
  {
    g_return_val_if_fail (G_IS_SOCKET (socket) && G_IS_SOCKET_ADDRESS (address), FALSE);
  
 +  if (g_socket_has_error (socket, error))
 +    return FALSE;
 +
    {
      gchar addr[256];
  
@@@ -560,11 -490,6 +586,11 @@@ g_socket_accept (GSocket       *socket
  {
    gint ret;
  
 +  g_return_val_if_fail (G_IS_SOCKET (socket), NULL);
 +
 +  if (g_socket_has_error (socket, error))
 +    return NULL;
 +
    if ((ret = accept (socket->priv->fd, NULL, 0)) < 0)
      {
        g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), "error accepting connection: %s", g_strerror (errno));
@@@ -583,9 -508,6 +609,9 @@@ g_socket_connect (GSocket         *sock
  
    g_return_val_if_fail (G_IS_SOCKET (socket) && G_IS_SOCKET_ADDRESS (address), FALSE);
  
 +  if (g_socket_has_error (socket, error))
 +    return FALSE;
 +
    g_socket_address_to_native (address, buffer);
  
    if (connect (socket->priv->fd, (struct sockaddr *) buffer, g_socket_address_native_size (address)) < 0)
@@@ -623,7 -545,7 +649,7 @@@ g_socket_receive (GSocket       *socket
  
  gssize
  g_socket_send (GSocket       *socket,
-                gchar         *buffer,
+                const gchar   *buffer,
                 gsize          size,
                 GError       **error)
  {
@@@ -659,5 -581,5 +685,5 @@@ g_socket_create_source (GSocket      *s
  {
    g_return_val_if_fail (G_IS_SOCKET (socket) && (cancellable == NULL || G_IS_CANCELLABLE (cancellable)), NULL);
  
-   return _g_fd_source_new (socket->priv->fd, G_IO_IN | G_IO_HUP | G_IO_ERR, cancellable);
+   return _g_fd_source_new (socket->priv->fd, condition, cancellable);
  }
diff --combined gnio/gsocket.h
@@@ -72,7 -72,8 +72,7 @@@ GType            g_socket_get_typ
  
  GSocket *        g_socket_new                (GSocketDomain   domain,
                                                GSocketType     type,
 -                                              const gchar    *protocol,
 -                                              GError        **error);
 +                                              const gchar    *protocol);
  
  GSocket *        g_socket_new_from_fd        (gint fd);
  
@@@ -90,7 -91,7 +90,7 @@@ void             g_socket_set_reuse_add
  
  gboolean         g_socket_get_reuse_address  (GSocket  *socket);
  
- gboolean         g_socket_has_error          (GSocket  *socket,
+ gboolean         g_socket_has_socket_error   (GSocket  *socket,
                                                GError  **error);
  
  gboolean         g_socket_bind               (GSocket         *socket,
@@@ -113,7 -114,7 +113,7 @@@ gssize           g_socket_receiv
                                                GError       **error);
  
  gssize           g_socket_send               (GSocket       *socket,
-                                               gchar         *buffer,
+                                               const gchar   *buffer,
                                                gsize          size,
                                                GError       **error);
  
diff --combined gnio/gtcpclient.c
@@@ -36,15 -36,19 +36,19 @@@ enu
    PROP_0,
    PROP_ADDRESS,
    PROP_HOSTNAME,
-   PROP_PORT
+   PROP_PORT,
+   PROP_INPUT_STREAM,
+   PROP_OUTPUT_STREAM
  };
  
  struct _GTcpClientPrivate
  {
-   GInetSocketAddress *address;
-   gchar              *hostname;
-   gushort             port;
-   GSocket            *socket;
+   GInetSocketAddress   *address;
+   gchar                *hostname;
+   gushort               port;
+   GSocket              *socket;
+   GNetworkInputStream  *input;
+   GNetworkOutputStream *output;
  };
  
  static void
@@@ -83,6 -87,14 +87,14 @@@ g_tcp_client_get_property (GObject    *
          g_value_set_uint (value, client->priv->port);
          break;
  
+       case PROP_INPUT_STREAM:
+         g_value_set_object (value, g_tcp_client_get_input_stream (client));
+         break;
+       case PROP_OUTPUT_STREAM:
+         g_value_set_object (value, g_tcp_client_get_output_stream (client));
+         break;
        default:
          G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      }
@@@ -175,6 -187,20 +187,20 @@@ g_tcp_client_class_init (GTcpClientClas
                                                        G_MAXUSHORT,
                                                        0,
                                                        G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB | G_PARAM_STATIC_NICK));
+   g_object_class_install_property (gobject_class, PROP_INPUT_STREAM,
+                                    g_param_spec_object ("input-stream",
+                                                         "input stream",
+                                                         "the GNetworkInputStream for reading from this socket",
+                                                         G_TYPE_NETWORK_INPUT_STREAM,
+                                                         G_PARAM_READABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB | G_PARAM_STATIC_NICK));
+   g_object_class_install_property (gobject_class, PROP_OUTPUT_STREAM,
+                                    g_param_spec_object ("output-stream",
+                                                         "output stream",
+                                                         "the GNetworkOutputStream for writing to this socket",
+                                                         G_TYPE_NETWORK_OUTPUT_STREAM,
+                                                         G_PARAM_READABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB | G_PARAM_STATIC_NICK));
  }
  
  static void
@@@ -186,6 -212,8 +212,8 @@@ g_tcp_client_init (GTcpClient *client
    client->priv->hostname = NULL;
    client->priv->port = 0;
    client->priv->socket = NULL;
+   client->priv->input = NULL;
+   client->priv->output = NULL;
  }
  
  GTcpClient *
@@@ -201,6 -229,34 +229,34 @@@ g_tcp_client_new_from_address (GInetSoc
    return G_TCP_CLIENT (g_object_new (G_TYPE_TCP_CLIENT, "address", address, NULL));
  }
  
+ GNetworkInputStream *
+ g_tcp_client_get_input_stream (GTcpClient *client)
+ {
+   if (!client->priv->socket)
+     return NULL;
+   if (client->priv->input)
+     return client->priv->input;
+   // TODO: should we set g_object_notify here, or just create both these streams earlier?
+   return (client->priv->input = _g_network_input_stream_new (client->priv->socket));
+ }
+ GNetworkOutputStream *
+ g_tcp_client_get_output_stream (GTcpClient *client)
+ {
+   if (!client->priv->socket)
+     return NULL;
+   if (client->priv->output)
+     return client->priv->output;
+   // TODO: should we set g_object_notify here, or just create both these streams earlier?
+   return (client->priv->output = _g_network_output_stream_new (client->priv->socket));
+ }
  gboolean
  g_tcp_client_connect (GTcpClient    *client,
                        GCancellable  *cancellable,
      }
    else
      {
-      address = g_inet_socket_address_get_address (client->priv->address);
+       address = g_inet_socket_address_get_address (client->priv->address);
      }
  
    if (G_IS_INET4_ADDRESS (address))
 -    client->priv->socket = g_socket_new (G_SOCKET_DOMAIN_INET, G_SOCKET_TYPE_STREAM, NULL, error);
 +    client->priv->socket = g_socket_new (G_SOCKET_DOMAIN_INET, G_SOCKET_TYPE_STREAM, NULL);
    else if (G_IS_INET6_ADDRESS (address))
 -    client->priv->socket = g_socket_new (G_SOCKET_DOMAIN_INET6, G_SOCKET_TYPE_STREAM, NULL, error);
 +    client->priv->socket = g_socket_new (G_SOCKET_DOMAIN_INET6, G_SOCKET_TYPE_STREAM, NULL);
    else
      {
        g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "unsupported address domain");
        return FALSE;
      }
  
 -  if (!client->priv->socket)
 +  if (g_socket_has_error (client->priv->socket, error))
      return FALSE;
  
    if (g_cancellable_set_error_if_cancelled (cancellable, error))
@@@ -258,8 -314,6 +314,6 @@@ typedef struct 
    GCancellable        *cancellable;
    gpointer             user_data;
    GTcpClient          *client;
-   gchar                address_buffer[256];
-   gsize                address_length;
  } ConnectData;
  
  static gboolean
@@@ -267,16 -321,149 +321,149 @@@ connect_callback (ConnectData *data
                    GIOCondition condition,
                    gint fd)
  {
+   GTcpClient *client;
+   GSimpleAsyncResult *result;
+   GError *error = NULL;
+   client = data->client;
+   if (condition & G_IO_OUT)
+     {
+       result = g_simple_async_result_new (G_OBJECT (client), data->callback, data->user_data, g_tcp_client_connect_async);
+     }
+   else
+     {
+       if (!g_socket_has_socket_error (client->priv->socket, &error))
+         g_warning ("got G_IO_ERR but socket does not have error");
+       result = g_simple_async_result_new_from_error (G_OBJECT (client), data->callback, data->user_data, error);
+     }
+   g_simple_async_result_complete (result);
+   g_object_unref (result);
    return FALSE;
  }
  
+ static void
+ resolve_callback (GObject      *source,
+                   GAsyncResult *result,
+                   gpointer      user_data)
+ {
+   ConnectData *data = (ConnectData *) user_data;
+   GInetAddress *address;
+   GSimpleAsyncResult *error_result;
+   GError *error = NULL;
+   address = g_resolver_resolve_finish (G_RESOLVER (source), result, &error);
+   g_object_unref (G_RESOLVER (source));
+   if (!address)
+     {
+       error_result = g_simple_async_result_new_from_error (G_OBJECT (data->client), data->callback, data->user_data, error);
+       g_simple_async_result_complete (error_result);
+       g_object_unref (error_result);
+     }
+   else
+     {
+       data->client->priv->address = g_inet_socket_address_new (address, data->client->priv->port);
+       g_object_ref_sink (data->client->priv->address);
+       // at this point, the address has been resolved, so connect_async again
+       g_tcp_client_connect_async (data->client, data->cancellable, data->callback, data->user_data);
+     }
+   g_free (data);
+ }
  void
  g_tcp_client_connect_async (GTcpClient          *client,
                              GCancellable        *cancellable,
                              GAsyncReadyCallback  callback,
                              gpointer             user_data)
  {
+   GInetAddress *address;
+   GSimpleAsyncResult *result;
+   GSource *source;
+   ConnectData *data;
+   GError *error = NULL;
+   g_return_if_fail (G_IS_TCP_CLIENT (client));
+   if (!client->priv->address)
+     {
+       // we've been constructed with just hostname+port, resolve
+       GResolver *resolver = g_resolver_new ();
+       data = g_new (ConnectData, 1);
+       data->client = client;
+       data->callback = callback;
+       data->cancellable = cancellable;
+       data->user_data = user_data;
+       g_resolver_resolve_async (resolver, client->priv->hostname, cancellable, resolve_callback, data);
+       return;
+     }
+   address = g_inet_socket_address_get_address (client->priv->address);
  
+   if (G_IS_INET4_ADDRESS (address))
+     client->priv->socket = g_socket_new (G_SOCKET_DOMAIN_INET, G_SOCKET_TYPE_STREAM, NULL, &error);
+   else if (G_IS_INET6_ADDRESS (address))
+     client->priv->socket = g_socket_new (G_SOCKET_DOMAIN_INET6, G_SOCKET_TYPE_STREAM, NULL, &error);
+   else
+     {
+       g_simple_async_report_error_in_idle (G_OBJECT (client), callback, user_data, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "unsupported address domain");
+       return;
+     }
+   if (!client->priv->socket)
+     {
+       g_simple_async_report_gerror_in_idle (G_OBJECT (client), callback, user_data, error);
+       return;
+     }
+   g_socket_set_blocking (client->priv->socket, FALSE);
+   if (!g_socket_connect (client->priv->socket, G_SOCKET_ADDRESS (client->priv->address), &error))
+     {
+       if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_PENDING))
+         {
+           // the connection is in progress
+           source = g_socket_create_source (client->priv->socket, G_IO_OUT | G_IO_ERR | G_IO_HUP, cancellable);
+           data = g_new (ConnectData, 1);
+           data->client = client;
+           data->callback = callback;
+           data->cancellable = cancellable;
+           data->user_data = user_data;
+           g_source_set_callback (source, (GSourceFunc) connect_callback, data, g_free);
+           g_source_attach (source, NULL);
+         }
+       else
+         {
+           g_simple_async_report_gerror_in_idle (G_OBJECT (client), callback, user_data, error);
+         }
+     }
+   else
+     {
+       // the connection is already completed
+       result = g_simple_async_result_new (G_OBJECT (client), callback, user_data, g_tcp_client_connect_async);
+       g_simple_async_result_complete_in_idle (result);
+       g_object_unref (result);
+     }
  }
  
  gboolean
diff --combined test/test-server.c
@@@ -60,7 -60,7 +60,7 @@@ int main (int argc, char *argv[]
  
        loop = g_main_loop_new (NULL, FALSE);
  
 -      socket = g_socket_new (G_SOCKET_DOMAIN_INET, G_SOCKET_TYPE_STREAM, NULL, NULL);
 +      socket = g_socket_new (G_SOCKET_DOMAIN_INET, G_SOCKET_TYPE_STREAM, NULL);
  
        g_socket_set_reuse_address (socket, TRUE);
  
                if (size == 0)
                        break;
  
-               g_print ("received %d bytes of data: %s\n", size, buffer);
+               g_print ("received %" G_GSSIZE_FORMAT " bytes of data: %s\n", size, buffer);
  
                if ((size = g_socket_send (new_socket, buffer, size, &error)) < 0) {
                        g_error (error->message);