+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
#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
#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;
{
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);
/* 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;
}
/**
* 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);
+ }
}
#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))
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 */
struct SoupConnectionPrivate {
SoupSocket *socket;
- GIOChannel *raw_chan, *cooked_chan;
gboolean in_use, new;
time_t last_used;
guint death_tag;
LAST_SIGNAL
};
-guint signals[LAST_SIGNAL] = { 0 };
+static guint signals[LAST_SIGNAL] = { 0 };
+
static void
init (GObject *object)
{
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;
}
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);
}
/**
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;
* 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)
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);
}
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);
gpointer user_data;
guint timeout_tag;
- gpointer connect_tag;
+ SoupSocket *sock;
};
static 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),
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--;
/*
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);
}
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;
}
}
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
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;
}
return FALSE;
}
- return try_create_connection (&data) == FALSE;
+ return try_create_connection (data) == FALSE;
}
/**
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,
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);
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);
}
char *
-soup_ntop (gpointer addr, int family)
+soup_ntop (gconstpointer addr, int family)
{
switch (family) {
case AF_INET:
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);
}
void soup_gethostby_cancel (SoupDNSHandle id);
-char *soup_ntop (gpointer addr,
+char *soup_ntop (gconstpointer addr,
int family);
#endif /* SOUP_DNS_H */
* 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 "
* 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,
NONE:NONE
+NONE:INT
+NONE:OBJECT
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;
+}
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);
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
guint refcnt;
GMainLoop *loop;
- guint accept_tag;
SoupSocket *listen_sock;
GIOChannel *cgi_read_chan;
soup_queue_read_done_cb,
soup_queue_error_cb,
req);
-
- g_io_channel_unref (channel);
}
else {
req->status = SOUP_STATUS_FINISHED;
soup_queue_error_cb,
req);
- g_io_channel_unref (channel);
-
req->status = SOUP_STATUS_SENDING_REQUEST;
}
void
soup_queue_connect_cb (SoupContext *ctx,
- SoupConnectErrorCode err,
+ SoupKnownErrorCode err,
SoupConnection *conn,
gpointer user_data)
{
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.
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;
}
gpointer user_data);
void soup_queue_connect_cb (SoupContext *ctx,
- SoupConnectErrorCode err,
+ SoupKnownErrorCode err,
SoupConnection *conn,
gpointer user_data);
};
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);
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);
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 *
{
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 *
--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);
start_another_request,
data);
}
-
- g_io_channel_unref (chan);
}
if (server_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)
{
SOUP_TRANSFER_CONTENT_LENGTH);
channel = soup_socket_get_iochannel (msg->priv->server_sock);
-
msg->priv->write_tag =
soup_transfer_write_simple (channel,
header,
write_done_cb,
error_cb,
msg);
-
- g_io_channel_unref (channel);
-} /* issue_bad_request */
+}
static void
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",
req);
}
- g_io_channel_unref (channel);
-
return;
}
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 {
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);
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 *
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
#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)
{
{
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);
/* 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;
}
#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))
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 */
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);
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;
}
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;
}
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);
G_IO_ERR | G_IO_HUP | G_IO_NVAL,
(GIOFunc) soup_socks_error,
sd);
- g_io_channel_unref (channel);
}
void
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);
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
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);
}
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);
{
GMainLoop *loop;
int opt;
- int port = SOUP_SERVER_ANY_PORT;
+ int port = SOUP_ADDRESS_ANY_PORT;
SoupServer *server;
g_type_init ();
#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]);
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;