/*
* soup-address.c: Internet address handing
*
- * Copyright (C) 2000-2003, Ximian, Inc.
+ * Copyright (C) 2010 Red Hat, Inc.
*/
#ifdef HAVE_CONFIG_H
#include "soup-marshal.h"
#include "soup-misc.h"
-#ifndef INET_ADDRSTRLEN
-# define INET_ADDRSTRLEN 16
-# define INET6_ADDRSTRLEN 46
-#endif
-
-#ifndef INADDR_NONE
-#define INADDR_NONE -1
-#endif
-
/**
* SECTION:soup-address
* @short_description: DNS support
};
typedef struct {
- struct sockaddr *sockaddr;
+ struct sockaddr_storage *sockaddr;
+ int n_addrs, offset;
char *name, *physical;
guint port;
#define SOUP_SIN6(priv) ((struct sockaddr_in6 *)priv->sockaddr)
/* sockaddr family macros */
-#define SOUP_ADDRESS_GET_FAMILY(priv) (priv->sockaddr->sa_family)
+#define SOUP_ADDRESS_GET_FAMILY(priv) (priv->sockaddr->ss_family)
#define SOUP_ADDRESS_SET_FAMILY(priv, family) \
- (priv->sockaddr->sa_family = family)
+ (priv->sockaddr->ss_family = family)
#define SOUP_ADDRESS_FAMILY_IS_VALID(family) \
(family == AF_INET || family == AF_INET6)
#define SOUP_ADDRESS_FAMILY_SOCKADDR_SIZE(family) \
/* sockaddr port macros */
#define SOUP_ADDRESS_PORT_IS_VALID(port) (port >= 0 && port <= 65535)
#define SOUP_ADDRESS_GET_PORT(priv) \
- (priv->sockaddr->sa_family == AF_INET ? \
+ (priv->sockaddr->ss_family == AF_INET ? \
SOUP_SIN(priv)->sin_port : \
SOUP_SIN6(priv)->sin6_port)
#define SOUP_ADDRESS_SET_PORT(priv, port) \
G_STMT_START { \
- if (priv->sockaddr->sa_family == AF_INET) \
+ if (priv->sockaddr->ss_family == AF_INET) \
SOUP_SIN(priv)->sin_port = port; \
else \
SOUP_SIN6(priv)->sin6_port = port; \
/* sockaddr data macros */
#define SOUP_ADDRESS_GET_DATA(priv) \
- (priv->sockaddr->sa_family == AF_INET ? \
+ (priv->sockaddr->ss_family == AF_INET ? \
(gpointer)&SOUP_SIN(priv)->sin_addr : \
(gpointer)&SOUP_SIN6(priv)->sin6_addr)
#define SOUP_ADDRESS_SET_DATA(priv, data, length) \
static void get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec);
-G_DEFINE_TYPE (SoupAddress, soup_address, G_TYPE_OBJECT)
+static void soup_address_connectable_iface_init (GSocketConnectableIface *connectable_iface);
+static GSocketAddressEnumerator *soup_address_connectable_enumerate (GSocketConnectable *connectable);
+
+G_DEFINE_TYPE_WITH_CODE (SoupAddress, soup_address, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (G_TYPE_SOCKET_CONNECTABLE,
+ soup_address_connectable_iface_init))
static void
soup_address_init (SoupAddress *addr)
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
}
+static void
+soup_address_connectable_iface_init (GSocketConnectableIface *connectable_iface)
+{
+ connectable_iface->enumerate = soup_address_connectable_enumerate;
+}
+
static GObject *
constructor (GType type,
guint n_construct_properties,
priv->sockaddr = g_malloc0 (SOUP_ADDRESS_FAMILY_SOCKADDR_SIZE (family));
SOUP_ADDRESS_SET_FAMILY (priv, family);
SOUP_ADDRESS_SET_PORT (priv, htons (priv->port));
+ priv->n_addrs = 1;
break;
case PROP_PORT:
len = SOUP_ADDRESS_FAMILY_SOCKADDR_SIZE (sa->sa_family);
priv->sockaddr = g_memdup (sa, len);
+ priv->n_addrs = 1;
priv->port = ntohs (SOUP_ADDRESS_GET_PORT (priv));
break;
default:
if (priv->sockaddr && len)
*len = SOUP_ADDRESS_FAMILY_SOCKADDR_SIZE (SOUP_ADDRESS_GET_FAMILY (priv));
- return priv->sockaddr;
+ return (struct sockaddr *)priv->sockaddr;
+}
+
+/**
+ * soup_address_get_gsockaddr:
+ * @addr: a #SoupAddress
+ *
+ * Creates a new #GSocketAddress corresponding to @addr (which is assumed
+ * to only have one socket address associated with it).
+ *
+ * Return value: a new #GSocketAddress
+ */
+GSocketAddress *
+soup_address_get_gsockaddr (SoupAddress *addr)
+{
+ SoupAddressPrivate *priv = SOUP_ADDRESS_GET_PRIVATE (addr);
+
+ return g_socket_address_new_from_native (priv->sockaddr,
+ SOUP_ADDRESS_FAMILY_SOCKADDR_SIZE (SOUP_ADDRESS_GET_FAMILY (priv)));
}
static GInetAddress *
SoupAddressPrivate *priv = SOUP_ADDRESS_GET_PRIVATE (addr);
GInetAddress *gia;
GSocketAddress *gsa;
- gsize len;
+ int i;
if (error) {
if (error->domain == G_IO_ERROR &&
else if (priv->sockaddr)
return SOUP_STATUS_OK;
- gia = addrs->data;
- gsa = g_inet_socket_address_new (gia, priv->port);
-
- len = g_socket_address_get_native_size (gsa);
- priv->sockaddr = g_malloc (len);
- if (!g_socket_address_to_native (gsa, priv->sockaddr, len, NULL)) {
- /* can't happen: We know the address format is supported
- * and the buffer is large enough */
- g_warn_if_reached ();
- }
- g_object_unref (gsa);
+ priv->n_addrs = g_list_length (addrs);
+ priv->sockaddr = g_new (struct sockaddr_storage, priv->n_addrs);
+ for (i = 0; addrs; addrs = addrs->next, i++) {
+ gia = addrs->data;
+ gsa = g_inet_socket_address_new (gia, priv->port);
+
+ if (!g_socket_address_to_native (gsa, &priv->sockaddr[i],
+ sizeof (struct sockaddr_storage),
+ NULL)) {
+ /* can't happen: We know the address format is supported
+ * and the buffer is large enough
+ */
+ g_warn_if_reached ();
+ }
+ g_object_unref (gsa);
+ }
return SOUP_STATUS_OK;
}
SoupAddressPrivate *priv = SOUP_ADDRESS_GET_PRIVATE (addr);
SoupAddressResolveAsyncData *res_data;
GSList *lookups, *l;
+ GSource *current_source;
+ GMainContext *current_context;
lookups = priv->async_lookups;
priv->async_lookups = NULL;
+ /* Awful hack; to make soup_socket_connect_async() with an
+ * non-default async_context work correctly, we need to ensure
+ * that the non-default context (which we're now running in)
+ * is the thread-default when the callbacks are run...
+ */
+ current_source = g_main_current_source ();
+ if (current_source && !g_source_is_destroyed (current_source))
+ current_context = g_source_get_context (current_source);
+ else
+ current_context = NULL;
+ g_main_context_push_thread_default (current_context);
+
for (l = lookups; l; l = l->next) {
res_data = l->data;
}
g_slist_free (lookups);
+ g_main_context_pop_thread_default (current_context);
+
g_object_unref (addr);
}
} else
status = SOUP_STATUS_OK;
- if (error)
- g_error_free (error);
+ /* For the enumerator impl, below */
+ g_object_ref (addr);
+ g_object_set_data (G_OBJECT (addr), "async-resolved-error", error);
complete_resolve_async (addr, status);
+
+ g_object_set_data (G_OBJECT (addr), "async-resolved-error", NULL);
+ g_object_unref (addr);
+ if (error)
+ g_error_free (error);
}
static gboolean
}
resolver = g_resolver_get_default ();
- if (async_context && async_context != g_main_context_default ())
+ if (async_context)
g_main_context_push_thread_default (async_context);
if (priv->name) {
g_object_unref (gia);
}
- if (async_context && async_context != g_main_context_default ())
+ if (async_context)
g_main_context_pop_thread_default (async_context);
g_object_unref (resolver);
}
-/**
- * soup_address_resolve_sync:
- * @addr: a #SoupAddress
- * @cancellable: a #GCancellable object, or %NULL
- *
- * Synchronously resolves the missing half of @addr, as with
- * soup_address_resolve_async().
- *
- * If @cancellable is non-%NULL, it can be used to cancel the
- * resolution. soup_address_resolve_sync() will then return a status
- * of %SOUP_STATUS_CANCELLED.
- *
- * It is safe to call this more than once, even from different
- * threads, but it is not safe to mix calls to
- * soup_address_resolve_sync() with calls to
- * soup_address_resolve_async() on the same address.
- *
- * Return value: %SOUP_STATUS_OK, %SOUP_STATUS_CANT_RESOLVE, or
- * %SOUP_STATUS_CANCELLED.
- **/
-guint
-soup_address_resolve_sync (SoupAddress *addr, GCancellable *cancellable)
+static guint
+resolve_sync_internal (SoupAddress *addr, GCancellable *cancellable, GError **error)
{
- SoupAddressPrivate *priv;
+ SoupAddressPrivate *priv = SOUP_ADDRESS_GET_PRIVATE (addr);
GResolver *resolver;
- GError *error = NULL;
guint status;
-
- g_return_val_if_fail (SOUP_IS_ADDRESS (addr), SOUP_STATUS_MALFORMED);
- priv = SOUP_ADDRESS_GET_PRIVATE (addr);
- g_return_val_if_fail (priv->name || priv->sockaddr, SOUP_STATUS_MALFORMED);
+ GError *my_err = NULL;
resolver = g_resolver_get_default ();
g_mutex_unlock (priv->lock);
addrs = g_resolver_lookup_by_name (resolver, priv->name,
- cancellable, &error);
+ cancellable, &my_err);
g_mutex_lock (priv->lock);
- status = update_addrs (addr, addrs, error);
+ status = update_addrs (addr, addrs, my_err);
g_resolver_free_addresses (addrs);
} else if (!priv->name) {
GInetAddress *gia;
g_mutex_unlock (priv->lock);
gia = soup_address_make_inet_address (addr);
name = g_resolver_lookup_by_address (resolver, gia,
- cancellable, &error);
+ cancellable, &my_err);
g_object_unref (gia);
g_mutex_lock (priv->lock);
- status = update_name (addr, name, error);
+ status = update_name (addr, name, my_err);
g_free (name);
} else
status = SOUP_STATUS_OK;
g_mutex_unlock (priv->lock);
- if (error)
- g_error_free (error);
+ if (my_err)
+ g_propagate_error (error, my_err);
g_object_unref (resolver);
return status;
}
/**
+ * soup_address_resolve_sync:
+ * @addr: a #SoupAddress
+ * @cancellable: a #GCancellable object, or %NULL
+ *
+ * Synchronously resolves the missing half of @addr, as with
+ * soup_address_resolve_async().
+ *
+ * If @cancellable is non-%NULL, it can be used to cancel the
+ * resolution. soup_address_resolve_sync() will then return a status
+ * of %SOUP_STATUS_CANCELLED.
+ *
+ * It is safe to call this more than once, even from different
+ * threads, but it is not safe to mix calls to
+ * soup_address_resolve_sync() with calls to
+ * soup_address_resolve_async() on the same address.
+ *
+ * Return value: %SOUP_STATUS_OK, %SOUP_STATUS_CANT_RESOLVE, or
+ * %SOUP_STATUS_CANCELLED.
+ **/
+guint
+soup_address_resolve_sync (SoupAddress *addr, GCancellable *cancellable)
+{
+ SoupAddressPrivate *priv;
+
+ g_return_val_if_fail (SOUP_IS_ADDRESS (addr), SOUP_STATUS_MALFORMED);
+ priv = SOUP_ADDRESS_GET_PRIVATE (addr);
+ g_return_val_if_fail (priv->name || priv->sockaddr, SOUP_STATUS_MALFORMED);
+
+ return resolve_sync_internal (addr, cancellable, NULL);
+}
+
+/**
* soup_address_is_resolved:
* @addr: a #SoupAddress
*
g_return_val_if_fail (priv->sockaddr != NULL, 0);
memcpy (&hash, SOUP_ADDRESS_GET_DATA (priv),
- MIN (sizeof (hash), SOUP_ADDRESS_FAMILY_DATA_SIZE (priv->sockaddr->sa_family)));
+ MIN (sizeof (hash), SOUP_ADDRESS_FAMILY_DATA_SIZE (priv->sockaddr->ss_family)));
return hash;
}
g_return_val_if_fail (priv1->sockaddr != NULL, FALSE);
g_return_val_if_fail (priv2->sockaddr != NULL, FALSE);
- size = SOUP_ADDRESS_FAMILY_SOCKADDR_SIZE (priv1->sockaddr->sa_family);
- return (priv1->sockaddr->sa_family ==
- priv2->sockaddr->sa_family &&
+ size = SOUP_ADDRESS_FAMILY_SOCKADDR_SIZE (priv1->sockaddr->ss_family);
+ return (priv1->sockaddr->ss_family ==
+ priv2->sockaddr->ss_family &&
!memcmp (priv1->sockaddr, priv2->sockaddr, size));
}
+
+
+#define SOUP_TYPE_ADDRESS_ADDRESS_ENUMERATOR (_soup_address_address_enumerator_get_type ())
+#define SOUP_ADDRESS_ADDRESS_ENUMERATOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_ADDRESS_ADDRESS_ENUMERATOR, SoupAddressAddressEnumerator))
+
+typedef struct {
+ GSocketAddressEnumerator parent_instance;
+
+ SoupAddress *addr;
+ int orig_offset;
+ int n;
+} SoupAddressAddressEnumerator;
+
+typedef struct {
+ GSocketAddressEnumeratorClass parent_class;
+
+} SoupAddressAddressEnumeratorClass;
+
+GType _soup_address_address_enumerator_get_type (void);
+G_DEFINE_TYPE (SoupAddressAddressEnumerator, _soup_address_address_enumerator, G_TYPE_SOCKET_ADDRESS_ENUMERATOR)
+
+static void
+soup_address_address_enumerator_finalize (GObject *object)
+{
+ SoupAddressAddressEnumerator *addr_enum =
+ SOUP_ADDRESS_ADDRESS_ENUMERATOR (object);
+
+ g_object_unref (addr_enum->addr);
+
+ G_OBJECT_CLASS (_soup_address_address_enumerator_parent_class)->finalize (object);
+}
+
+static GSocketAddress *
+next_address (SoupAddressAddressEnumerator *addr_enum)
+{
+ SoupAddressPrivate *priv = SOUP_ADDRESS_GET_PRIVATE (addr_enum->addr);
+ struct sockaddr_storage *ss;
+ int next_addr;
+
+ /* If there are two addresses but the first one is unusable
+ * (eg, it's IPv6 and we can only do IPv4), then we don't want to
+ * try the bad one every time. So we use priv->offset to remember
+ * the offset of the first usable address (ie, the first address
+ * that we weren't called again after returning).
+ */
+ next_addr = (addr_enum->orig_offset + addr_enum->n) % priv->n_addrs;
+ priv->offset = next_addr;
+
+ if (addr_enum->n >= priv->n_addrs)
+ return NULL;
+ addr_enum->n++;
+
+ ss = &priv->sockaddr[next_addr];
+ return g_socket_address_new_from_native (ss, SOUP_ADDRESS_FAMILY_SOCKADDR_SIZE (ss->ss_family));
+}
+
+static GSocketAddress *
+soup_address_address_enumerator_next (GSocketAddressEnumerator *enumerator,
+ GCancellable *cancellable,
+ GError **error)
+{
+ SoupAddressAddressEnumerator *addr_enum =
+ SOUP_ADDRESS_ADDRESS_ENUMERATOR (enumerator);
+ SoupAddressPrivate *priv = SOUP_ADDRESS_GET_PRIVATE (addr_enum->addr);
+
+ if (!priv->sockaddr) {
+ if (resolve_sync_internal (addr_enum->addr, cancellable, error) != SOUP_STATUS_OK)
+ return NULL;
+ }
+
+ return next_address (addr_enum);
+}
+
+static void
+got_addresses (SoupAddress *addr, guint status, gpointer user_data)
+{
+ GSimpleAsyncResult *simple = user_data;
+ GError *error;
+
+ error = g_object_get_data (G_OBJECT (addr), "async-resolved-error");
+ if (error)
+ g_simple_async_result_set_from_error (simple, error);
+
+ g_simple_async_result_complete (simple);
+ g_object_unref (simple);
+}
+
+static void
+soup_address_address_enumerator_next_async (GSocketAddressEnumerator *enumerator,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ SoupAddressAddressEnumerator *addr_enum =
+ SOUP_ADDRESS_ADDRESS_ENUMERATOR (enumerator);
+ SoupAddressPrivate *priv = SOUP_ADDRESS_GET_PRIVATE (addr_enum->addr);
+ GSimpleAsyncResult *simple;
+
+ simple = g_simple_async_result_new (G_OBJECT (enumerator),
+ callback, user_data,
+ soup_address_address_enumerator_next_async);
+
+ if (!priv->sockaddr) {
+ soup_address_resolve_async (addr_enum->addr, NULL, cancellable,
+ got_addresses, simple);
+ } else {
+ g_simple_async_result_complete_in_idle (simple);
+ g_object_unref (simple);
+ }
+}
+
+static GSocketAddress *
+soup_address_address_enumerator_next_finish (GSocketAddressEnumerator *enumerator,
+ GAsyncResult *result,
+ GError **error)
+{
+ SoupAddressAddressEnumerator *addr_enum =
+ SOUP_ADDRESS_ADDRESS_ENUMERATOR (enumerator);
+ GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
+
+ if (g_simple_async_result_propagate_error (simple, error))
+ return NULL;
+ else
+ return next_address (addr_enum);
+}
+
+static void
+_soup_address_address_enumerator_init (SoupAddressAddressEnumerator *enumerator)
+{
+}
+
+static void
+_soup_address_address_enumerator_class_init (SoupAddressAddressEnumeratorClass *addrenum_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (addrenum_class);
+ GSocketAddressEnumeratorClass *enumerator_class =
+ G_SOCKET_ADDRESS_ENUMERATOR_CLASS (addrenum_class);
+
+ enumerator_class->next = soup_address_address_enumerator_next;
+ enumerator_class->next_async = soup_address_address_enumerator_next_async;
+ enumerator_class->next_finish = soup_address_address_enumerator_next_finish;
+ object_class->finalize = soup_address_address_enumerator_finalize;
+}
+
+static GSocketAddressEnumerator *
+soup_address_connectable_enumerate (GSocketConnectable *connectable)
+{
+ SoupAddressAddressEnumerator *addr_enum;
+ SoupAddressPrivate *priv;
+
+ addr_enum = g_object_new (SOUP_TYPE_ADDRESS_ADDRESS_ENUMERATOR, NULL);
+ addr_enum->addr = g_object_ref (connectable);
+
+ priv = SOUP_ADDRESS_GET_PRIVATE (addr_enum->addr);
+ addr_enum->orig_offset = priv->offset;
+
+ return (GSocketAddressEnumerator *)addr_enum;
+}
* soup_socket_get_remote_address()) may be useful to applications.
**/
-static void
-_soup_networking_init (void)
-{
-#ifdef G_OS_WIN32
- WSADATA wsadata;
- if (WSAStartup (MAKEWORD (2, 0), &wsadata) != 0)
- g_error ("Windows Sockets could not be initialized");
-#endif
-}
-
-G_DEFINE_TYPE_WITH_CODE (SoupSocket, soup_socket, G_TYPE_OBJECT,
- _soup_networking_init ();)
+G_DEFINE_TYPE (SoupSocket, soup_socket, G_TYPE_OBJECT)
enum {
READABLE,
int sockfd;
SoupAddress *local_addr, *remote_addr;
GIOChannel *iochannel;
+ GSocketConnection *conn;
guint non_blocking:1;
guint is_server:1;
GSource *watch_src;
GSource *read_src, *write_src;
GSource *read_timeout, *write_timeout;
- GSource *connect_timeout;
GByteArray *read_buf;
GMutex *iolock, *addrlock;
#ifdef G_OS_WIN32
#define SOUP_IS_SOCKET_ERROR(status) ((status) == SOCKET_ERROR)
#define SOUP_IS_INVALID_SOCKET(socket) ((socket) == INVALID_SOCKET)
-#define SOUP_IS_CONNECT_STATUS_INPROGRESS() (WSAGetLastError () == WSAEWOULDBLOCK)
#define SHUT_RDWR SD_BOTH
#else
#define SOUP_IS_SOCKET_ERROR(status) ((status) == -1)
#define SOUP_IS_INVALID_SOCKET(socket) ((socket) < 0)
-#define SOUP_IS_CONNECT_STATUS_INPROGRESS() (errno == EINPROGRESS)
#endif
static void
g_io_channel_unref (priv->iochannel);
priv->iochannel = NULL;
}
+ if (priv->conn) {
+ g_object_unref (priv->conn);
+ priv->conn = NULL;
+ }
priv->sockfd = -1;
if (priv->read_src) {
g_warning ("Disposing socket %p during async op", object);
g_source_destroy (priv->watch_src);
}
- if (priv->connect_timeout) {
- if (priv->clean_dispose)
- g_warning ("Disposing socket %p during connect (2)", object);
- g_source_destroy (priv->connect_timeout);
- }
if (priv->async_context)
g_main_context_unref (priv->async_context);
{
GObjectClass *object_class = G_OBJECT_CLASS (socket_class);
-#ifdef SIGPIPE
- signal (SIGPIPE, SIG_IGN);
-#endif
-
g_type_class_add_private (socket_class, sizeof (SoupSocketPrivate));
/* virtual method override */
priv->iochannel =
g_io_channel_win32_new_socket (priv->sockfd);
#endif
- g_io_channel_set_close_on_unref (priv->iochannel, TRUE);
+ g_io_channel_set_close_on_unref (priv->iochannel, priv->conn == NULL);
g_io_channel_set_encoding (priv->iochannel, NULL, NULL);
g_io_channel_set_buffered (priv->iochannel, FALSE);
}
return sock;
}
-typedef struct {
- SoupSocket *sock;
- GCancellable *cancellable;
- guint cancel_id;
- SoupSocketCallback callback;
- gpointer user_data;
-} SoupSocketAsyncConnectData;
-
-static void
-free_sacd (SoupSocketAsyncConnectData *sacd)
-{
- if (sacd->cancel_id)
- g_signal_handler_disconnect (sacd->cancellable, sacd->cancel_id);
- g_object_unref (sacd->cancellable);
- g_object_unref (sacd->sock);
- g_slice_free (SoupSocketAsyncConnectData, sacd);
-}
-
-static gboolean
-idle_connect_result (gpointer user_data)
+static guint
+socket_connected (SoupSocket *sock, GSocketConnection *conn, GError *error)
{
- SoupSocketAsyncConnectData *sacd = user_data;
- SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sacd->sock);
- guint status;
+ SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
+ GSocket *gsock;
+ g_object_unref (priv->connect_cancel);
priv->connect_cancel = NULL;
- priv->watch_src = NULL;
-
- if (priv->sockfd == -1) {
- if (g_cancellable_is_cancelled (sacd->cancellable))
- status = SOUP_STATUS_CANCELLED;
- else
- status = SOUP_STATUS_CANT_CONNECT;
- } else
- status = SOUP_STATUS_OK;
-
- /* Have to do this before calling the callback... */
- if (sacd->cancel_id) {
- g_signal_handler_disconnect (sacd->cancellable, sacd->cancel_id);
- sacd->cancel_id = 0;
- }
-
- sacd->callback (sacd->sock, status, sacd->user_data);
- free_sacd (sacd);
- return FALSE;
-}
-static gboolean
-connect_watch (GIOChannel* iochannel, GIOCondition condition, gpointer data)
-{
- SoupSocketAsyncConnectData *sacd = data;
- SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sacd->sock);
- int error = 0;
- int len = sizeof (error);
-
- /* Remove the watch now in case we don't return immediately */
- g_source_destroy (priv->watch_src);
- priv->watch_src = NULL;
- if (priv->connect_timeout) {
- g_source_destroy (priv->connect_timeout);
- priv->connect_timeout = NULL;
+ if (error) {
+ if (error->domain == G_RESOLVER_ERROR) {
+ g_error_free (error);
+ return SOUP_STATUS_CANT_RESOLVE;
+ } else {
+ g_error_free (error);
+ return SOUP_STATUS_CANT_CONNECT;
+ }
}
- if ((condition & ~(G_IO_IN | G_IO_OUT)) ||
- (getsockopt (priv->sockfd, SOL_SOCKET, SO_ERROR,
- (void *)&error, (void *)&len) != 0) ||
- error)
- disconnect_internal (priv);
-
- return idle_connect_result (sacd);
-}
-
-static gboolean
-connect_timeout (gpointer data)
-{
- SoupSocketAsyncConnectData *sacd = data;
- SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sacd->sock);
-
- /* Remove the watch now in case we don't return immediately */
- g_source_destroy (priv->watch_src);
- priv->watch_src = NULL;
- g_source_destroy (priv->connect_timeout);
- priv->connect_timeout = NULL;
-
- disconnect_internal (priv);
- return idle_connect_result (sacd);
-}
-
-static void
-got_address (SoupAddress *addr, guint status, gpointer user_data)
-{
- SoupSocketAsyncConnectData *sacd = user_data;
- SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sacd->sock);
-
- priv->connect_cancel = NULL;
-
- if (SOUP_STATUS_IS_SUCCESSFUL (status)) {
- soup_socket_connect_async (sacd->sock, sacd->cancellable,
- sacd->callback, sacd->user_data);
- } else
- sacd->callback (sacd->sock, status, sacd->user_data);
-
- free_sacd (sacd);
-}
-
-static void
-async_cancel (GCancellable *cancellable, gpointer user_data)
-{
- SoupSocketAsyncConnectData *sacd = user_data;
- SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sacd->sock);
-
- if (priv->watch_src)
- g_source_destroy (priv->watch_src);
- disconnect_internal (priv);
- priv->watch_src = soup_add_completion (priv->async_context,
- idle_connect_result, sacd);
-}
-
-static guint
-socket_connect_internal (SoupSocket *sock)
-{
- SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
- struct sockaddr *sa;
- int len, status;
-
- sa = soup_address_get_sockaddr (priv->remote_addr, &len);
- if (!sa)
- return SOUP_STATUS_CANT_RESOLVE;
+ /* We keep the GSocketConnection around because its GSocket
+ * will close the fd when it's destroyed.
+ */
+ priv->conn = conn;
- priv->sockfd = socket (sa->sa_family, SOCK_STREAM, 0);
- if (SOUP_IS_INVALID_SOCKET (priv->sockfd))
- return SOUP_STATUS_CANT_CONNECT;
+ gsock = g_socket_connection_get_socket (conn);
+ priv->sockfd = g_socket_get_fd (gsock);
set_fdflags (priv);
- status = connect (priv->sockfd, sa, len);
-
- if (SOUP_IS_SOCKET_ERROR (status)) {
- if (SOUP_IS_CONNECT_STATUS_INPROGRESS ())
- return SOUP_STATUS_CONTINUE;
-
- disconnect_internal (priv);
- return SOUP_STATUS_CANT_CONNECT;
- } else
- return SOUP_STATUS_OK;
+ return SOUP_STATUS_OK;
}
/**
* The callback function passed to soup_socket_connect_async().
**/
+typedef struct {
+ SoupSocket *sock;
+ SoupSocketCallback callback;
+ gpointer user_data;
+} SoupSocketAsyncConnectData;
+
+static void
+async_connected (GObject *client, GAsyncResult *result, gpointer data)
+{
+ SoupSocketAsyncConnectData *sacd = data;
+ GError *error = NULL;
+ GSocketConnection *conn;
+ guint status;
+
+ conn = g_socket_client_connect_finish (G_SOCKET_CLIENT (client),
+ result, &error);
+ status = socket_connected (sacd->sock, conn, error);
+
+ sacd->callback (sacd->sock, status, sacd->user_data);
+ g_object_unref (sacd->sock);
+ g_slice_free (SoupSocketAsyncConnectData, sacd);
+}
+
/**
* soup_socket_connect_async:
* @sock: a client #SoupSocket (which must not already be connected)
{
SoupSocketPrivate *priv;
SoupSocketAsyncConnectData *sacd;
- guint status;
+ GSocketClient *client;
g_return_if_fail (SOUP_IS_SOCKET (sock));
priv = SOUP_SOCKET_GET_PRIVATE (sock);
sacd = g_slice_new0 (SoupSocketAsyncConnectData);
sacd->sock = g_object_ref (sock);
- sacd->cancellable = cancellable ? g_object_ref (cancellable) : g_cancellable_new ();
sacd->callback = callback;
sacd->user_data = user_data;
- priv->connect_cancel = sacd->cancellable;
-
- if (g_cancellable_is_cancelled (priv->connect_cancel)) {
- priv->watch_src = soup_add_completion (priv->async_context,
- idle_connect_result, sacd);
- return;
- }
+ priv->connect_cancel = cancellable ? g_object_ref (cancellable) : g_cancellable_new ();
- if (!soup_address_get_sockaddr (priv->remote_addr, NULL)) {
- soup_address_resolve_async (priv->remote_addr,
- priv->async_context,
- sacd->cancellable,
- got_address, sacd);
- return;
- }
+ if (priv->async_context)
+ g_main_context_push_thread_default (priv->async_context);
- status = socket_connect_internal (sock);
- if (status == SOUP_STATUS_CONTINUE) {
- /* Wait for connect to succeed or fail */
- priv->watch_src =
- soup_add_io_watch (priv->async_context,
- priv->iochannel,
- G_IO_IN | G_IO_OUT |
- G_IO_PRI | G_IO_ERR |
- G_IO_HUP | G_IO_NVAL,
- connect_watch, sacd);
- if (priv->timeout) {
- priv->connect_timeout =
- soup_add_timeout (priv->async_context,
- priv->timeout * 1000,
- connect_timeout, sacd);
+ client = g_socket_client_new ();
+ if (priv->timeout) {
+ /* FIXME: temporary workaround for not-new-enough glib */
+ if (g_object_class_find_property (G_OBJECT_GET_CLASS (client), "timeout")) {
+ g_object_set (G_OBJECT (client),
+ "timeout", priv->timeout,
+ NULL);
}
- sacd->cancel_id =
- g_signal_connect (sacd->cancellable, "cancelled",
- G_CALLBACK (async_cancel),
- sacd);
- } else {
- priv->watch_src = soup_add_completion (priv->async_context,
- idle_connect_result, sacd);
}
-}
+ g_socket_client_connect_async (client,
+ G_SOCKET_CONNECTABLE (priv->remote_addr),
+ priv->connect_cancel,
+ async_connected, sacd);
+ g_object_unref (client);
-static void
-sync_cancel (GCancellable *cancellable, gpointer sock)
-{
- SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
-
- shutdown (priv->sockfd, SHUT_RDWR);
+ if (priv->async_context)
+ g_main_context_pop_thread_default (priv->async_context);
}
/**
soup_socket_connect_sync (SoupSocket *sock, GCancellable *cancellable)
{
SoupSocketPrivate *priv;
- guint status, cancel_id;
+ GSocketClient *client;
+ GSocketConnection *conn;
+ GError *error = NULL;
g_return_val_if_fail (SOUP_IS_SOCKET (sock), SOUP_STATUS_MALFORMED);
priv = SOUP_SOCKET_GET_PRIVATE (sock);
cancellable = g_cancellable_new ();
priv->connect_cancel = cancellable;
- if (!soup_address_get_sockaddr (priv->remote_addr, NULL)) {
- status = soup_address_resolve_sync (priv->remote_addr,
- cancellable);
- if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
- priv->connect_cancel = NULL;
- g_object_unref (cancellable);
- return status;
+ client = g_socket_client_new ();
+ if (priv->timeout) {
+ /* FIXME: temporary workaround for not-new-enough glib */
+ if (g_object_class_find_property (G_OBJECT_GET_CLASS (client), "timeout")) {
+ g_object_set (G_OBJECT (client),
+ "timeout", priv->timeout,
+ NULL);
}
}
+ conn = g_socket_client_connect (client,
+ G_SOCKET_CONNECTABLE (priv->remote_addr),
+ priv->connect_cancel, &error);
+ g_object_unref (client);
- cancel_id = g_signal_connect (cancellable, "cancelled",
- G_CALLBACK (sync_cancel), sock);
-
- status = socket_connect_internal (sock);
- priv->connect_cancel = NULL;
-
- if (status != SOUP_STATUS_OK &&
- g_cancellable_is_cancelled (cancellable)) {
- status = SOUP_STATUS_CANCELLED;
- disconnect_internal (priv);
- }
- g_signal_handler_disconnect (cancellable, cancel_id);
- g_object_unref (cancellable);
-
- return status;
+ return socket_connected (sock, conn, error);
}
int
int sa_len, sockfd;
if (condition & (G_IO_HUP | G_IO_ERR)) {
- g_source_destroy (priv->watch_src);
priv->watch_src = NULL;
return FALSE;
}
{
SoupSocketPrivate *priv;
- struct sockaddr *sa;
+ struct sockaddr_storage sa;
int sa_len;
+ GSocketAddress *addr;
g_return_val_if_fail (SOUP_IS_SOCKET (sock), FALSE);
priv = SOUP_SOCKET_GET_PRIVATE (sock);
* have to make a new addr by calling getsockname(), which
* will have the right port number.
*/
- sa = soup_address_get_sockaddr (priv->local_addr, &sa_len);
- g_return_val_if_fail (sa != NULL, FALSE);
+ addr = soup_address_get_gsockaddr (priv->local_addr);
+ g_return_val_if_fail (addr != NULL, FALSE);
+
+ sa_len = g_socket_address_get_native_size (addr);
+ g_socket_address_to_native (addr, &sa, sizeof (sa), NULL);
+ g_object_unref (addr);
- priv->sockfd = socket (sa->sa_family, SOCK_STREAM, 0);
+ priv->sockfd = socket (sa.ss_family, SOCK_STREAM, 0);
if (SOUP_IS_INVALID_SOCKET (priv->sockfd))
goto cant_listen;
set_fdflags (priv);
/* Bind */
- if (bind (priv->sockfd, sa, sa_len) != 0)
+ if (bind (priv->sockfd, (struct sockaddr *)&sa, sa_len) != 0)
goto cant_listen;
/* Force local_addr to be re-resolved now */
g_object_unref (priv->local_addr);