Make this more like a struct sockaddr again (like it used to be). In
authorDan Winship <danw@src.gnome.org>
Mon, 18 Aug 2003 15:59:05 +0000 (15:59 +0000)
committerDan Winship <danw@src.gnome.org>
Mon, 18 Aug 2003 15:59:05 +0000 (15:59 +0000)
* libsoup/soup-address.c (SoupAddressPrivate): Make this more like
a struct sockaddr again (like it used to be). In particular, add
back the "port" field. Add a bunch of macros to try (and fail) to
simplify some of the code.
(soup_address_new): Now returns a SoupAddress directly rather than
a random handle, and the caller can just use g_object_unref to
cancel the lookup. Also, the callback now uses a
SoupKnownErrorCode rather than a special-purpose address-lookup
error code.
(soup_address_new_cancel): No longer needed.
(soup_address_new_sync): Removed
(soup_address_new_any): Replaces soup_address_ipv4_any and
soup_address_ipv6_any.
(soup_address_get_name, etc): Gone. Use soup_address_resolve()
now.
(soup_address_get_physical): Renamed from
soup_address_get_canonical_name.
(soup_address_get_sockaddr): Replaces soup_address_make_sockaddr()

* libsoup/soup-socket.c: Update for SoupAddress changes and make
similar changes here.
(soup_socket_new): Just creates a generic SoupSocket now.
(soup_socket_connect): Client setup
(soup_socket_listen): Server setup. Now also sets up an iochannel
listening for connects and emits a "new_connection" signal as they
come in.
(soup_socket_start_ssl): Turns on SSL.
(soup_socket_client_new, soup_socket_server_new): Utility
functions that wrap the above.
(soup_socket_new_cancel, soup_socket_new_sync): Gone
(soup_socket_server_accept, soup_socket_server_try_accept): No
longer needed.
(soup_socket_get_iochannel): No longer adds a ref when returning
the iochannel. Also, we set it to "close_on_unref" so that if a
caller adds a ref to it, the connection will actually remain open
even after the SoupSocket is destroyed.
(soup_socket_get_local_address, soup_socket_get_remote_address):
Let the caller get both of these.

* libsoup/soup-connection.c: Don't keep a private copy of the
socket's iochannel.
(soup_connection_new): Don't need to set socket options here.
SoupSocket does it.
(soup_connection_start_ssl): Just call soup_socket_start_ssl.
(soup_connection_get_iochannel): Just return the socket's
iochannel (and don't ref it)

* libsoup/soup-error.c: add SOUP_ERROR_CANT_RESOLVE and
SOUP_ERROR_CANT_RESOLVE_PROXY

* libsoup/soup-dns.c (soup_ntop): Make the address arg const.
Remove the "FIXME add a CANT_RESOLVE error" and return
SOUP_ERROR_CANT_RESOLVE instead.

* libsoup/soup-server.c: Update for socket/address changes. Don't
poke into SoupSocket's private fields.
(soup_server_run_async): Just connect to the socket's
"new_connection" signal.

* libsoup/soup-context.c (try_create_connection,
soup_context_connect_cb): Update for socket changes. Replace
SOUP_CONNECT_ERROR codes with plain SOUP_ERROR codes.

* libsoup/soup-misc.c (soup_signal_connect_once): Utility function
to connect to a signal handler and connect another function to
clean up the first signal handler after its first invocation.
(Lets us use signals to replace one-off callbacks.)

* libsoup/soup-private.h: Remove SoupSocketPrivate since it is
actually private now.
(struct _SoupServer): Remove accept_tag.

* libsoup/soup-queue.c (soup_queue_read_done_cb, start_request):
Don't unref the iochannel.
(soup_queue_connect_cb): Takes a SoupKnownErrorCode now.

* libsoup/soup-socks.c: Update for socket/address changes

* tests/simple-httpd.c (main):
s/SOUP_SERVER_ANY_PORT/SOUP_ADDRESS_ANY_PORT/
* tests/simple-proxy.c (main): Likewise

* tests/timeserver.c: Update for SoupSocket's "new_connection"
signal, and for SoupAddress changes.

24 files changed:
ChangeLog
libsoup/soup-address.c
libsoup/soup-address.h
libsoup/soup-connection.c
libsoup/soup-context.c
libsoup/soup-context.h
libsoup/soup-dns.c
libsoup/soup-dns.h
libsoup/soup-error.c
libsoup/soup-error.h
libsoup/soup-marshal.list
libsoup/soup-misc.c
libsoup/soup-misc.h
libsoup/soup-private.h
libsoup/soup-queue.c
libsoup/soup-queue.h
libsoup/soup-server.c
libsoup/soup-server.h
libsoup/soup-socket.c
libsoup/soup-socket.h
libsoup/soup-socks.c
tests/simple-httpd.c
tests/simple-proxy.c
tests/timeserver.c

index 34f1250..f120400 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,90 @@
+2003-08-18  Dan Winship  <danw@ximian.com>
+
+       * libsoup/soup-address.c (SoupAddressPrivate): Make this more like
+       a struct sockaddr again (like it used to be). In particular, add
+       back the "port" field. Add a bunch of macros to try (and fail) to
+       simplify some of the code.
+       (soup_address_new): Now returns a SoupAddress directly rather than
+       a random handle, and the caller can just use g_object_unref to
+       cancel the lookup. Also, the callback now uses a
+       SoupKnownErrorCode rather than a special-purpose address-lookup
+       error code.
+       (soup_address_new_cancel): No longer needed.
+       (soup_address_new_sync): Removed
+       (soup_address_new_any): Replaces soup_address_ipv4_any and
+       soup_address_ipv6_any.
+       (soup_address_get_name, etc): Gone. Use soup_address_resolve()
+       now.
+       (soup_address_get_physical): Renamed from
+       soup_address_get_canonical_name.
+       (soup_address_get_sockaddr): Replaces soup_address_make_sockaddr()
+
+       * libsoup/soup-socket.c: Update for SoupAddress changes and make
+       similar changes here.
+       (soup_socket_new): Just creates a generic SoupSocket now.
+       (soup_socket_connect): Client setup
+       (soup_socket_listen): Server setup. Now also sets up an iochannel
+       listening for connects and emits a "new_connection" signal as they
+       come in.
+       (soup_socket_start_ssl): Turns on SSL.
+       (soup_socket_client_new, soup_socket_server_new): Utility
+       functions that wrap the above.
+       (soup_socket_new_cancel, soup_socket_new_sync): Gone
+       (soup_socket_server_accept, soup_socket_server_try_accept): No
+       longer needed.
+       (soup_socket_get_iochannel): No longer adds a ref when returning
+       the iochannel. Also, we set it to "close_on_unref" so that if a
+       caller adds a ref to it, the connection will actually remain open
+       even after the SoupSocket is destroyed.
+       (soup_socket_get_local_address, soup_socket_get_remote_address):
+       Let the caller get both of these.
+
+       * libsoup/soup-connection.c: Don't keep a private copy of the
+       socket's iochannel.
+       (soup_connection_new): Don't need to set socket options here.
+       SoupSocket does it.
+       (soup_connection_start_ssl): Just call soup_socket_start_ssl.
+       (soup_connection_get_iochannel): Just return the socket's
+       iochannel (and don't ref it)
+
+       * libsoup/soup-error.c: add SOUP_ERROR_CANT_RESOLVE and
+       SOUP_ERROR_CANT_RESOLVE_PROXY
+
+       * libsoup/soup-dns.c (soup_ntop): Make the address arg const.
+       Remove the "FIXME add a CANT_RESOLVE error" and return
+       SOUP_ERROR_CANT_RESOLVE instead.
+
+       * libsoup/soup-server.c: Update for socket/address changes. Don't
+       poke into SoupSocket's private fields.
+       (soup_server_run_async): Just connect to the socket's
+       "new_connection" signal.
+
+       * libsoup/soup-context.c (try_create_connection,
+       soup_context_connect_cb): Update for socket changes. Replace
+       SOUP_CONNECT_ERROR codes with plain SOUP_ERROR codes.
+
+       * libsoup/soup-misc.c (soup_signal_connect_once): Utility function
+       to connect to a signal handler and connect another function to
+       clean up the first signal handler after its first invocation.
+       (Lets us use signals to replace one-off callbacks.)
+
+       * libsoup/soup-private.h: Remove SoupSocketPrivate since it is
+       actually private now.
+       (struct _SoupServer): Remove accept_tag.
+
+       * libsoup/soup-queue.c (soup_queue_read_done_cb, start_request):
+       Don't unref the iochannel.
+       (soup_queue_connect_cb): Takes a SoupKnownErrorCode now.
+
+       * libsoup/soup-socks.c: Update for socket/address changes
+
+       * tests/simple-httpd.c (main):
+       s/SOUP_SERVER_ANY_PORT/SOUP_ADDRESS_ANY_PORT/
+       * tests/simple-proxy.c (main): Likewise
+
+       * tests/timeserver.c: Update for SoupSocket's "new_connection"
+       signal, and for SoupAddress changes.
+
 2003-08-14  Dan Winship  <danw@ximian.com>
 
        * libsoup/soup-connection.c: New, split out from soup-context and
index b94e81f..66db876 100644 (file)
@@ -11,7 +11,6 @@
 
 #include <errno.h>
 #include <fcntl.h>
-#include <glib.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/types.h>
 #include "soup-private.h"
 #include "soup-address.h"
 #include "soup-dns.h"
-
-struct SoupAddressPrivate {
-       char *name;
-       int   family;
-       union {
-               struct in_addr  in;
-#ifdef HAVE_IPV6
-               struct in6_addr in6;
-#endif
-       } addr;
-};
+#include "soup-marshal.h"
 
 #include <unistd.h>
 #ifndef socklen_t
@@ -51,6 +40,61 @@ struct SoupAddressPrivate {
 #define INADDR_NONE -1
 #endif
 
+struct SoupAddressPrivate {
+       struct sockaddr *sockaddr;
+
+       char *name, *physical;
+       guint port;
+
+       SoupDNSHandle lookup;
+       guint idle_id;
+};
+
+#define SOUP_ADDRESS_PORT_IS_VALID(port) (port >= 0 && port <= 65535)
+#define SOUP_ADDRESS_FAMILY(addr) (addr->priv->sockaddr->sa_family)
+
+#define SOUP_SIN(addr) ((struct sockaddr_in *)addr->priv->sockaddr)
+
+#ifdef HAVE_IPV6
+
+#  define SOUP_SIN6(addr) ((struct sockaddr_in6 *)addr->priv->sockaddr)
+
+#  define SOUP_ADDRESS_FAMILY_IS_VALID(family) \
+       (family == AF_INET || family == AF_INET6)
+#  define SOUP_ADDRESS_FAMILY_SOCKADDR_SIZE(family) \
+       (family == AF_INET ? sizeof (struct sockaddr_in) : \
+                            sizeof (struct sockaddr_in6))
+#  define SOUP_ADDRESS_FAMILY_DATA_SIZE(family) \
+       (family == AF_INET ? sizeof (struct in_addr) : \
+                            sizeof (struct in6_addr))
+
+#  define SOUP_ADDRESS_DATA(addr) \
+       (addr->priv->sockaddr->sa_family == AF_INET ? \
+               (gpointer)&SOUP_SIN(addr)->sin_addr : \
+               (gpointer)&SOUP_SIN6(addr)->sin6_addr)
+#  define SOUP_ADDRESS_PORT(addr) \
+       (addr->priv->sockaddr->sa_family == AF_INET ? \
+               SOUP_SIN(addr)->sin_port : \
+               SOUP_SIN6(addr)->sin6_port)
+
+#else
+
+#  define SOUP_ADDRESS_FAMILY_IS_VALID(family) (family == AF_INET6)
+#  define SOUP_ADDRESS_FAMILY_SOCKADDR_SIZE(family) sizeof (struct sockaddr_in)
+#  define SOUP_ADDRESS_FAMILY_DATA_SIZE(family) sizeof (struct in_addr)
+
+#  define SOUP_ADDRESS_DATA(addr) ((gpointer)&SOUP_SIN(addr)->sin_addr)
+#  define SOUP_ADDRESS_PORT(addr) (SOUP_SIN(addr)->sin_port)
+
+#endif
+
+enum {
+       DNS_RESULT,
+       LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
 #define PARENT_TYPE G_TYPE_OBJECT
 static GObjectClass *parent_class;
 
@@ -67,8 +111,17 @@ finalize (GObject *object)
 {
        SoupAddress *addr = SOUP_ADDRESS (object);
 
+       if (addr->priv->sockaddr)
+               g_free (addr->priv->sockaddr);
        if (addr->priv->name)
                g_free (addr->priv->name);
+       if (addr->priv->physical)
+               g_free (addr->priv->physical);
+
+       if (addr->priv->lookup)
+               soup_gethostby_cancel (addr->priv->lookup);
+       if (addr->priv->idle_id)
+               g_source_remove (addr->priv->idle_id);
 
        g_free (addr->priv);
 
@@ -82,68 +135,50 @@ class_init (GObjectClass *object_class)
 
        /* virtual method override */
        object_class->finalize = finalize;
+
+       /* signals */
+       signals[DNS_RESULT] =
+               g_signal_new ("dns_result",
+                             G_OBJECT_CLASS_TYPE (object_class),
+                             G_SIGNAL_RUN_FIRST,
+                             G_STRUCT_OFFSET (SoupAddressClass, dns_result),
+                             NULL, NULL,
+                             soup_marshal_NONE__INT,
+                             G_TYPE_NONE, 1,
+                             G_TYPE_INT);
 }
 
 SOUP_MAKE_TYPE (soup_address, SoupAddress, class_init, init, PARENT_TYPE)
 
 
-static void
-soup_address_new_sync_cb (SoupAddress       *addr,
-                         SoupAddressStatus  status,
-                         gpointer           user_data)
-{
-       SoupAddress **ret = user_data;
-       *ret = addr;
-}
 
 /**
- * soup_address_new_sync:
- * @name: a hostname, as with soup_address_new()
+ * soup_address_new:
+ * @name: a hostname or physical address
+ * @port: a port number
+ *
+ * Creates a #SoupAddress from @name and @port. The #SoupAddress's IP
+ * address will not be available right away; the caller can listen for
+ * a #dns_result signal to know when @name has been resolved.
  *
- * Return value: a #SoupAddress, or %NULL if the lookup fails.
+ * The DNS lookup can be cancelled by destroying the address object.
+ *
+ * Return value: a #SoupAddress
  **/
 SoupAddress *
-soup_address_new_sync (const char *name)
-{
-       SoupAddress *ret = (SoupAddress *) 0xdeadbeef;
-
-       soup_address_new (name, soup_address_new_sync_cb, &ret);
-
-       while (1) {
-               g_main_iteration (TRUE);
-               if (ret != (SoupAddress *) 0xdeadbeef) return ret;
-       }
-
-       return ret;
-}
-
-static SoupAddress *
-new_address (const char *name, int family, gpointer addr_data)
+soup_address_new (const char *name, guint port)
 {
        SoupAddress *addr;
 
-       addr = g_object_new (SOUP_TYPE_ADDRESS, NULL);
-       if (name)
-               addr->priv->name = g_strdup (name);
-       addr->priv->family = family;
-
-       switch (family) {
-       case AF_INET:
-               memcpy (&addr->priv->addr.in, addr_data,
-                       sizeof (addr->priv->addr.in));
-               break;
+       g_return_val_if_fail (name != NULL, NULL);
+       g_return_val_if_fail (SOUP_ADDRESS_PORT_IS_VALID (port), NULL);
 
-#ifdef HAVE_IPV6
-       case AF_INET6:
-               memcpy (&addr->priv->addr.in6, addr_data,
-                       sizeof (addr->priv->addr.in6));
-               break;
-#endif
+       addr = g_object_new (SOUP_TYPE_ADDRESS, NULL);
+       addr->priv->name = g_strdup (name);
+       addr->priv->port = port;
 
-       default:
-               g_object_unref (addr);
-               addr = NULL;
-       }
+       /* Start a lookup */
+       soup_address_resolve (addr, NULL, NULL);
 
        return addr;
 }
@@ -151,432 +186,222 @@ new_address (const char *name, int family, gpointer addr_data)
 /**
  * soup_address_new_from_sockaddr:
  * @sa: a pointer to a sockaddr
- * @port: pointer to a variable to store @sa's port number in
- *
- * This parses @sa and returns its address as a #SoupAddress
- * and its port in @port. @sa can point to a #sockaddr_in or
- * (if soup was compiled with IPv6 support) a #sockaddr_in6.
- *
- * Return value: a #SoupAddress, or %NULL if the lookup fails.
- **/
-SoupAddress *
-soup_address_new_from_sockaddr (struct sockaddr *sa, guint *port)
-{
-       switch (sa->sa_family) {
-       case AF_INET:
-       {
-               struct sockaddr_in *sa_in = (struct sockaddr_in *)sa;
-
-               if (port)
-                       *port = g_ntohs (sa_in->sin_port);
-               return new_address (NULL, AF_INET, &sa_in->sin_addr);
-       }
-
-#ifdef HAVE_IPV6
-       case AF_INET6:
-       {
-               struct sockaddr_in6 *sa_in6 = (struct sockaddr_in6 *)sa;
-
-               if (port)
-                       *port = g_ntohs (sa_in6->sin6_port);
-               return new_address (NULL, AF_INET6, &sa_in6->sin6_addr);
-       }
-#endif
-
-       default:
-               return NULL;
-       }
-}
-
-/**
- * soup_address_ipv4_any:
+ * @len: size of @sa
  *
- * Return value: a #SoupAddress corresponding to %INADDR_ANY, suitable
- * for passing to soup_socket_server_new().
+ * Return value: a #SoupAddress equivalent to @sa (or %NULL if @sa's
+ * address family isn't supported)
  **/
 SoupAddress *
-soup_address_ipv4_any (void)
+soup_address_new_from_sockaddr (struct sockaddr *sa, int len)
 {
-       static SoupAddress *ipv4_any = NULL;
-
-       if (!ipv4_any) {
-               struct sockaddr_in sa_in;
+       SoupAddress *addr;
 
-               sa_in.sin_family = AF_INET;
-               sa_in.sin_addr.s_addr = INADDR_ANY;
-               ipv4_any = soup_address_new_from_sockaddr ((struct sockaddr *)&sa_in, NULL);
-       }
+       g_return_val_if_fail (sa != NULL, NULL);
+       g_return_val_if_fail (SOUP_ADDRESS_FAMILY_IS_VALID (sa->sa_family), NULL);
+       g_return_val_if_fail (len == SOUP_ADDRESS_FAMILY_SOCKADDR_SIZE (sa->sa_family), NULL);
 
-       g_object_ref (ipv4_any);
-       return ipv4_any;
+       addr = g_object_new (SOUP_TYPE_ADDRESS, NULL);
+       addr->priv->sockaddr = g_memdup (sa, len);
+       addr->priv->port = ntohs (SOUP_ADDRESS_PORT (addr));
+       return addr;
 }
 
 /**
- * soup_address_ipv6_any:
+ * soup_address_new_any:
+ * @family: the address family
+ * @port: the port number (usually 0, meaning "any port")
  *
- * Return value: If soup was compiled without IPv6 support, %NULL.
- * Otherwise, a #SoupAddress corresponding to the IPv6 address "::",
- * suitable for passing to soup_socket_server_new().
+ * Return value: a #SoupAddress corresponding to the "any" address
+ * for @family (or %NULL if @family isn't supported), suitable for
+ * passing to soup_socket_server_new().
  **/
 SoupAddress *
-soup_address_ipv6_any (void)
+soup_address_new_any (SoupAddressFamily family, guint port)
 {
-       static SoupAddress *ipv6_any = NULL;
+       SoupAddress *addr;
 
-#ifdef HAVE_IPV6
-       if (!ipv6_any) {
-               struct sockaddr_in6 sa_in6;
+       g_return_val_if_fail (SOUP_ADDRESS_FAMILY_IS_VALID (family), NULL);
+       g_return_val_if_fail (SOUP_ADDRESS_PORT_IS_VALID (port), NULL);
 
-               sa_in6.sin6_family = AF_INET6;
-               sa_in6.sin6_addr = in6addr_any;
-               ipv6_any = soup_address_new_from_sockaddr ((struct sockaddr *)&sa_in6, NULL);
-       }
+       addr = g_object_new (SOUP_TYPE_ADDRESS, NULL);
+       addr->priv->port = port;
 
-       g_object_ref (ipv6_any);
-#endif
-       return ipv6_any;
-}
+       addr->priv->sockaddr =
+               g_malloc0 (SOUP_ADDRESS_FAMILY_SOCKADDR_SIZE (family));
+       SOUP_ADDRESS_FAMILY (addr) = family;
+       SOUP_ADDRESS_PORT (addr) = htons (port);
 
-static void
-soup_address_get_name_sync_cb (SoupAddress       *addr,
-                              SoupAddressStatus  status,
-                              const char        *name,
-                              gpointer           user_data)
-{
-       const char **ret = user_data;
-       *ret = name;
+       return addr;
 }
 
 /**
- * soup_address_get_name_sync:
+ * soup_address_get_name:
  * @addr: a #SoupAddress
  *
- * Return value: the hostname associated with @addr, as with
- * soup_address_get_name().
+ * Return value: the hostname associated with @addr, or %NULL if
+ * it is not known.
  **/
 const char *
-soup_address_get_name_sync (SoupAddress *addr)
+soup_address_get_name (SoupAddress *addr)
 {
-       const char *ret = (const char *) 0xdeadbeef;
-
-       soup_address_get_name (addr, soup_address_get_name_sync_cb, &ret);
-
-       while (1) {
-               g_main_iteration (TRUE);
-               if (ret != (const char *) 0xdeadbeef) return ret;
-       }
+       g_return_val_if_fail (SOUP_IS_ADDRESS (addr), NULL);
 
-       return ret;
+       return addr->priv->name;
 }
 
 /**
- * soup_address_get_canonical_name:
- * @addr: Address to get the canonical name of.
+ * soup_address_get_sockaddr:
+ * @addr: a #SoupAddress
+ * @len: return location for sockaddr length
  *
- * Get the "canonical" name of an address (eg, for IP4 the dotted
- * decimal name 141.213.8.59).
+ * Returns the sockaddr associated with @addr, with its length in
+ * *@len. If the sockaddr is not yet know, returns %NULL.
  *
- * Returns: %NULL if there was an error.  The caller is responsible
- * for deleting the returned string.
+ * Return value: the sockaddr, or %NULL
  **/
-char *
-soup_address_get_canonical_name (SoupAddress *addr)
+struct sockaddr *
+soup_address_get_sockaddr (SoupAddress *addr, int *len)
 {
-       switch (addr->priv->family) {
-       case AF_INET:
-       {
-#ifdef HAVE_INET_NTOP
-               char buffer[INET_ADDRSTRLEN];
-
-               inet_ntop (addr->priv->family, &addr->priv->addr.in,
-                          buffer, sizeof (buffer));
-               return g_strdup (buffer);
-#else
-               return g_strdup (inet_ntoa (addr->priv->addr.in));
-#endif
-       }
-
-#ifdef HAVE_IPV6
-       case AF_INET6:
-       {
-               char buffer[INET6_ADDRSTRLEN];
-
-               inet_ntop (addr->priv->family, &addr->priv->addr.in6,
-                          buffer, sizeof (buffer));
-               return g_strdup (buffer);
-       }
-#endif
-
-       default:
-               return NULL;
-       }
-}
+       g_return_val_if_fail (SOUP_IS_ADDRESS (addr), NULL);
 
-/**
- * soup_address_make_sockaddr:
- * @addr: The %SoupAddress.
- * @port: The port number
- * @sa: Pointer to struct sockaddr * to output the sockaddr into
- * @len: Pointer to int to return the size of the sockaddr into
- *
- * This creates an appropriate struct sockaddr for @addr and @port
- * and outputs it into *@sa. The caller must free *@sa with g_free().
- **/
-void
-soup_address_make_sockaddr (SoupAddress *addr, guint port,
-                           struct sockaddr **sa, int *len)
-{
-       switch (addr->priv->family) {
-       case AF_INET:
-       {
-               struct sockaddr_in sa_in;
-
-               memset (&sa_in, 0, sizeof (sa_in));
-               sa_in.sin_family = AF_INET;
-               memcpy (&sa_in.sin_addr, &addr->priv->addr.in,
-                       sizeof (sa_in.sin_addr));
-               sa_in.sin_port = g_htons (port);
-
-               *sa = g_memdup (&sa_in, sizeof (sa_in));
-               *len = sizeof (sa_in);
-               break;
-       }
+       if (addr->priv->sockaddr && len)
+               *len = SOUP_ADDRESS_FAMILY_SOCKADDR_SIZE (SOUP_ADDRESS_FAMILY (addr));
 
-#ifdef HAVE_IPV6
-       case AF_INET6:
-       {
-               struct sockaddr_in6 sa_in6;
-
-               memset (&sa_in6, 0, sizeof (sa_in6));
-               sa_in6.sin6_family = AF_INET6;
-               memcpy (&sa_in6.sin6_addr, &addr->priv->addr.in6,
-                       sizeof (sa_in6.sin6_addr));
-               sa_in6.sin6_port = g_htons (port);
-
-               *sa = g_memdup (&sa_in6, sizeof (sa_in6));
-               *len = sizeof (sa_in6);
-               break;
-       }
-#endif
-       default:
-               *sa = NULL;
-               *len = 0;
-       }
+       return addr->priv->sockaddr;
 }
 
 /**
- * soup_address_hash:
- * @p: Pointer to an #SoupAddress.
+ * soup_address_get_physical:
+ * @addr: a #SoupAddress
  *
- * Hash the address.  This is useful for glib containers.
+ * Returns the physical address associated with @addr as a string.
+ * (Eg, "127.0.0.1"). If the address is not yet known, returns %NULL.
  *
- * Returns: hash value.
+ * Return value: the physical address, or %NULL
  **/
-guint
-soup_address_hash (const gpointer p)
+const char *
+soup_address_get_physical (SoupAddress *addr)
 {
-       const SoupAddress *addr;
-
-       g_return_val_if_fail (p != NULL, 0);
-
-       addr = (const SoupAddress*) p;
+       g_return_val_if_fail (SOUP_IS_ADDRESS (addr), NULL);
 
-       /* This isn't network byte-order transparent... (Not sure how
-        * that works in the v6 case.)
-        */
+       if (!addr->priv->sockaddr)
+               return NULL;
 
-       switch (addr->priv->family) {
-       case AF_INET:
-               return addr->priv->addr.in.s_addr;
-#ifdef HAVE_IPV6
-       case AF_INET6:
-       {
-               guint32 *bytes = (guint32 *)&(addr->priv->addr.in6.s6_addr);
-               return (bytes[0] ^ bytes[1] ^ bytes[2] ^ bytes[3]);
-       }
-#endif
-       default:
-               return 0;
+       if (!addr->priv->physical) {
+               addr->priv->physical = soup_ntop (SOUP_ADDRESS_DATA (addr),
+                                                 SOUP_ADDRESS_FAMILY (addr));
        }
+
+       return addr->priv->physical;
 }
 
 /**
- * soup_address_equal:
- * @p1: Pointer to first #SoupAddress.
- * @p2: Pointer to second #SoupAddress.
- *
- * Compare two #SoupAddress structures.
+ * soup_address_get_port:
+ * @addr: a #SoupAddress
  *
- * Returns: 1 if they are the same; 0 otherwise.
+ * Return value: the port associated with @addr
  **/
-gint
-soup_address_equal (const gpointer p1, const gpointer p2)
+guint
+soup_address_get_port (SoupAddress *addr)
 {
-       const SoupAddress *addr1 = (const SoupAddress*) p1;
-       const SoupAddress *addr2 = (const SoupAddress*) p2;
-
-       g_return_val_if_fail (p1 != NULL && p2 != NULL, TRUE);
+       g_return_val_if_fail (SOUP_IS_ADDRESS (addr), 0);
 
-       /* Note network byte order doesn't matter */
-       return memcmp (&addr1->priv->addr, &addr2->priv->addr,
-                      sizeof (addr1->priv->addr)) == 0;
+       return addr->priv->port;
 }
 
 
-typedef struct {
-       SoupDNSHandle    handle;
-       SoupAddressNewFn func;
-       gpointer         data;
-} SoupAddressLookupState;
-
-typedef struct {
-       SoupAddress          *addr;
-       SoupDNSHandle         handle;
-       SoupAddressGetNameFn  func;
-       gpointer              data;
-} SoupAddressReverseState;
 
 static void
-soup_address_new_cb (SoupDNSHandle handle, SoupKnownErrorCode status,
-                    struct hostent *h, gpointer data)
+got_addr (SoupDNSHandle handle, SoupKnownErrorCode status,
+         struct hostent *h, gpointer data)
 {
-       SoupAddressLookupState *state = (SoupAddressLookupState*) data;
-       SoupAddress *addr = NULL;
-
-       if (status == SOUP_ERROR_OK)
-               addr = new_address (h->h_name, h->h_addrtype, h->h_addr);
+       SoupAddress *addr = data;
+
+       addr->priv->lookup = NULL;
+
+       if (status == SOUP_ERROR_OK) {
+               if (!SOUP_ADDRESS_FAMILY_IS_VALID (h->h_addrtype)) {
+                       status = SOUP_ERROR_CANT_RESOLVE;
+                       goto done;
+               }
+               if (SOUP_ADDRESS_FAMILY_DATA_SIZE (h->h_addrtype) != h->h_length) {
+                       status = SOUP_ERROR_MALFORMED;
+                       goto done;
+               }
+
+               addr->priv->sockaddr = g_malloc0 (SOUP_ADDRESS_FAMILY_SOCKADDR_SIZE (h->h_addrtype));
+               SOUP_ADDRESS_FAMILY (addr) = h->h_addrtype;
+               SOUP_ADDRESS_PORT (addr) = htons (addr->priv->port);
+               memcpy (SOUP_ADDRESS_DATA (addr), h->h_addr, h->h_length);
+       }
 
-       state->func (addr, 
-                    addr ? SOUP_ADDRESS_STATUS_OK : SOUP_ADDRESS_STATUS_ERROR,
-                    state->data);
-       g_free (state);
+ done:
+       g_signal_emit (addr, signals[DNS_RESULT], 0, status);
 }
 
-/**
- * soup_address_new:
- * @name: a nice name (eg, mofo.eecs.umich.edu) or a dotted decimal name
- *   (eg, 141.213.8.59).
- * @func: Callback function.
- * @data: User data passed when callback function is called.
- *
- * Create a SoupAddress from a name asynchronously. Once the structure
- * is created, it will call the callback. It will call the callback if
- * there is a failure.
- *
- * Currently this routine forks and does the lookup, which can cause
- * some problems. In general, this will work ok for most programs most
- * of the time. It will be slow or even fail when using operating
- * systems that copy the entire process when forking.
- *
- * If you need to lookup a lot of addresses, you should call
- * g_main_iteration(FALSE) between calls. This will help prevent an
- * explosion of processes.
- *
- * Returns: ID of the lookup which can be used with
- * soup_address_new_cancel() to cancel it;
- **/
-SoupAddressNewId
-soup_address_new (const char *name, SoupAddressNewFn func, gpointer data)
+static void
+got_name (SoupDNSHandle handle, SoupKnownErrorCode status,
+         struct hostent *h, gpointer data)
 {
-       SoupAddressLookupState *state;
-
-       g_return_val_if_fail (name != NULL, NULL);
-       g_return_val_if_fail (func != NULL, NULL);
-
-       state = g_new0 (SoupAddressLookupState, 1);
-       state->func = func;
-       state->data = data;
-       state->handle = soup_gethostbyname (name, soup_address_new_cb, state);
+       SoupAddress *addr = data;
 
-       return state;
-}
+       addr->priv->lookup = NULL;
 
-/**
- * soup_address_new_cancel:
- * @id: ID of the lookup
- *
- * Cancel an asynchronous SoupAddress creation that was started with
- * soup_address_new(). The lookup's callback will not be called.
- */
-void
-soup_address_new_cancel (SoupAddressNewId id)
-{
-       SoupAddressLookupState *state = id;
+       if (status == SOUP_ERROR_OK)
+               addr->priv->name = g_strdup (h->h_name);
 
-       soup_gethostby_cancel (state->handle);
-       g_free (state);
+       g_signal_emit (addr, signals[DNS_RESULT], 0, status);
 }
 
-static void
-soup_address_get_name_cb (SoupDNSHandle handle, SoupKnownErrorCode status,
-                         struct hostent *h, gpointer data)
+static gboolean
+idle_dns_result (gpointer user_data)
 {
-       SoupAddressReverseState *state = data;
-
-       if (status == SOUP_ERROR_OK && !state->addr->priv->name)
-               state->addr->priv->name = g_strdup (h->h_name);
-
-       state->func (state->addr, 
-                    state->addr->priv->name ? SOUP_ADDRESS_STATUS_OK : SOUP_ADDRESS_STATUS_ERROR,
-                    state->addr->priv->name,
-                    state->data);
+       SoupAddress *addr = user_data;
 
-       g_object_unref (state->addr);
-       g_free (state);
+       addr->priv->idle_id = 0;
+       g_signal_emit (addr, signals[DNS_RESULT], 0, SOUP_ERROR_OK);
+       return FALSE;
 }
 
 /**
- * soup_address_get_name:
- * @addr: Address to get the name of.
- * @func: Callback function.
- * @data: User data passed when callback function is called.
- *
- * Get the nice name of the address (eg, "mofo.eecs.umich.edu").
- * This function will use the callback once it knows the nice name
- * or if there is an error.
- *
- * As with soup_address_new(), this forks to do the lookup.
+ * soup_address_resolve:
+ * @addr: a #SoupAddress
+ * @callback: callback to call with the result
+ * @user_data: data for @callback
  *
- * Returns: ID of the lookup which can be used with
- * soup_address_get_name_cancel() to cancel it;
+ * Asynchronously resolves the missing half of @addr. (It's IP address
+ * if it was created with soup_address_new(), or it's hostname if it
+ * was created with soup_address_new_from_sockaddr() or
+ * soup_address_new_any().) @callback will be called when the
+ * resolution finishes (successfully or not).
  **/
-SoupAddressGetNameId
-soup_address_get_name (SoupAddress          *addr,
-                      SoupAddressGetNameFn  func,
-                      gpointer              data)
+void
+soup_address_resolve (SoupAddress *addr,
+                     SoupAddressCallback callback, gpointer user_data)
 {
-       SoupAddressReverseState *state;
+       g_return_if_fail (SOUP_IS_ADDRESS (addr));
 
-       g_return_val_if_fail (SOUP_IS_ADDRESS (addr), NULL);
-       g_return_val_if_fail (func != NULL, NULL);
-
-       state = g_new0 (SoupAddressReverseState, 1);
-       state->addr = g_object_ref (addr);
-       state->func = func;
-       state->data = data;
-       state->handle = soup_gethostbyaddr (&addr->priv->addr,
-                                           addr->priv->family,
-                                           soup_address_get_name_cb,
-                                           state);
-
-       return state;
-}
+       if (callback) {
+               soup_signal_connect_once (addr, "dns_result",
+                                         G_CALLBACK (callback), user_data);
+       }
 
-/**
- * soup_address_get_name_cancel:
- * @id: ID of the lookup
- *
- * Cancel an asynchronous nice name lookup that was started with
- * soup_address_get_name().
- */
-void
-soup_address_get_name_cancel (SoupAddressGetNameId id)
-{
-       SoupAddressReverseState *state = id;
+       if (addr->priv->lookup)
+               return;
 
-       g_return_if_fail (state != NULL);
+       if (addr->priv->name && addr->priv->sockaddr) {
+               addr->priv->idle_id = g_idle_add (idle_dns_result, addr);
+               return;
+       }
 
-       soup_gethostby_cancel (state->handle);
-       g_object_unref (state->addr);
-       g_free(state);
+       if (addr->priv->name) {
+               addr->priv->lookup =
+                       soup_gethostbyname (addr->priv->name,
+                                           got_addr, addr);
+       } else {
+               addr->priv->lookup =
+                       soup_gethostbyaddr (SOUP_ADDRESS_DATA (addr),
+                                           SOUP_ADDRESS_FAMILY (addr),
+                                           got_name, addr);
+       }
 }
index 1c2b9f8..5de2b09 100644 (file)
@@ -10,6 +10,8 @@
 #include <sys/types.h>
 #include <sys/socket.h>
 
+#include <libsoup/soup-error.h>
+
 #define SOUP_TYPE_ADDRESS            (soup_address_get_type ())
 #define SOUP_ADDRESS(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_ADDRESS, SoupAddress))
 #define SOUP_ADDRESS_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_ADDRESS, SoupAddressClass))
@@ -28,64 +30,41 @@ typedef struct {
 typedef struct {
        GObjectClass parent_class;
 
+       /* signals */
+       void (*dns_result) (SoupAddress *addr, SoupKnownErrorCode);
 } SoupAddressClass;
 
-GType soup_address_get_type (void);
-
-
-typedef gpointer SoupAddressNewId;
-
 typedef enum {
-       SOUP_ADDRESS_STATUS_OK,
-       SOUP_ADDRESS_STATUS_ERROR
-} SoupAddressStatus;
-
-typedef void (*SoupAddressNewFn) (SoupAddress       *inetaddr, 
-                                 SoupAddressStatus  status, 
-                                 gpointer           user_data);
-
-SoupAddressNewId     soup_address_new                (const char        *name, 
-                                                     SoupAddressNewFn   func, 
-                                                     gpointer           data);
-
-void                 soup_address_new_cancel         (SoupAddressNewId   id);
-
-SoupAddress         *soup_address_new_sync           (const char        *name);
-
-SoupAddress         *soup_address_ipv4_any           (void);
-SoupAddress         *soup_address_ipv6_any           (void);
+       SOUP_ADDRESS_FAMILY_IPV4 = AF_INET,
+#ifdef AF_INET6
+       SOUP_ADDRESS_FAMILY_IPV6 = AF_INET6
+#else
+       SOUP_ADDRESS_FAMILY_IPV6 = -1
+#endif
+} SoupAddressFamily;
 
+#define SOUP_ADDRESS_ANY_PORT 0
 
-typedef gpointer SoupAddressGetNameId;
-
-typedef void (*SoupAddressGetNameFn) (SoupAddress       *inetaddr, 
-                                     SoupAddressStatus  status, 
-                                     const char        *name,
-                                     gpointer           user_data);
-
-SoupAddressGetNameId
-             soup_address_get_name           (SoupAddress          *addr, 
-                                             SoupAddressGetNameFn  func,
-                                             gpointer              data);
-
-void         soup_address_get_name_cancel    (SoupAddressGetNameId  id);
-
-const char  *soup_address_get_name_sync      (SoupAddress          *addr);
-
-char        *soup_address_get_canonical_name (SoupAddress          *addr);
-
-
-SoupAddress *soup_address_new_from_sockaddr  (struct sockaddr      *sa,
-                                             guint                *port);
-
-void         soup_address_make_sockaddr      (SoupAddress          *addr,
-                                             guint                 port,
-                                             struct sockaddr     **sa,
-                                             int                  *len);
-
-guint        soup_address_hash               (const gpointer        p);
+GType soup_address_get_type (void);
 
-gint         soup_address_equal              (const gpointer        p1, 
-                                             const gpointer        p2);
+SoupAddress     *soup_address_new                (const char          *name,
+                                                 guint                port);
+SoupAddress     *soup_address_new_from_sockaddr  (struct sockaddr     *sa,
+                                                 int                  len);
+SoupAddress     *soup_address_new_any            (SoupAddressFamily    family,
+                                                 guint                port);
+
+typedef void   (*SoupAddressCallback)            (SoupAddress         *addr,
+                                                 SoupKnownErrorCode   status,
+                                                 gpointer             data);
+void             soup_address_resolve            (SoupAddress         *addr,
+                                                 SoupAddressCallback  cb,
+                                                 gpointer             data);
+
+const char      *soup_address_get_name           (SoupAddress         *addr);
+struct sockaddr *soup_address_get_sockaddr       (SoupAddress         *addr,
+                                                 int                 *len);
+const char      *soup_address_get_physical       (SoupAddress         *addr);
+guint            soup_address_get_port           (SoupAddress         *addr);
 
 #endif /* SOUP_ADDRESS_H */
index 571eafe..d5f1f19 100644 (file)
@@ -30,7 +30,6 @@
 
 struct SoupConnectionPrivate {
        SoupSocket *socket;
-       GIOChannel *raw_chan, *cooked_chan;
        gboolean    in_use, new;
        time_t      last_used;
        guint       death_tag;
@@ -44,7 +43,8 @@ enum {
        LAST_SIGNAL
 };
 
-guint signals[LAST_SIGNAL] = { 0 };
+static guint signals[LAST_SIGNAL] = { 0 };
+
 static void
 init (GObject *object)
 {
@@ -100,17 +100,10 @@ SoupConnection *
 soup_connection_new (SoupSocket *sock)
 {
        SoupConnection *conn;
-       int yes = 1, flags = 0, fd;
 
        conn = g_object_new (SOUP_TYPE_CONNECTION, NULL);
        conn->priv->socket = g_object_ref (sock);
 
-       conn->priv->raw_chan = soup_socket_get_iochannel (sock);
-       fd = g_io_channel_unix_get_fd (conn->priv->raw_chan);
-       setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof (yes));
-       flags = fcntl (fd, F_GETFL, 0);
-       fcntl (fd, F_SETFL, flags | O_NONBLOCK);
-
        return conn;
 }
 
@@ -126,10 +119,7 @@ soup_connection_start_ssl (SoupConnection *conn)
        g_return_if_fail (SOUP_IS_CONNECTION (conn));
        g_return_if_fail (conn->priv->socket != NULL);
 
-       if (conn->priv->cooked_chan)
-               g_io_channel_unref (conn->priv->cooked_chan);
-       conn->priv->cooked_chan =
-               soup_ssl_get_iochannel (conn->priv->raw_chan);
+       soup_socket_start_ssl (conn->priv->socket);
 }
 
 /**
@@ -149,15 +139,6 @@ soup_connection_disconnect (SoupConnection *conn)
                conn->priv->death_tag = 0;
        }
 
-       if (conn->priv->raw_chan) {
-               g_io_channel_unref (conn->priv->raw_chan);
-               conn->priv->raw_chan = NULL;
-       }
-       if (conn->priv->cooked_chan) {
-               g_io_channel_unref (conn->priv->cooked_chan);
-               conn->priv->cooked_chan = NULL;
-       }
-
        if (conn->priv->socket) {
                g_object_unref (conn->priv->socket);
                conn->priv->socket = NULL;
@@ -180,8 +161,7 @@ soup_connection_is_connected (SoupConnection *conn)
  * Returns a #GIOChannel used for IO operations on the network
  * connection represented by @conn.
  *
- * Return value: a pointer to the #GIOChannel used for IO on @conn,
- * which the caller must unref.
+ * Return value: a pointer to the #GIOChannel used for IO on @conn
  */
 GIOChannel *
 soup_connection_get_iochannel (SoupConnection *conn)
@@ -191,13 +171,7 @@ soup_connection_get_iochannel (SoupConnection *conn)
        if (!conn->priv->socket)
                return NULL;
 
-       if (!conn->priv->cooked_chan) {
-               conn->priv->cooked_chan = conn->priv->raw_chan;
-               g_io_channel_ref (conn->priv->cooked_chan);
-       }
-
-       g_io_channel_ref (conn->priv->cooked_chan);
-       return conn->priv->cooked_chan;
+       return soup_socket_get_iochannel (conn->priv->socket);
 }
 
 
@@ -231,10 +205,8 @@ soup_connection_set_in_use (SoupConnection *conn, gboolean in_use)
 
        conn->priv->in_use = in_use;
        if (!conn->priv->in_use) {
-               if (!conn->priv->cooked_chan)
-                       soup_connection_get_iochannel (conn);
                conn->priv->death_tag = 
-                       g_io_add_watch (conn->priv->cooked_chan,
+                       g_io_add_watch (soup_socket_get_iochannel (conn->priv->socket),
                                        G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
                                        connection_died,
                                        conn);
index 14f4cf9..92bd592 100644 (file)
@@ -286,7 +286,7 @@ struct SoupConnectData {
        gpointer               user_data;
 
        guint                  timeout_tag;
-       gpointer               connect_tag;
+       SoupSocket            *sock;
 };
 
 static void
@@ -329,20 +329,17 @@ prune_least_used_connection (void)
 static gboolean retry_connect_timeout_cb (struct SoupConnectData *data);
 
 static void
-soup_context_connect_cb (SoupSocket              *socket,
-                        SoupSocketConnectStatus  status,
-                        gpointer                 user_data)
+soup_context_connect_cb (SoupSocket         *socket,
+                        SoupKnownErrorCode  status,
+                        gpointer            user_data)
 {
        struct SoupConnectData *data = user_data;
        SoupContext            *ctx = data->ctx;
-       SoupConnection         *new_conn;
+       SoupConnection         *new_conn = NULL;
 
        switch (status) {
-       case SOUP_SOCKET_CONNECT_ERROR_NONE:
+       case SOUP_ERROR_OK:
                new_conn = soup_connection_new (socket);
-               g_object_unref (socket);
-               if (ctx->priv->uri->protocol == SOUP_PROTOCOL_HTTPS)
-                       soup_connection_start_ssl (new_conn);
 
                g_signal_connect (new_conn, "disconnected",
                                  G_CALLBACK (connection_disconnected),
@@ -355,20 +352,13 @@ soup_context_connect_cb (SoupSocket              *socket,
                ctx->priv->server->connections =
                        g_slist_prepend (ctx->priv->server->connections, new_conn);
 
-               (*data->cb) (ctx, 
-                            SOUP_CONNECT_ERROR_NONE, 
-                            new_conn, 
-                            data->user_data);
                break;
-       case SOUP_SOCKET_CONNECT_ERROR_ADDR_RESOLVE:
-               connection_count--;
 
-               (*data->cb) (ctx, 
-                            SOUP_CONNECT_ERROR_ADDR_RESOLVE, 
-                            NULL, 
-                            data->user_data);
+       case SOUP_ERROR_CANT_RESOLVE:
+               connection_count--;
                break;
-       case SOUP_SOCKET_CONNECT_ERROR_NETWORK:
+
+       default:
                connection_count--;
 
                /*
@@ -384,14 +374,12 @@ soup_context_connect_cb (SoupSocket              *socket,
                        return;
                }
 
-               (*data->cb) (ctx, 
-                            SOUP_CONNECT_ERROR_NETWORK, 
-                            NULL, 
-                            data->user_data);
                break;
        }
 
+       (*data->cb) (ctx, status, new_conn, data->user_data);
        g_object_unref (ctx);
+       g_object_unref (data->sock);
        g_free (data);
 }
 
@@ -412,7 +400,7 @@ try_existing_connections (SoupContext           *ctx,
                        soup_connection_set_in_use (conn, TRUE);
 
                        /* Issue success callback */
-                       (*cb) (ctx, SOUP_CONNECT_ERROR_NONE, conn, user_data);
+                       (*cb) (ctx, SOUP_ERROR_OK, conn, user_data);
                        return TRUE;
                }
 
@@ -423,11 +411,10 @@ try_existing_connections (SoupContext           *ctx,
 }
 
 static gboolean
-try_create_connection (struct SoupConnectData **dataptr)
+try_create_connection (struct SoupConnectData *data)
 {
-       struct SoupConnectData *data = *dataptr;
        int conn_limit = soup_get_connection_limit ();
-       gpointer connect_tag;
+       SoupUri *uri;
 
        /* 
         * Check if we are allowed to create a new connection, otherwise wait
@@ -436,26 +423,17 @@ try_create_connection (struct SoupConnectData **dataptr)
        if (conn_limit &&
            connection_count >= conn_limit &&
            !prune_least_used_connection ()) {
-               data->connect_tag = 0;
+               data->sock = NULL;
                return FALSE;
        }
 
        connection_count++;
 
        data->timeout_tag = 0;
-       connect_tag = soup_socket_connect (data->ctx->priv->uri->host,
-                                          data->ctx->priv->uri->port,
-                                          soup_context_connect_cb,
-                                          data);
-       /* 
-        * NOTE: soup_socket_connect can fail immediately and call our
-        * callback which will delete the state.  
-        */
-       if (connect_tag)
-               data->connect_tag = connect_tag;
-       else
-               *dataptr = NULL;
-
+       uri = data->ctx->priv->uri;
+       data->sock = soup_socket_client_new (uri->host, uri->port,
+                                            uri->protocol == SOUP_PROTOCOL_HTTPS,
+                                            soup_context_connect_cb, data);
        return TRUE;
 }
 
@@ -470,7 +448,7 @@ retry_connect_timeout_cb (struct SoupConnectData *data)
                return FALSE;
        }
 
-       return try_create_connection (&data) == FALSE;
+       return try_create_connection (data) == FALSE;
 }
 
 /**
@@ -514,7 +492,7 @@ soup_context_get_connection (SoupContext           *ctx,
 
        data->ctx = g_object_ref (ctx);
 
-       if (!try_create_connection (&data)) {
+       if (!try_create_connection (data)) {
                data->timeout_tag =
                        g_timeout_add (150,
                                       (GSourceFunc) retry_connect_timeout_cb,
@@ -540,9 +518,9 @@ soup_context_cancel_connect (SoupConnectId tag)
 
        if (data->timeout_tag)
                g_source_remove (data->timeout_tag);
-       else if (data->connect_tag) {
+       else if (data->sock) {
                connection_count--;
-               soup_socket_connect_cancel (data->connect_tag);
+               g_object_unref (data->sock);
        }
 
        g_free (data);
index 5521f7a..1a730b1 100644 (file)
@@ -33,15 +33,8 @@ typedef struct {
 GType soup_context_get_type (void);
 
 
-
-typedef enum {
-       SOUP_CONNECT_ERROR_NONE,
-       SOUP_CONNECT_ERROR_ADDR_RESOLVE,
-       SOUP_CONNECT_ERROR_NETWORK
-} SoupConnectErrorCode;
-
 typedef void (*SoupConnectCallbackFn) (SoupContext          *ctx,
-                                      SoupConnectErrorCode  err,
+                                      SoupKnownErrorCode    err,
                                       SoupConnection       *conn, 
                                       gpointer              user_data);
 
index 52464ed..017f36b 100644 (file)
@@ -123,7 +123,7 @@ new_hostent_from_phys (const char *addr)
 }
 
 char *
-soup_ntop (gpointer addr, int family)
+soup_ntop (gconstpointer addr, int family)
 {
        switch (family) {
        case AF_INET:
@@ -422,8 +422,7 @@ soup_gothost (gpointer user_data)
                info = entry->lookups->data;
                entry->lookups = g_slist_remove (entry->lookups, info);
 
-               /* FIXME: add a CANT_RESOLVE error */
-               (*info->func) (info, entry->h ? SOUP_ERROR_OK : SOUP_ERROR_CANT_CONNECT, entry->h, info->data);
+               (*info->func) (info, entry->h ? SOUP_ERROR_OK : SOUP_ERROR_CANT_RESOLVE, entry->h, info->data);
                g_free (info);
        }
 
index 4c5c420..c7ba32e 100644 (file)
@@ -30,7 +30,7 @@ SoupDNSHandle    soup_gethostbyaddr        (gpointer            addr,
 
 void             soup_gethostby_cancel     (SoupDNSHandle       id);
 
-char            *soup_ntop                 (gpointer            addr,
+char            *soup_ntop                 (gconstpointer       addr,
                                            int                 family);
 
 #endif /* SOUP_DNS_H */
index d73ad36..efd8e95 100644 (file)
@@ -20,6 +20,8 @@ struct {
         * SOUP_ERROR_CLASS_TRANSPORT 
         */
        { SOUP_ERROR_CANCELLED,               "Cancelled" },
+       { SOUP_ERROR_CANT_RESOLVE,            "Cannot resolve hostname" },
+       { SOUP_ERROR_CANT_RESOLVE_PROXY,      "Cannot resolve proxy hostname" },
        { SOUP_ERROR_CANT_CONNECT,            "Cannot connect to destination" },
        { SOUP_ERROR_CANT_CONNECT_PROXY,      "Cannot connect to proxy" },
        { SOUP_ERROR_IO,                      "Connection terminated "
index dd19c3c..51803b6 100644 (file)
@@ -35,6 +35,8 @@ typedef enum {
         * Transport Errors
         */
        SOUP_ERROR_CANCELLED                = 1,
+       SOUP_ERROR_CANT_RESOLVE             = 2,
+       SOUP_ERROR_CANT_RESOLVE_PROXY       = 3,
        SOUP_ERROR_CANT_CONNECT             = 2,
        SOUP_ERROR_CANT_CONNECT_PROXY       = 3,
        SOUP_ERROR_IO                       = 4,
index fa33740..1b091eb 100644 (file)
@@ -1 +1,3 @@
 NONE:NONE
+NONE:INT
+NONE:OBJECT
index 025e85a..ee89cb4 100644 (file)
@@ -804,3 +804,61 @@ soup_set_authorize_callback (SoupAuthorizeFn authfn,
        soup_auth_fn = authfn;
        soup_auth_fn_user_data = user_data;
 }
+
+
+typedef struct {
+       gpointer instance;
+       guint    real_id, self_id;
+} SoupSignalOnceData;
+
+static void
+signal_once_destroy (gpointer ssod, GClosure *closure)
+{
+       g_free (ssod);
+}
+
+static void
+signal_once_cb (gpointer user_data, ...)
+{
+       SoupSignalOnceData *ssod = user_data;
+
+       if (g_signal_handler_is_connected (ssod->instance, ssod->real_id))
+               g_signal_handler_disconnect (ssod->instance, ssod->real_id);
+       g_signal_handler_disconnect (ssod->instance, ssod->self_id);
+}
+
+/**
+ * soup_signal_connect_once:
+ * @instance: an object
+ * @detailed_signal: "signal-name" or "signal-name::detail" to connect to
+ * @c_handler: the #GCallback to connect
+ * @data: data to pass to @c_handler calls
+ *
+ * Connects a #GCallback function to a signal as with
+ * g_signal_connect(), but automatically removes the signal handler
+ * after its first invocation.
+ *
+ * Return value: the signal handler id
+ **/
+guint
+soup_signal_connect_once (gpointer instance, const char *detailed_signal,
+                         GCallback c_handler, gpointer data)
+{
+       SoupSignalOnceData *ssod;
+
+       g_return_val_if_fail (G_TYPE_CHECK_INSTANCE (instance), 0);
+       g_return_val_if_fail (detailed_signal != NULL, 0);
+       g_return_val_if_fail (c_handler != NULL, 0);
+
+       ssod = g_new0 (SoupSignalOnceData, 1);
+       ssod->instance = instance;
+       ssod->real_id =
+               g_signal_connect (instance, detailed_signal, c_handler, data);
+       ssod->self_id =
+               g_signal_connect_data (instance, detailed_signal,
+                                      G_CALLBACK (signal_once_cb), ssod,
+                                      signal_once_destroy,
+                                      G_CONNECT_SWAPPED);
+
+       return ssod->real_id;
+}
index cb97b8b..3df12ce 100644 (file)
@@ -92,6 +92,13 @@ int                soup_base64_decode_step     (const guchar   *in,
                                                int            *state, 
                                                guint          *save);
 
+/* Misc utils */
+
+guint              soup_signal_connect_once   (gpointer         instance,
+                                              const char *     signal,
+                                              GCallback        c_handler,
+                                              gpointer         data);
+
 /* Useful debugging routines */
 
 void               soup_debug_print_headers  (SoupMessage *req);
index 52ac68d..ae8b58e 100644 (file)
@@ -55,13 +55,6 @@ typedef struct {
        GHashTable *ntlm_auths;       /* KEY: SoupConnection, VALUE: SoupAuth */
 } SoupHost;
 
-struct SoupSocketPrivate {
-       int             sockfd;
-       SoupAddress    *addr;
-       guint           port;
-       GIOChannel     *iochannel;
-};
-
 #ifdef HAVE_IPV6
 #define soup_sockaddr_max sockaddr_in6
 #else
@@ -75,7 +68,6 @@ struct _SoupServer {
        guint              refcnt;
        GMainLoop         *loop;
 
-       guint              accept_tag;
        SoupSocket        *listen_sock;
 
        GIOChannel        *cgi_read_chan;
index 2386efa..53a6557 100644 (file)
@@ -262,8 +262,6 @@ soup_queue_read_done_cb (const SoupDataBuffer *data,
                                            soup_queue_read_done_cb,
                                            soup_queue_error_cb,
                                            req);
-
-               g_io_channel_unref (channel);
        } 
        else {
                req->status = SOUP_STATUS_FINISHED;
@@ -502,8 +500,6 @@ start_request (SoupContext *ctx, SoupMessage *req)
                                    soup_queue_error_cb,
                                    req);
 
-       g_io_channel_unref (channel);
-
        req->status = SOUP_STATUS_SENDING_REQUEST;
 }
 
@@ -587,7 +583,7 @@ proxy_connect (SoupContext *ctx, SoupMessage *req, SoupConnection *conn)
 
 void
 soup_queue_connect_cb (SoupContext          *ctx,
-                      SoupConnectErrorCode  err,
+                      SoupKnownErrorCode    err,
                       SoupConnection       *conn,
                       gpointer              user_data)
 {
@@ -600,7 +596,7 @@ soup_queue_connect_cb (SoupContext          *ctx,
        req->connection = conn;
 
        switch (err) {
-       case SOUP_CONNECT_ERROR_NONE:
+       case SOUP_ERROR_OK:
                /* 
                 * NOTE: proxy_connect will either set an error or call us 
                 * again after proxy negotiation.
@@ -611,29 +607,19 @@ soup_queue_connect_cb (SoupContext          *ctx,
                start_request (ctx, req);
                break;
 
-       case SOUP_CONNECT_ERROR_ADDR_RESOLVE:
-               if (ctx != req->context)
-                       soup_message_set_error_full (
-                               req, 
-                               SOUP_ERROR_CANT_CONNECT_PROXY,
-                               "Unable to resolve proxy hostname");
-               else 
-                       soup_message_set_error_full (
-                               req, 
-                               SOUP_ERROR_CANT_CONNECT,
-                               "Unable to resolve hostname");
-
+       case SOUP_ERROR_CANT_RESOLVE:
+               if (ctx == req->context)
+                       soup_message_set_error (req, SOUP_ERROR_CANT_RESOLVE);
+               else
+                       soup_message_set_error (req, SOUP_ERROR_CANT_RESOLVE_PROXY);
                soup_message_issue_callback (req);
                break;
 
-       case SOUP_CONNECT_ERROR_NETWORK:
-               if (ctx != req->context)
-                       soup_message_set_error (req, 
-                                               SOUP_ERROR_CANT_CONNECT_PROXY);
+       default:
+               if (ctx == req->context)
+                       soup_message_set_error (req, SOUP_ERROR_CANT_CONNECT);
                else
-                       soup_message_set_error (req, 
-                                               SOUP_ERROR_CANT_CONNECT);
-
+                       soup_message_set_error (req, SOUP_ERROR_CANT_CONNECT_PROXY);
                soup_message_issue_callback (req);
                break;
        }
index acd938a..5368326 100644 (file)
@@ -20,7 +20,7 @@ void         soup_queue_message        (SoupMessage          *req,
                                        gpointer              user_data);
 
 void         soup_queue_connect_cb     (SoupContext          *ctx,
-                                       SoupConnectErrorCode  err,
+                                       SoupKnownErrorCode    err,
                                        SoupConnection       *conn,
                                        gpointer              user_data);
 
index fdd152d..f57a494 100644 (file)
@@ -49,11 +49,12 @@ struct _SoupServerMessage {
 };
 
 static SoupServer *
-new_server (SoupAddress *address, SoupProtocol proto, guint port)
+new_server (SoupAddress *address, SoupProtocol proto)
 {
        SoupServer *serv;
        SoupSocket *sock = NULL;
        GIOChannel *read_chan = NULL, *write_chan = NULL;
+       guint port = 0;
 
        g_return_val_if_fail (address, NULL);
 
@@ -68,11 +69,14 @@ new_server (SoupAddress *address, SoupProtocol proto, guint port)
                        return NULL;
                }
        } else {
-               sock = soup_socket_server_new (address, port);
+               sock = soup_socket_server_new (address,
+                                              proto == SOUP_PROTOCOL_HTTPS,
+                                              NULL, NULL);
                if (!sock)
                        return NULL;
 
-               port = soup_socket_get_port (sock);
+               address = soup_socket_get_local_address (sock);
+               port = soup_address_get_port (address);
        }
 
        serv = g_new0 (SoupServer, 1);
@@ -89,7 +93,7 @@ new_server (SoupAddress *address, SoupProtocol proto, guint port)
 SoupServer *
 soup_server_new (SoupProtocol proto, guint port)
 {
-       return new_server (soup_address_ipv4_any (), proto, port);
+       return new_server (soup_address_new_any (SOUP_ADDRESS_FAMILY_IPV4, port), proto);
 }
 
 SoupServer *
@@ -97,12 +101,12 @@ soup_server_new_with_host (const char *host, SoupProtocol proto, guint port)
 {
        SoupAddress *address;
 
-       address = soup_address_new_sync (host);
+       address = soup_address_new (host, port);
 
        if (!address)
                return NULL;
 
-       return new_server (address, proto, port);
+       return new_server (address, proto);
 }
 
 SoupServer *
@@ -155,9 +159,6 @@ soup_server_unref (SoupServer *serv)
        --serv->refcnt;
 
        if (serv->refcnt == 0) {
-               if (serv->accept_tag)
-                       g_source_remove (serv->accept_tag);
-
                if (serv->listen_sock)
                        g_object_unref (serv->listen_sock);
 
@@ -280,8 +281,6 @@ destroy_message (SoupMessage *msg)
                                        start_another_request,
                                        data);
                }
-
-               g_io_channel_unref (chan);
        }
 
        if (server_msg) {
@@ -441,28 +440,6 @@ read_headers_cgi (SoupMessage *msg,
        return SOUP_TRANSFER_END;
 }
 
-#define SOUP_SOCKADDR_IN(s) (*((struct sockaddr_in*) &s))
-
-static gchar *
-get_server_sockname (gint fd)
-{
-       struct sockaddr name;
-       int namelen;
-       gchar *host = NULL;
-       guchar *p;
-
-       if (getsockname (fd, &name, &namelen) == 0) {
-               p = (guchar*) &(SOUP_SOCKADDR_IN(name).sin_addr);
-               host = g_strdup_printf ("%d.%d.%d.%d",
-                                       p [0],
-                                       p [1],
-                                       p [2],
-                                       p [3]);
-       }
-
-       return host;
-}
-
 static void
 write_header (gchar *key, gchar *value, GString *ret)
 {
@@ -532,7 +509,6 @@ issue_bad_request (SoupMessage *msg)
                                      SOUP_TRANSFER_CONTENT_LENGTH);
 
        channel = soup_socket_get_iochannel (msg->priv->server_sock);
-
        msg->priv->write_tag =
                soup_transfer_write_simple (channel,
                                            header,
@@ -540,9 +516,7 @@ issue_bad_request (SoupMessage *msg)
                                            write_done_cb,
                                            error_cb,
                                            msg);
-
-       g_io_channel_unref (channel);
-} /* issue_bad_request */
+}
 
 static void
 read_headers_cb (const GString        *headers,
@@ -631,9 +605,9 @@ read_headers_cb (const GString        *headers,
                         * No Host header, no AbsoluteUri
                         */
                        SoupSocket *server_sock = msg->priv->server_sock;
-                       gchar *host;
+                       SoupAddress *addr = soup_socket_get_local_address (server_sock);
+                       const char *host = soup_address_get_physical (addr);
 
-                       host = get_server_sockname (server_sock->priv->sockfd);
                        url = 
                                g_strdup_printf (
                                        "%s%s:%d%s",
@@ -908,8 +882,6 @@ read_done_cb (const SoupDataBuffer *data,
                                                    req);
        }
 
-       g_io_channel_unref (channel);
-
        return;
 }
 
@@ -968,43 +940,27 @@ start_another_request (GIOChannel    *serv_chan,
        return FALSE;
 }
 
-static gboolean 
-conn_accept (GIOChannel    *serv_chan,
-            GIOCondition   condition, 
-            gpointer       user_data)
+static void
+new_connection (SoupSocket *listner, SoupSocket *sock, gpointer user_data)
 {
        SoupServer *server = user_data;
        SoupMessage *msg;
-       GIOChannel *chan;
-       SoupSocket *sock;
-
-       sock = soup_socket_server_try_accept (server->listen_sock);
-       if (!sock) return TRUE;
 
        msg = message_new (server);
        if (!msg) {
                g_warning ("Unable to create new incoming message\n");
-               return TRUE;
+               return;
        }
 
-       chan = soup_socket_get_iochannel (sock);
-
-       if (server->proto == SOUP_PROTOCOL_HTTPS)
-               sock->priv->iochannel = soup_ssl_get_server_iochannel (chan);
-
-       msg->priv->server_sock = sock;
+       msg->priv->server_sock = g_object_ref (sock);
        msg->priv->read_tag = 
-               soup_transfer_read (sock->priv->iochannel,
+               soup_transfer_read (soup_socket_get_iochannel (sock),
                                    FALSE,
                                    read_headers_cb,
                                    NULL,
                                    read_done_cb,
                                    error_cb,
                                    msg);
-
-       g_io_channel_unref (chan);
-
-       return TRUE;
 }
 
 typedef struct {
@@ -1109,25 +1065,11 @@ soup_server_run_async (SoupServer *server)
                        read_done_cgi_cb (&buf, msg);
                }
        } else {
-               GIOChannel *chan;
-
                if (!server->listen_sock) 
                        goto START_ERROR;
 
-               /* 
-                * Listen for new connections (if not already)
-                */
-               if (!server->accept_tag) {
-                       chan = soup_socket_get_iochannel (server->listen_sock);
-
-                       server->accept_tag = 
-                               g_io_add_watch (chan,
-                                               G_IO_IN,
-                                               (GIOFunc) conn_accept, 
-                                               server);
-
-                       g_io_channel_unref (chan);
-               }
+               g_signal_connect (server->listen_sock, "new_connection",
+                                 G_CALLBACK (new_connection), server);
        }
 
        soup_server_ref (server);
@@ -1220,27 +1162,20 @@ SoupAddress *
 soup_server_context_get_client_address (SoupServerContext *context)
 {
        SoupSocket *socket;
-       SoupAddress *address;
 
        g_return_val_if_fail (context != NULL, NULL);
 
        socket = context->msg->priv->server_sock;
-       address = soup_socket_get_address (socket);
-
-       return address;
+       return soup_socket_get_remote_address (socket);
 }
 
-gchar *
+const char *
 soup_server_context_get_client_host (SoupServerContext *context)
 {
-       gchar *host;
        SoupAddress *address;
 
        address = soup_server_context_get_client_address (context);
-       host = g_strdup (soup_address_get_canonical_name (address));
-       g_object_unref (address);
-       
-       return host;
+       return soup_address_get_physical (address);
 }
 
 static SoupServerAuthContext *
index 8060786..3ef1938 100644 (file)
@@ -91,7 +91,7 @@ GSList            *soup_server_list_handlers (SoupServer            *serv);
 
 SoupAddress       *soup_server_context_get_client_address (SoupServerContext *context);
 
-gchar             *soup_server_context_get_client_host    (SoupServerContext *context);
+const char        *soup_server_context_get_client_host    (SoupServerContext *context);
 
 /* 
  * Apache/soup-httpd module initializtion
index ec6652a..6c24e8f 100644 (file)
 
 #include <errno.h>
 #include <fcntl.h>
-#include <glib.h>
 #include <string.h>
+#include <unistd.h>
 
 #include "soup-private.h"
 #include "soup-socket.h"
+#include "soup-marshal.h"
+#include "soup-ssl.h"
 
-#include <unistd.h>
-#ifndef socklen_t
-#  define socklen_t size_t
-#endif
+#include <sys/socket.h>
+#include <netinet/tcp.h>
 
 #define PARENT_TYPE G_TYPE_OBJECT
 static GObjectClass *parent_class;
 
+enum {
+       CONNECT_RESULT,
+       NEW_CONNECTION,
+       LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+struct SoupSocketPrivate {
+       int sockfd;
+       SoupAddress *local_addr, *remote_addr;
+       GIOChannel *iochannel;
+
+       guint watch;
+       gboolean server, ssl;
+};
+
 static void
 init (GObject *object)
 {
@@ -42,15 +59,17 @@ finalize (GObject *object)
 {
        SoupSocket *sock = SOUP_SOCKET (object);
 
-       if (sock->priv->sockfd != -1)
-               close (sock->priv->sockfd);
-
-       if (sock->priv->addr)
-               g_object_unref (sock->priv->addr);
+       if (sock->priv->local_addr)
+               g_object_unref (sock->priv->local_addr);
+       if (sock->priv->remote_addr)
+               g_object_unref (sock->priv->remote_addr);
 
        if (sock->priv->iochannel)
                g_io_channel_unref (sock->priv->iochannel);
 
+       if (sock->priv->watch)
+               g_source_remove (sock->priv->watch);
+
        g_free (sock->priv);
 
        G_OBJECT_CLASS (parent_class)->finalize (object);
@@ -63,601 +82,435 @@ class_init (GObjectClass *object_class)
 
        /* virtual method override */
        object_class->finalize = finalize;
-}
-
-SOUP_MAKE_TYPE (soup_socket, SoupSocket, class_init, init, PARENT_TYPE)
 
-
-typedef struct {
-       SoupSocketConnectFn  func;
-       gpointer             data;
-       guint                port;
-
-       gpointer             inetaddr_id;
-       gpointer             tcp_id;
-} SoupSocketConnectState;
-
-static void
-soup_socket_connect_tcp_cb (SoupSocket *socket,
-                           SoupSocketConnectStatus status,
-                           gpointer data)
-{
-       SoupSocketConnectState *state = (SoupSocketConnectState*) data;
-       SoupSocketConnectFn func = state->func;
-       gpointer user_data = state->data;
-
-       if (status == SOUP_SOCKET_NEW_STATUS_OK)
-               (*func) (socket,
-                        SOUP_SOCKET_CONNECT_ERROR_NONE,
-                        user_data);
-       else
-               (*func) (NULL,
-                        SOUP_SOCKET_CONNECT_ERROR_NETWORK,
-                        user_data);
-
-       if (state->tcp_id)
-               g_free (state);
+       /* signals */
+       signals[CONNECT_RESULT] =
+               g_signal_new ("connect_result",
+                             G_OBJECT_CLASS_TYPE (object_class),
+                             G_SIGNAL_RUN_FIRST,
+                             G_STRUCT_OFFSET (SoupSocketClass, connect_result),
+                             NULL, NULL,
+                             soup_marshal_NONE__INT,
+                             G_TYPE_NONE, 1,
+                             G_TYPE_INT);
+       signals[NEW_CONNECTION] =
+               g_signal_new ("new_connection",
+                             G_OBJECT_CLASS_TYPE (object_class),
+                             G_SIGNAL_RUN_FIRST,
+                             G_STRUCT_OFFSET (SoupSocketClass, new_connection),
+                             NULL, NULL,
+                             soup_marshal_NONE__OBJECT,
+                             G_TYPE_NONE, 1,
+                             SOUP_TYPE_SOCKET);
 }
 
-static void
-soup_socket_connect_inetaddr_cb (SoupAddress *inetaddr,
-                                SoupAddressStatus status,
-                                gpointer data)
-{
-       SoupSocketConnectState *state = (SoupSocketConnectState*) data;
-
-       if (status == SOUP_ADDRESS_STATUS_OK) {
-               state->tcp_id = soup_socket_new (inetaddr, state->port,
-                                                soup_socket_connect_tcp_cb,
-                                                state);
-               g_object_unref (inetaddr);
-       } else {
-               SoupSocketConnectFn func = state->func;
-               gpointer user_data = state->data;
-
-               (*func) (NULL, 
-                        SOUP_SOCKET_CONNECT_ERROR_ADDR_RESOLVE, 
-                        user_data);
-       }
+SOUP_MAKE_TYPE (soup_socket, SoupSocket, class_init, init, PARENT_TYPE)
 
-       if (state->inetaddr_id && !state->tcp_id)
-               g_free (state);
-       else
-               state->inetaddr_id = NULL;
-}
 
 /**
- * soup_socket_connect:
- * @hostname: Name of host to connect to
- * @port: Port to connect to
- * @func: Callback function
- * @data: User data passed when callback function is called.
- *
- * A quick and easy non-blocking #SoupSocket constructor.  This
- * connects to the specified address and port and then calls the
- * callback with the data.  Use this function when you're a client
- * connecting to a server and you don't want to block or mess with
- * #SoupAddress.  It may call the callback before the function
- * returns.  It will call the callback if there is a failure.
+ * soup_socket_new:
  *
- * Returns: ID of the connection which can be used with
- * soup_socket_connect_cancel() to cancel it; %NULL if it succeeds or
- * fails immediately.
+ * Return value: a new (disconnected) socket
  **/
-SoupSocketConnectId
-soup_socket_connect (const char         *hostname,
-                    guint               port,
-                    SoupSocketConnectFn func,
-                    gpointer            data)
+SoupSocket *
+soup_socket_new (void)
 {
-       SoupSocketConnectState *state;
-
-       g_return_val_if_fail (hostname != NULL, NULL);
-       g_return_val_if_fail (func != NULL, NULL);
-
-       state = g_new0 (SoupSocketConnectState, 1);
-       state->func = func;
-       state->data = data;
-       state->port = port;
-
-       state->inetaddr_id = soup_address_new (hostname,
-                                              soup_socket_connect_inetaddr_cb,
-                                              state);
-       /* NOTE: soup_address_new could succeed immediately
-        * and call our callback, in which case state->inetaddr_id
-        * will be NULL but state->tcp_id may be set.
-        */
-
-       if (state->tcp_id || state->inetaddr_id)
-               return state;
-       else {
-               g_free (state);
-               return NULL;
-       }
+       return g_object_new (SOUP_TYPE_SOCKET, NULL);
 }
 
-/**
- * soup_socket_connect_cancel:
- * @id: Id of the connection.
- *
- * Cancel an asynchronous connection that was started with
- * soup_socket_connect().
- */
-void
-soup_socket_connect_cancel (SoupSocketConnectId id)
-{
-       SoupSocketConnectState *state = (SoupSocketConnectState*) id;
-
-       g_return_if_fail (state != NULL);
-
-       if (state->inetaddr_id)
-               soup_address_new_cancel (state->inetaddr_id);
-       else if (state->tcp_id)
-               soup_socket_new_cancel (state->tcp_id);
-
-       g_free (state);
-}
+#define SOUP_SOCKET_NONBLOCKING (1<<0)
+#define SOUP_SOCKET_NONBUFFERED (1<<1)
+#define SOUP_SOCKET_REUSEADDR   (1<<2)
 
 static void
-soup_socket_connect_sync_cb (SoupSocket              *socket,
-                            SoupSocketConnectStatus  status,
-                            gpointer                 data)
+soup_set_sockopts (int sockfd, int opts)
 {
-       SoupSocket **ret = data;
-       *ret = socket;
-}
-
-SoupSocket *
-soup_socket_connect_sync (const char *name, guint port)
-{
-       SoupSocket *ret = (SoupSocket *) 0xdeadbeef;
-
-       soup_socket_connect (name, port, soup_socket_connect_sync_cb, &ret);
+       int flags;
 
-       while (1) {
-               g_main_iteration (TRUE);
-               if (ret != (SoupSocket *) 0xdeadbeef) return ret;
+       if (opts & SOUP_SOCKET_NONBLOCKING) {
+               flags = fcntl (sockfd, F_GETFL, 0);
+               if (flags != -1)
+                       fcntl (sockfd, F_SETFL, flags | O_NONBLOCK);
        }
 
-       return ret;
-}
+       if (opts & SOUP_SOCKET_NONBUFFERED) {
+               flags = 1;
+               setsockopt (sockfd, IPPROTO_TCP, TCP_NODELAY,
+                           &flags, sizeof (flags));
+       }
 
-static void
-soup_socket_new_sync_cb (SoupSocket          *socket,
-                        SoupSocketNewStatus  status,
-                        gpointer             data)
-{
-       SoupSocket **ret = data;
-       *ret = socket;
+       if (opts & SOUP_SOCKET_REUSEADDR) {
+               flags = 1;
+               setsockopt (sockfd, SOL_SOCKET, SO_REUSEADDR,
+                           &flags, sizeof (flags));
+       }
 }
 
-SoupSocket *
-soup_socket_new_sync (SoupAddress *addr, guint port)
+static gboolean
+connect_watch (GIOChannel* iochannel, GIOCondition condition, gpointer data)
 {
-       SoupSocket *ret = (SoupSocket *) 0xdeadbeef;
+       SoupSocket *sock = data;
+       int error = 0;
+       int len = sizeof (error);
 
-       soup_socket_new (addr, port, soup_socket_new_sync_cb, &ret);
+       /* Remove the watch now in case we don't return immediately */
+       g_source_remove (sock->priv->watch);
+       sock->priv->watch = 0;
 
-       while (1) {
-               g_main_iteration (TRUE);
-               if (ret != (SoupSocket *) 0xdeadbeef) return ret;
-       }
+       if (condition & ~(G_IO_IN | G_IO_OUT))
+               goto cant_connect;
 
-       return ret;
-}
+       if (getsockopt (sock->priv->sockfd, SOL_SOCKET, SO_ERROR,
+                       &error, &len) != 0)
+               goto cant_connect;
+       if (error)
+               goto cant_connect;
 
-/**
- * soup_socket_get_iochannel:
- * @socket: #SoupSocket to get #GIOChannel from.
- *
- * Get the #GIOChannel for the #SoupSocket.
- *
- * For a client socket, the #GIOChannel represents the data stream.
- * Use it like you would any other #GIOChannel.
- *
- * For a server socket however, the #GIOChannel represents incoming
- * connections.  If you can read from it, there's a connection
- * waiting.
- *
- * There is one channel for every socket.  This function refs the
- * channel before returning it.  You should unref the channel when
- * you are done with it.  However, you should not close the channel -
- * this is done when you delete the socket.
- *
- * Returns: A #GIOChannel; %NULL on failure.
- *
- **/
-GIOChannel*
-soup_socket_get_iochannel (SoupSocket *socket)
-{
-       g_return_val_if_fail (SOUP_IS_SOCKET (socket), NULL);
+       if (sock->priv->ssl)
+               soup_socket_start_ssl (sock);
 
-       if (socket->priv->iochannel == NULL) {
-               socket->priv->iochannel = g_io_channel_unix_new (socket->priv->sockfd);
-               g_io_channel_set_encoding (socket->priv->iochannel, NULL, NULL);
-               g_io_channel_set_buffered (socket->priv->iochannel, FALSE);
-       }
-
-       g_io_channel_ref (socket->priv->iochannel);
+       g_signal_emit (sock, signals[CONNECT_RESULT], 0, SOUP_ERROR_OK);
+       return FALSE;
 
-       return socket->priv->iochannel;
+ cant_connect:
+       g_signal_emit (sock, signals[CONNECT_RESULT], 0, SOUP_ERROR_CANT_CONNECT);
+       return FALSE;
 }
 
-/**
- * soup_socket_get_address:
- * @socket: #SoupSocket to get address of.
- *
- * Get the address of the socket.  If the socket is connected to a
- * remote machine (as a client or server), the address will be the
- * address of that machine. If it is a listening socket, the address
- * will be %NULL.
- *
- * Returns: #SoupAddress of socket; %NULL on failure.
- **/
-SoupAddress *
-soup_socket_get_address (const SoupSocket *socket)
+static gboolean
+idle_connect_result (gpointer user_data)
 {
-       g_return_val_if_fail (SOUP_IS_SOCKET (socket), NULL);
-       g_return_val_if_fail (socket->priv->addr != NULL, NULL);
+       SoupSocket *sock = user_data;
+
+       sock->priv->watch = 0;
 
-       return g_object_ref (socket->priv->addr);
+       g_signal_emit (sock, signals[CONNECT_RESULT], 0,
+                      sock->priv->sockfd != -1 ? SOUP_ERROR_OK : SOUP_ERROR_CANT_CONNECT);
+       return FALSE;
 }
 
-/**
- * soup_socket_get_port:
- * @socket: #SoupSocket to get the port number of.
- *
- * Get the port number the socket is bound to.
- *
- * Returns: Port number of the socket.
- **/
-guint
-soup_socket_get_port (const SoupSocket *socket)
+static void
+got_address (SoupAddress *addr, SoupKnownErrorCode status, gpointer user_data)
 {
-       g_return_val_if_fail (SOUP_IS_SOCKET (socket), 0);
+       SoupSocket *sock = user_data;
+
+       if (!SOUP_ERROR_IS_SUCCESSFUL (status)) {
+               g_signal_emit (sock, signals[CONNECT_RESULT], 0, status);
+               return;
+       }
 
-       return socket->priv->port;
+       soup_socket_connect (sock, addr);
+       /* soup_socket_connect re-reffed addr */
+       g_object_unref (addr);
 }
 
 /**
- * soup_socket_server_new:
- * @local_addr: Local address to bind to. (soup_address_ipv4_any() to
- * accept connections on any local IPv4 address)
- * @local_port: Port number for the socket (%SOUP_SERVER_ANY_PORT if you
- * don't care).
- *
- * Create and open a new #SoupSocket listening on the specified
- * address and port. Use this sort of socket when your are a server
- * and you know what the port number should be (or pass 0 if you don't
- * care what the port is).
+ * soup_socket_connect:
+ * @sock: a #SoupSocket (which must not be connected or listening)
+ * @remote_addr: address to connect to
  *
- * Returns: a new #SoupSocket, or %NULL if there was a failure.
+ * Starts connecting to the indicated remote address and port. The
+ * socket will emit %connect_result when it succeeds or fails (but
+ * not before returning from this function).
  **/
-SoupSocket *
-soup_socket_server_new (SoupAddress *local_addr, guint local_port)
+void
+soup_socket_connect (SoupSocket *sock, SoupAddress *remote_addr)
 {
-       SoupSocket *s;
        struct sockaddr *sa = NULL;
-       struct soup_sockaddr_max bound_sa;
-       int sa_len;
-       const int on = 1;
-       int flags;
-
-       g_return_val_if_fail (local_addr != NULL, NULL);
-
-       /* Create an appropriate sockaddr */
-       soup_address_make_sockaddr (local_addr, local_port, &sa, &sa_len);
-
-       /* Create socket */
-       s = g_object_new (SOUP_TYPE_SOCKET, NULL);
-
-       if ((s->priv->sockfd = socket (sa->sa_family, SOCK_STREAM, 0)) < 0) {
-               g_object_unref (s);
-               g_free (sa);
-               return NULL;
+       int len, status;
+
+       g_return_if_fail (SOUP_IS_SOCKET (sock));
+       g_return_if_fail (SOUP_IS_ADDRESS (remote_addr));
+       g_return_if_fail (sock->priv->sockfd == -1);
+
+       sock->priv->remote_addr = g_object_ref (remote_addr);
+       sa = soup_address_get_sockaddr (sock->priv->remote_addr, &len);
+       if (!sa) {
+               soup_address_resolve (sock->priv->remote_addr,
+                                     got_address, sock);
+               return;
        }
 
-       /* Set REUSEADDR so we can reuse the port */
-       if (setsockopt (s->priv->sockfd,
-                       SOL_SOCKET,
-                       SO_REUSEADDR,
-                       &on,
-                       sizeof (on)) != 0)
-               g_warning("Can't set reuse on tcp socket\n");
-
-       /* Get the flags (should all be 0?) */
-       flags = fcntl (s->priv->sockfd, F_GETFL, 0);
-       if (flags == -1) goto SETUP_ERROR;
-
-       /* Make the socket non-blocking */
-       if (fcntl (s->priv->sockfd, F_SETFL, flags | O_NONBLOCK) == -1)
-               goto SETUP_ERROR;
+       sock->priv->sockfd = socket (sa->sa_family, SOCK_STREAM, 0);
+       if (sock->priv->sockfd < 0)
+               goto cant_connect;
+       soup_set_sockopts (sock->priv->sockfd,
+                          SOUP_SOCKET_NONBLOCKING | SOUP_SOCKET_NONBUFFERED);
 
-       /* Bind */
-       if (bind (s->priv->sockfd, sa, sa_len) != 0)
-               goto SETUP_ERROR;
+       /* Connect (non-blocking) */
+       status = connect (sock->priv->sockfd, sa, len);
        g_free (sa);
+       sa = NULL;
 
-       sa_len = sizeof (bound_sa);
-       getsockname (s->priv->sockfd, (struct sockaddr *)&bound_sa, &sa_len); 
-       s->priv->addr =
-               soup_address_new_from_sockaddr ((struct sockaddr *)&bound_sa,
-                                               &s->priv->port);
-
-       /* Listen */
-       if (listen (s->priv->sockfd, 10) != 0) goto SETUP_ERROR;
-
-       return s;
-
- SETUP_ERROR:
-       g_object_unref (s);
-       g_free (sa);
-       return NULL;
+       if (status == 0) {
+               /* Connect already succeeded */
+               sock->priv->watch = g_idle_add (idle_connect_result, sock);
+               return;
+       }
+       if (errno != EINPROGRESS)
+               goto cant_connect;
+
+       soup_socket_get_iochannel (sock);
+       sock->priv->watch = g_io_add_watch (sock->priv->iochannel,
+                                           (G_IO_IN | G_IO_OUT | G_IO_PRI |
+                                            G_IO_ERR | G_IO_HUP | G_IO_NVAL),
+                                           connect_watch, sock);
+       return;
+
+ cant_connect: 
+       if (sa)
+               g_free (sa);
+       if (sock->priv->sockfd != -1) {
+               close (sock->priv->sockfd);
+               sock->priv->sockfd = -1;
+       }
+       sock->priv->watch = g_idle_add (idle_connect_result, sock);
 }
 
-
-#define SOUP_ANY_IO_CONDITION  (G_IO_IN | G_IO_OUT | G_IO_PRI | \
-                                G_IO_ERR | G_IO_HUP | G_IO_NVAL)
-
-typedef struct {
-       int              sockfd;
-       SoupAddress     *addr;
-       guint            port;
-       SoupSocketNewFn  func;
-       gpointer         data;
-       int              flags;
-       guint            connect_watch;
-} SoupSocketState;
-
 static gboolean
-soup_socket_new_cb (GIOChannel *iochannel,
-                   GIOCondition condition,
-                   gpointer data)
+listen_watch (GIOChannel* iochannel, GIOCondition condition, gpointer data)
 {
-       SoupSocketState *state = (SoupSocketState*) data;
-       SoupSocket *s;
-       int error = 0;
-       int len = sizeof (int);
-
-       /* Remove the watch now in case we don't return immediately */
-       g_source_remove (state->connect_watch);
-
-       if (condition & ~(G_IO_IN | G_IO_OUT)) goto ERROR;
-
-       errno = 0;
-       if (getsockopt (state->sockfd,
-                       SOL_SOCKET,
-                       SO_ERROR,
-                       &error,
-                       &len) != 0) goto ERROR;
-
-       if (error) goto ERROR;
+       SoupSocket *sock = data, *new;
+       struct soup_sockaddr_max sa;
+       int sa_len, sockfd;
 
-       if (fcntl (state->sockfd, F_SETFL, state->flags) != 0)
-               goto ERROR;
+       if (condition & (G_IO_HUP | G_IO_ERR)) {
+               g_source_remove (sock->priv->watch);
+               sock->priv->watch = 0;
+               return FALSE;
+       }
 
-       s = g_object_new (SOUP_TYPE_SOCKET, NULL);
-       s->priv->sockfd = state->sockfd;
-       s->priv->addr = state->addr;
-       s->priv->port = state->port;
+       sa_len = sizeof (sa);
+       sockfd = accept (sock->priv->sockfd, (struct sockaddr *)&sa, &sa_len);
+       if (sockfd == -1)
+               return TRUE;
 
-       (*state->func) (s, SOUP_SOCKET_NEW_STATUS_OK, state->data);
+       soup_set_sockopts (sockfd,
+                          SOUP_SOCKET_NONBLOCKING | SOUP_SOCKET_NONBUFFERED);
 
-       g_free (state);
+       new = soup_socket_new ();
+       new->priv->sockfd = sockfd;
+       new->priv->remote_addr = soup_address_new_from_sockaddr ((struct sockaddr *)&sa, sa_len);
 
-       return FALSE;
+       new->priv->server = TRUE;
+       if (sock->priv->ssl) {
+               new->priv->ssl = TRUE;
+               soup_socket_start_ssl (new);
+       }
 
- ERROR:
-       g_object_unref (state->addr);
-       (*state->func) (NULL, SOUP_SOCKET_NEW_STATUS_ERROR, state->data);
-       g_free (state);
+       g_signal_emit (sock, signals[NEW_CONNECTION], 0, new);
+       g_object_unref (new);
 
-       return FALSE;
+       return TRUE;
 }
 
 /**
- * soup_socket_new:
- * @addr: Address to connect to.
- * @port: Port to connect to
- * @func: Callback function.
- * @data: User data passed when callback function is called.
+ * soup_socket_listen:
+ * @sock: a #SoupSocket (which must not be connected or listening)
+ * @local_addr: Local address to bind to.
  *
- * Connect to a specifed address asynchronously.  When the connection
- * is complete or there is an error, it will call the callback.  It
- * may call the callback before the function returns.  It will call
- * the callback if there is a failure.
+ * Makes @sock start listening on the given interface and port. When
+ * connections come in, @sock will emit %new_connection.
  *
- * Returns: ID of the connection which can be used with
- * soup_socket_connect_cancel() to cancel it; %NULL on
- * failure.
+ * Return value: whether or not @sock is now listening.
  **/
-SoupSocketNewId
-soup_socket_new (SoupAddress      *addr,
-                guint             port,
-                SoupSocketNewFn   func,
-                gpointer          data)
+gboolean
+soup_socket_listen (SoupSocket *sock, SoupAddress *local_addr)
 {
-       int sockfd;
-       int flags;
-       SoupSocketState *state;
-       GIOChannel *chan;
        struct sockaddr *sa;
-       int len;
+       int sa_len;
 
-       g_return_val_if_fail(addr != NULL, NULL);
-       g_return_val_if_fail(func != NULL, NULL);
+       g_return_val_if_fail (SOUP_IS_SOCKET (sock), FALSE);
+       g_return_val_if_fail (SOUP_IS_ADDRESS (local_addr), FALSE);
+       g_return_val_if_fail (sock->priv->sockfd == -1, FALSE);
 
-       /* Create socket */
-       soup_address_make_sockaddr (addr, port, &sa, &len);
-       sockfd = socket (sa->sa_family, SOCK_STREAM, 0);
-       if (sockfd < 0) {
-               (func) (NULL, SOUP_SOCKET_NEW_STATUS_ERROR, data);
-               g_free (sa);
-               return NULL;
-       }
-
-       /* Get the flags (should all be 0?) */
-       flags = fcntl (sockfd, F_GETFL, 0);
-       if (flags == -1) {
-               (func) (NULL, SOUP_SOCKET_NEW_STATUS_ERROR, data);
-               g_free (sa);
-               return NULL;
-       }
+       /* @local_addr may have its port set to 0. So we intentionally
+        * don't store it in sock->priv->local_addr, so that if the
+        * caller calls soup_socket_get_local_address() later, we'll
+        * have to make a new addr by calling getsockname(), which
+        * will have the right port number.
+        */
+       sa = soup_address_get_sockaddr (local_addr, &sa_len);
+       g_return_val_if_fail (sa != NULL, FALSE);
 
-       if (fcntl (sockfd, F_SETFL, flags | O_NONBLOCK) == -1) {
-               (func) (NULL, SOUP_SOCKET_NEW_STATUS_ERROR, data);
-               g_free (sa);
-               return NULL;
-       }
+       sock->priv->sockfd = socket (sa->sa_family, SOCK_STREAM, 0);
+       if (sock->priv->sockfd < 0)
+               goto cant_listen;
+       soup_set_sockopts (sock->priv->sockfd,
+                          SOUP_SOCKET_NONBLOCKING | SOUP_SOCKET_REUSEADDR);
 
-       errno = 0;
+       /* Bind */
+       if (bind (sock->priv->sockfd, sa, sa_len) != 0)
+               goto cant_listen;
 
-       /* Connect (but non-blocking!) */
-       if (connect (sockfd, sa, len) < 0 && errno != EINPROGRESS) {
-               (func) (NULL, SOUP_SOCKET_NEW_STATUS_ERROR, data);
-               g_free (sa);
-               return NULL;
-       }
-       g_free (sa);
+       /* Listen */
+       if (listen (sock->priv->sockfd, 10) != 0)
+               goto cant_listen;
 
-       /* Unref in soup_socket_new_cb if failure */
-       g_object_ref (addr);
+       sock->priv->server = TRUE;
 
-       /* Connect succeeded, return immediately */
-       if (!errno) {
-               SoupSocket *s = g_object_new (SOUP_TYPE_SOCKET, NULL);
-               s->priv->sockfd = sockfd;
-               s->priv->addr = addr;
+       soup_socket_get_iochannel (sock);
+       sock->priv->watch = g_io_add_watch (sock->priv->iochannel,
+                                           G_IO_IN | G_IO_ERR | G_IO_HUP,
+                                           listen_watch, sock);
+       return TRUE;
 
-               (*func) (s, SOUP_SOCKET_NEW_STATUS_OK, data);
-               return NULL;
+ cant_listen:
+       if (sock->priv->sockfd != -1) {
+               close (sock->priv->sockfd);
+               sock->priv->sockfd = -1;
        }
-
-       chan = g_io_channel_unix_new (sockfd);
-
-       /* Wait for the connection */
-       state = g_new0 (SoupSocketState, 1);
-       state->sockfd = sockfd;
-       state->addr = addr;
-       state->port = port;
-       state->func = func;
-       state->data = data;
-       state->flags = flags;
-       state->connect_watch = g_io_add_watch (chan,
-                                              SOUP_ANY_IO_CONDITION,
-                                              soup_socket_new_cb,
-                                              state);
-
-       g_io_channel_unref (chan);
-
-       return state;
+       if (sa)
+               g_free (sa);
+       return FALSE;
 }
 
 /**
- * soup_socket_new_cancel:
- * @id: ID of the connection.
+ * soup_socket_start_ssl:
+ * @socket: the socket
  *
- * Cancel an asynchronous connection that was started with
- * soup_socket_new().
+ * Starts using SSL on @socket.
  **/
 void
-soup_socket_new_cancel (SoupSocketNewId id)
+soup_socket_start_ssl (SoupSocket *sock)
 {
-       SoupSocketState *state = (SoupSocketState*) id;
+       GIOChannel *chan;
 
-       g_source_remove (state->connect_watch);
-       g_object_unref (state->addr);
-       g_free (state);
+       chan = soup_socket_get_iochannel (sock);
+       sock->priv->iochannel = sock->priv->server ?
+               soup_ssl_get_server_iochannel (chan) :
+               soup_ssl_get_iochannel (chan);
+       sock->priv->ssl = TRUE;
 }
+       
 
-static SoupSocket *
-server_accept_internal (SoupSocket *socket, gboolean block)
+/**
+ * soup_socket_client_new:
+ * @hostname: remote machine to connect to
+ * @port: remote port to connect to
+ * @ssl: whether or not to use SSL
+ * @callback: callback to call when the socket is connected
+ * @user_data: data for @callback
+ *
+ * Creates a connection to @uri. @callback will be called when the
+ * connection completes (or fails).
+ *
+ * Return value: the new socket (not yet ready for use).
+ **/
+SoupSocket *
+soup_socket_client_new (const char *hostname, guint port, gboolean ssl,
+                       SoupSocketCallback callback, gpointer user_data)
 {
-       int sockfd;
-       int flags;
-       struct soup_sockaddr_max sa;
-       socklen_t n;
-       fd_set fdset;
-       SoupSocket *s;
+       SoupSocket *sock;
 
-       g_return_val_if_fail (SOUP_IS_SOCKET (socket), NULL);
+       g_return_val_if_fail (hostname != NULL, NULL);
 
- try_again:
-       FD_ZERO (&fdset);
-       FD_SET (socket->priv->sockfd, &fdset);
+       sock = soup_socket_new ();
+       sock->priv->ssl = ssl;
+       soup_socket_connect (sock, soup_address_new (hostname, port));
 
-       if (select (socket->priv->sockfd + 1, &fdset, NULL, NULL, NULL) == -1) {
-               if (errno == EINTR) goto try_again;
-               return NULL;
+       if (callback) {
+               soup_signal_connect_once (sock, "connect_result",
+                                         G_CALLBACK (callback), user_data);
        }
+       return sock;
+}
 
-       n = sizeof(sa);
-
-       if ((sockfd = accept (socket->priv->sockfd, (struct sockaddr *)&sa, &n)) == -1) {
-               if (!block)
-                       return NULL;
-               if (errno == EWOULDBLOCK ||
-                   errno == ECONNABORTED ||
-#ifdef EPROTO          /* OpenBSD does not have EPROTO */
-                   errno == EPROTO ||
-#endif
-                   errno == EINTR)
-                       goto try_again;
+/**
+ * soup_socket_server_new:
+ * @local_addr: Local address to bind to. (Use soup_address_any_new() to
+ * accept connections on any local address)
+ * @ssl: Whether or not this is an SSL server.
+ * @callback: Callback to call when a client connects
+ * @user_data: data to pass to @callback.
+ *
+ * Create and open a new #SoupSocket listening on the specified
+ * address. @callback will be called each time a client connects,
+ * with a new #SoupSocket.
+ *
+ * Returns: a new #SoupSocket, or NULL if there was a failure.
+ **/
+SoupSocket *
+soup_socket_server_new (SoupAddress *local_addr, gboolean ssl,
+                       SoupSocketListenerCallback callback,
+                       gpointer user_data)
+{
+       SoupSocket *sock;
 
-               return NULL;
-       }
+       g_return_val_if_fail (SOUP_IS_ADDRESS (local_addr), NULL);
 
-       /* Get the flags (should all be 0?) */
-       flags = fcntl (sockfd, F_GETFL, 0);
-       if (flags == -1) return NULL;
+       sock = soup_socket_new ();
+       sock->priv->ssl = ssl;
 
-       /* Make the socket non-blocking */
-       if (fcntl (sockfd, F_SETFL, flags | O_NONBLOCK) == -1)
+       if (!soup_socket_listen (sock, local_addr)) {
+               g_object_unref (sock);
                return NULL;
+       }
 
-       s = g_object_new (SOUP_TYPE_SOCKET, NULL);
-       s->priv->sockfd = sockfd;
-       s->priv->addr = soup_address_new_from_sockaddr ((struct sockaddr *)&sa,
-                                                       &s->priv->port);
+       if (callback) {
+               g_signal_connect (sock, "new_connection",
+                                 G_CALLBACK (callback), user_data);
+       }
 
-       return s;
+       return sock;
 }
 
+
 /**
- * soup_socket_server_accept:
- * @socket: #SoupSocket to accept connections from.
+ * soup_socket_get_iochannel:
+ * @sock: #SoupSocket to get #GIOChannel from.
+ *
+ * Get the #GIOChannel for the #SoupSocket.
  *
- * Accept a connection from the socket.  The socket must have been
- * created using soup_socket_server_new().  This function will
- * block (use soup_socket_server_try_accept() if you don't
- * want to block).  If the socket's #GIOChannel is readable, it DOES
- * NOT mean that this function will not block.
+ * If you ref the iochannel, it will remain valid after @sock is
+ * destroyed.
  *
- * Returns: a new #SoupSocket if there is another connect, or %NULL if
- * there's an error.
+ * Returns: A #GIOChannel; %NULL on failure.
  **/
-SoupSocket *
-soup_socket_server_accept (SoupSocket *socket)
+GIOChannel *
+soup_socket_get_iochannel (SoupSocket *sock)
 {
-       return server_accept_internal (socket, TRUE);
+       g_return_val_if_fail (SOUP_IS_SOCKET (sock), NULL);
+
+       if (!sock->priv->iochannel) {
+               sock->priv->iochannel =
+                       g_io_channel_unix_new (sock->priv->sockfd);
+               g_io_channel_set_close_on_unref (sock->priv->iochannel, TRUE);
+       }
+       return sock->priv->iochannel;
 }
 
-/**
- * soup_socket_server_try_accept:
- * @socket: SoupSocket to accept connections from.
- *
- * Accept a connection from the socket without blocking.  The socket
- * must have been created using soup_socket_server_new().  This
- * function is best used with the sockets #GIOChannel.  If the
- * channel is readable, then you PROBABLY have a connection.  It is
- * possible for the connection to close by the time you call this, so
- * it may return %NULL even if the channel was readable.
- *
- * Returns a new SoupSocket if there is another connect, or %NULL
- * otherwise.
- **/
-SoupSocket *
-soup_socket_server_try_accept (SoupSocket *socket)
+
+SoupAddress *
+soup_socket_get_local_address (SoupSocket *sock)
 {
-       return server_accept_internal (socket, FALSE);
+       g_return_val_if_fail (SOUP_IS_SOCKET (sock), NULL);
+
+       if (!sock->priv->local_addr) {
+               struct soup_sockaddr_max bound_sa;
+               int sa_len;
+
+               sa_len = sizeof (bound_sa);
+               getsockname (sock->priv->sockfd, (struct sockaddr *)&bound_sa, &sa_len);
+               sock->priv->local_addr = soup_address_new_from_sockaddr ((struct sockaddr *)&bound_sa, sa_len);
+       }
+
+       return sock->priv->local_addr;
+}
+
+SoupAddress *
+soup_socket_get_remote_address (SoupSocket *sock)
+{
+       g_return_val_if_fail (SOUP_IS_SOCKET (sock), NULL);
+
+       if (!sock->priv->local_addr) {
+               struct soup_sockaddr_max bound_sa;
+               int sa_len;
+
+               sa_len = sizeof (bound_sa);
+               getpeername (sock->priv->sockfd, (struct sockaddr *)&bound_sa, &sa_len);
+               sock->priv->remote_addr = soup_address_new_from_sockaddr ((struct sockaddr *)&bound_sa, sa_len);
+       }
+
+       return sock->priv->remote_addr;
 }
index c82cfc4..036d52f 100644 (file)
@@ -8,6 +8,7 @@
 
 #include <glib-object.h>
 #include <libsoup/soup-address.h>
+#include <libsoup/soup-error.h>
 
 #define SOUP_TYPE_SOCKET            (soup_socket_get_type ())
 #define SOUP_SOCKET(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_SOCKET, SoupSocket))
@@ -27,70 +28,44 @@ typedef struct {
 typedef struct {
        GObjectClass parent_class;
 
+       /* signals */
+       void (*connect_result) (SoupSocket *, SoupKnownErrorCode);
+
+       void (*new_connection) (SoupSocket *, SoupSocket *);
 } SoupSocketClass;
 
 GType soup_socket_get_type (void);
 
+SoupSocket    *soup_socket_new                (void);
 
-typedef gpointer SoupSocketConnectId;
-
-typedef enum {
-       SOUP_SOCKET_CONNECT_ERROR_NONE,
-       SOUP_SOCKET_CONNECT_ERROR_ADDR_RESOLVE,
-       SOUP_SOCKET_CONNECT_ERROR_NETWORK
-} SoupSocketConnectStatus;
-
-typedef void (*SoupSocketConnectFn) (SoupSocket              *socket, 
-                                    SoupSocketConnectStatus  status, 
-                                    gpointer                 data);
-
-SoupSocketConnectId  soup_socket_connect        (const char         *hostname,
-                                                guint               port, 
-                                                SoupSocketConnectFn func, 
-                                                gpointer            data);
-
-void                 soup_socket_connect_cancel (SoupSocketConnectId id);
-
-SoupSocket          *soup_socket_connect_sync   (const char         *hostname, 
-                                                guint               port);
-
-
-typedef gpointer SoupSocketNewId;
-
-typedef enum {
-       SOUP_SOCKET_NEW_STATUS_OK,
-       SOUP_SOCKET_NEW_STATUS_ERROR
-} SoupSocketNewStatus;
-
-typedef void (*SoupSocketNewFn) (SoupSocket*         socket, 
-                                SoupSocketNewStatus status, 
-                                gpointer            data);
-
-SoupSocketNewId     soup_socket_new             (SoupAddress        *addr, 
-                                                guint               port,
-                                                SoupSocketNewFn     func,
-                                                gpointer            data);
-
-void                soup_socket_new_cancel      (SoupSocketNewId     id);
-
-SoupSocket         *soup_socket_new_sync        (SoupAddress        *addr,
-                                                guint               port);
-
-
-GIOChannel         *soup_socket_get_iochannel   (SoupSocket*         socket);
-
-SoupAddress        *soup_socket_get_address     (const SoupSocket*   socket);
+void           soup_socket_connect            (SoupSocket         *sock,
+                                              SoupAddress        *rem_addr);
+gboolean       soup_socket_listen             (SoupSocket         *sock,
+                                              SoupAddress        *local_addr);
+void           soup_socket_start_ssl          (SoupSocket         *sock);
 
-guint               soup_socket_get_port        (const SoupSocket*   socket);
 
+typedef void (*SoupSocketCallback)            (SoupSocket         *sock,
+                                              SoupKnownErrorCode  status,
+                                              gpointer            user_data);
+typedef void (*SoupSocketListenerCallback)    (SoupSocket         *listener,
+                                              SoupSocket         *sock,
+                                              gpointer            user_data);
 
-#define SOUP_SERVER_ANY_PORT 0
+SoupSocket    *soup_socket_client_new         (const char         *hostname,
+                                              guint               port,
+                                              gboolean            ssl,
+                                              SoupSocketCallback  callback,
+                                              gpointer            user_data);
+SoupSocket    *soup_socket_server_new         (SoupAddress        *local_addr,
+                                              gboolean            ssl,
+                                              SoupSocketListenerCallback,
+                                              gpointer            user_data);
 
-SoupSocket         *soup_socket_server_new        (SoupAddress        *local_addr,
-                                                  guint               local_port);
 
-SoupSocket         *soup_socket_server_accept     (SoupSocket         *socket);
+GIOChannel    *soup_socket_get_iochannel      (SoupSocket         *sock);
 
-SoupSocket         *soup_socket_server_try_accept (SoupSocket         *socket);
+SoupAddress   *soup_socket_get_local_address  (SoupSocket         *sock);
+SoupAddress   *soup_socket_get_remote_address (SoupSocket         *sock);
 
 #endif /* SOUP_SOCKET_H */
index 19b9949..c461e0e 100644 (file)
@@ -98,8 +98,7 @@ soup_socks_write (GIOChannel* iochannel,
                buf[len++] = 0x04;
                buf[len++] = 0x01;
                WSHORT (buf, &len, (gushort) dest_uri->port);
-               soup_address_make_sockaddr (sd->dest_addr, dest_uri->port,
-                                           &sa, &sa_len);
+               sa = soup_address_get_sockaddr (sd->dest_addr, &sa_len);
                memcpy (&buf [len], 
                        &((struct sockaddr_in *) sa)->sin_addr,
                        4);
@@ -161,10 +160,7 @@ soup_socks_write (GIOChannel* iochannel,
        return !finished;
 
  CONNECT_ERROR:
-       (*sd->cb) (sd->dest_ctx, 
-                  SOUP_CONNECT_ERROR_NETWORK, 
-                  NULL, 
-                  sd->user_data);
+       (*sd->cb) (sd->dest_ctx, SOUP_ERROR_CANT_CONNECT, NULL, sd->user_data);
        socks_data_free (sd);
        return FALSE;
 }
@@ -220,18 +216,12 @@ soup_socks_read (GIOChannel* iochannel,
        return TRUE;
 
  CONNECT_OK:
-       (*sd->cb) (sd->dest_ctx, 
-                  SOUP_CONNECT_ERROR_NONE, 
-                  sd->src_conn, 
-                  sd->user_data);
+       (*sd->cb) (sd->dest_ctx, SOUP_ERROR_OK, sd->src_conn, sd->user_data);
        socks_data_free (sd);
        return FALSE;
 
  CONNECT_ERROR:
-       (*sd->cb) (sd->dest_ctx, 
-                  SOUP_CONNECT_ERROR_NETWORK, 
-                  NULL, 
-                  sd->user_data);
+       (*sd->cb) (sd->dest_ctx, SOUP_ERROR_CANT_CONNECT, NULL, sd->user_data);
        socks_data_free (sd);
        return FALSE;
 }
@@ -241,33 +231,25 @@ soup_socks_error (GIOChannel* iochannel,
                  GIOCondition condition, 
                  SoupSocksData *sd)
 {
-       (*sd->cb) (sd->dest_ctx, 
-                  SOUP_CONNECT_ERROR_NETWORK, 
-                  NULL, 
-                  sd->user_data);
-
+       (*sd->cb) (sd->dest_ctx, SOUP_ERROR_CANT_CONNECT, NULL, sd->user_data);
        socks_data_free (sd);
        return FALSE;   
 }
 
 static void
-soup_lookup_dest_addr_cb (SoupAddress*         inetaddr, 
-                         SoupAddressStatus    status, 
+soup_lookup_dest_addr_cb (SoupAddress         *addr, 
+                         SoupKnownErrorCode   status, 
                          gpointer             data)
 {
        SoupSocksData *sd = data;
        GIOChannel *channel;
 
-       if (status != SOUP_ADDRESS_STATUS_OK) {
-               (*sd->cb) (sd->dest_ctx, 
-                          SOUP_CONNECT_ERROR_ADDR_RESOLVE, 
-                          NULL, 
-                          sd->user_data); 
+       if (status != SOUP_ERROR_OK) {
+               (*sd->cb) (sd->dest_ctx, status, NULL, sd->user_data); 
                g_free (sd);
                return;
        }
 
-       sd->dest_addr = inetaddr;
        sd->phase = SOCKS_4_SEND_DEST_ADDR;
 
        channel = soup_connection_get_iochannel (sd->src_conn);
@@ -277,7 +259,6 @@ soup_lookup_dest_addr_cb (SoupAddress*         inetaddr,
                        G_IO_ERR | G_IO_HUP | G_IO_NVAL, 
                        (GIOFunc) soup_socks_error, 
                        sd);            
-       g_io_channel_unref (channel);
 }
 
 void
@@ -304,9 +285,11 @@ soup_connect_socks_proxy (SoupConnection        *conn,
        proxy_uri = soup_context_get_uri (proxy_ctx);
 
        if (proxy_uri->protocol == SOUP_PROTOCOL_SOCKS4) {
-               soup_address_new (dest_uri->host, 
-                                 soup_lookup_dest_addr_cb,
-                                 sd);
+               sd->dest_addr = soup_address_new (dest_uri->host,
+                                                 dest_uri->port);
+               soup_address_resolve (sd->dest_addr, 
+                                     soup_lookup_dest_addr_cb,
+                                     sd);
                sd->phase = SOCKS_4_DEST_ADDR_LOOKUP;
        } else if (proxy_uri->protocol == SOUP_PROTOCOL_SOCKS5) {
                channel = soup_connection_get_iochannel (conn);
@@ -322,7 +305,6 @@ soup_connect_socks_proxy (SoupConnection        *conn,
                                G_IO_ERR | G_IO_HUP | G_IO_NVAL, 
                                (GIOFunc) soup_socks_error, 
                                sd);            
-               g_io_channel_unref (channel);
 
                sd->phase = SOCKS_5_SEND_INIT;
        } else
@@ -331,6 +313,6 @@ soup_connect_socks_proxy (SoupConnection        *conn,
        return;
        
  CONNECT_SUCCESS:
-       (*cb) (dest_ctx, SOUP_CONNECT_ERROR_NONE, conn, user_data); 
+       (*cb) (dest_ctx, SOUP_ERROR_OK, conn, user_data); 
        g_free (sd);
 }
index 9f549e5..027ff82 100644 (file)
@@ -119,8 +119,8 @@ main (int argc, char **argv)
        GMainLoop *loop;
        SoupServer *server, *ssl_server;
        int opt;
-       int port = SOUP_SERVER_ANY_PORT;
-       int ssl_port = SOUP_SERVER_ANY_PORT;
+       int port = SOUP_ADDRESS_ANY_PORT;
+       int ssl_port = SOUP_ADDRESS_ANY_PORT;
 
        g_type_init ();
        signal (SIGINT, quit);
index 92a2967..255c6a6 100644 (file)
@@ -50,7 +50,7 @@ main (int argc, char **argv)
 {
        GMainLoop *loop;
        int opt;
-       int port = SOUP_SERVER_ANY_PORT;
+       int port = SOUP_ADDRESS_ANY_PORT;
        SoupServer *server;
 
        g_type_init ();
index 9ea8d9b..766c322 100644 (file)
@@ -8,29 +8,60 @@
 #include <time.h>
 #include <libsoup/soup.h>
 
-int
-main (int argc, char **argv)
+static void
+got_address (SoupAddress *addr, SoupKnownErrorCode status, gpointer user_data)
 {
-       SoupSocket *listener, *client;
-       SoupAddress *addr;
-       guint port;
+       SoupSocket *client = user_data;
+       const char *name, *phys;
        time_t now;
        char *timebuf;
        GIOChannel *chan;
        gsize wrote;
 
+       name = soup_address_get_name (addr);
+       phys = soup_address_get_physical (addr);
+
+       printf ("got connection from %s (%s) port %d\n",
+               name ? name : "?", phys,
+               soup_address_get_port (addr));
+
+       now = time (NULL);
+       timebuf = ctime (&now);
+
+       chan = soup_socket_get_iochannel (client);
+       g_io_channel_write (chan, timebuf, strlen (timebuf), &wrote);
+       g_io_channel_unref (chan);
+
+       g_object_unref (client);
+}
+
+static void
+new_connection (SoupSocket *listener, SoupSocket *client, gpointer user_data)
+{
+       SoupAddress *addr;
+
+       g_object_ref (client);
+       addr = soup_socket_get_remote_address (client);
+       soup_address_resolve (addr, got_address, client);
+}
+
+int
+main (int argc, char **argv)
+{
+       SoupSocket *listener;
+       SoupAddressFamily family;
+       SoupAddress *addr;
+       guint port;
+       GMainLoop *loop;
+
        g_type_init ();
 
        if (argc >=2 && !strcmp (argv[1], "-6")) {
-               addr = soup_address_ipv6_any ();
-               if (!addr) {
-                       fprintf (stderr, "No IPv6 support\n");
-                       exit (1);
-               }
+               family = SOUP_ADDRESS_FAMILY_IPV6;
                argc--;
                argv++;
        } else
-               addr = soup_address_ipv4_any ();
+               family = SOUP_ADDRESS_FAMILY_IPV4;
 
        if (argc > 2) {
                fprintf (stderr, "Usage: %s [-6] [port]\n", argv[0]);
@@ -40,31 +71,27 @@ main (int argc, char **argv)
        if (argc == 2)
                port = atoi (argv[1]);
        else
-               port = SOUP_SERVER_ANY_PORT;
-       listener = soup_socket_server_new (addr, port);
+               port = SOUP_ADDRESS_ANY_PORT;
+
+       addr = soup_address_new_any (SOUP_ADDRESS_FAMILY_IPV4, port);
+       if (!addr) {
+               fprintf (stderr, "Could not create listener address\n");
+               exit (1);
+       }
+
+       listener = soup_socket_server_new (addr, port,
+                                          new_connection, NULL);
        g_object_unref (addr);
        if (!listener) {
                fprintf (stderr, "Could not create listening socket\n");
                exit (1);
        }
-       printf ("Listening on port %d\n", soup_socket_get_port (listener));
-
-       while ((client = soup_socket_server_accept (listener))) {
-               addr = soup_socket_get_address (client);
-               printf ("got connection from %s port %d\n",
-                       soup_address_get_name_sync (addr),
-                       soup_socket_get_port (client));
-               g_object_unref (addr);
+       printf ("Listening on port %d\n",
+               soup_address_get_port (
+                       soup_socket_get_local_address (listener)));
 
-               now = time (NULL);
-               timebuf = ctime (&now);
-
-               chan = soup_socket_get_iochannel (client);
-               g_io_channel_write (chan, timebuf, strlen (timebuf), &wrote);
-               g_io_channel_unref (chan);
-
-               g_object_unref (client);
-       }
+       loop = g_main_loop_new (NULL, TRUE);
+       g_main_loop_run (loop);
 
        g_object_unref (listener);
        return 0;