From e3172515fcfb95e994cf4eadadeb6fd62b5d221f Mon Sep 17 00:00:00 2001 From: Dan Winship Date: Mon, 11 Nov 2002 22:15:29 +0000 Subject: [PATCH] Move the SoupAddress code from soup-socket.c and soup-socket-unix.c to * libsoup/soup-address.c: Move the SoupAddress code from soup-socket.c and soup-socket-unix.c to here. * libsoup/soup-socket.c: Move the remaining code from soup-socket-unix.c here. * libsoup/soup-socket-unix.c: Gone * tests/get.c: really really trivial test program * configure.in (AC_OUTPUT): * Makefile.am (SUBDIRS): add tests/ --- .cvsignore | 2 +- ChangeLog | 15 + Makefile.am | 2 +- configure.in | 1 + libsoup/Makefile.am | 3 +- libsoup/{soup-socket-unix.c => soup-address.c} | 607 +++++++++++-------------- libsoup/soup-address.h | 86 ++++ libsoup/soup-socket.c | 559 ++++++++++++----------- libsoup/soup-socket.h | 70 +-- tests/.cvsignore | 3 + tests/Makefile.am | 9 + tests/get.c | 35 ++ 12 files changed, 726 insertions(+), 666 deletions(-) rename libsoup/{soup-socket-unix.c => soup-address.c} (77%) create mode 100644 libsoup/soup-address.h create mode 100644 tests/.cvsignore create mode 100644 tests/Makefile.am create mode 100644 tests/get.c diff --git a/.cvsignore b/.cvsignore index c49d391..40b72a9 100644 --- a/.cvsignore +++ b/.cvsignore @@ -12,7 +12,7 @@ ltconfig ltmain.sh Makefile Makefile.in -soup.pc +soup-2.0.pc stamp-h stamp.h stamp-h.in diff --git a/ChangeLog b/ChangeLog index 713c6ee..829bb9b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,18 @@ +2002-11-11 Dan Winship + + * libsoup/soup-address.c: Move the SoupAddress code from + soup-socket.c and soup-socket-unix.c to here. + + * libsoup/soup-socket.c: Move the remaining code from + soup-socket-unix.c here. + + * libsoup/soup-socket-unix.c: Gone + + * tests/get.c: really really trivial test program + + * configure.in (AC_OUTPUT): + * Makefile.am (SUBDIRS): add tests/ + 2002-11-05 Dan Winship * Split libsoup out of soup. ChangeLog.old contains the original diff --git a/Makefile.am b/Makefile.am index 5b2c4df..0c008a2 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,6 +1,6 @@ ## Process this file with automake to produce Makefile.in -SUBDIRS = libsoup +SUBDIRS = libsoup tests EXTRA_DIST = soup-2.0.pc.in diff --git a/configure.in b/configure.in index 4629d66..120fe91 100644 --- a/configure.in +++ b/configure.in @@ -368,6 +368,7 @@ AC_OUTPUT([ soup-2.0.pc Makefile libsoup/Makefile + tests/Makefile ]) echo " diff --git a/libsoup/Makefile.am b/libsoup/Makefile.am index 8e6f169..cca7644 100644 --- a/libsoup/Makefile.am +++ b/libsoup/Makefile.am @@ -14,6 +14,7 @@ libsoupincludedir = $(includedir)/soup-2.0/libsoup libsoupinclude_HEADERS = \ soup.h \ + soup-address.h \ soup-context.h \ soup-error.h \ soup-headers.h \ @@ -38,6 +39,7 @@ libsoup_2_0_la_LIBADD = \ libsoup_2_0_la_SOURCES = \ md5-utils.h \ md5-utils.c \ + soup-address.c \ soup-auth.h \ soup-auth.c \ soup-context.c \ @@ -55,7 +57,6 @@ libsoup_2_0_la_SOURCES = \ soup-server.c \ soup-server-auth.c \ soup-socket.c \ - soup-socket-unix.c \ soup-socks.h \ soup-socks.c \ soup-ssl.h \ diff --git a/libsoup/soup-socket-unix.c b/libsoup/soup-address.c similarity index 77% rename from libsoup/soup-socket-unix.c rename to libsoup/soup-address.c index c5dd86b..19892f4 100644 --- a/libsoup/soup-socket-unix.c +++ b/libsoup/soup-address.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* - * soup-socket-unix.c: Unix socket networking code. + * soup-address.c: Internet address handing * * Authors: * David Helder (dhelder@umich.edu) @@ -9,10 +9,7 @@ * Original code compliments of David Helder's GNET Networking Library, and is * Copyright (C) 2000 David Helder & Andrew Lanoix. * - * This is not originally my code. I've tried to clean it up where possible. - * But I make no promises towards its sanity. - * - * All else Copyright (C) 2000, Ximian, Inc. + * All else Copyright (C) 2000-2002, Ximian, Inc. */ #ifdef HAVE_CONFIG_H @@ -22,39 +19,291 @@ #include #include #include -#include -#include #include #include #include +#include +#include + +#include +#include +#include -#ifdef HAVE_SYS_PARAM_H -#include -#endif #include "soup-private.h" -#include "soup-socket.h" +#include "soup-address.h" -#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#ifndef socklen_t +# define socklen_t size_t +#endif -#ifdef HAVE_SYS_POLL_H -#include +#ifndef INET_ADDRSTRLEN +# define INET_ADDRSTRLEN 16 +# define INET6_ADDRSTRLEN 46 #endif -#ifdef HAVE_SYS_SOCKIO_H -#include +#ifndef INADDR_NONE +#define INADDR_NONE -1 #endif +#define SOUP_SOCKADDR_IN(s) (*((struct sockaddr_in*) &s)) + +static void +soup_address_new_sync_cb (SoupAddress *addr, + SoupAddressStatus status, + gpointer user_data) +{ + SoupAddress **ret = user_data; + *ret = addr; +} + +SoupAddress * +soup_address_new_sync (const gchar *name, const gint port) +{ + SoupAddress *ret = (SoupAddress *) 0xdeadbeef; + + soup_address_new (name, port, soup_address_new_sync_cb, &ret); + + while (1) { + g_main_iteration (TRUE); + if (ret != (SoupAddress *) 0xdeadbeef) return ret; + } + + return ret; +} + +/** + * soup_address_ref + * @ia: SoupAddress to reference + * + * Increment the reference counter of the SoupAddress. + **/ +void +soup_address_ref (SoupAddress* ia) +{ + g_return_if_fail (ia != NULL); + + ++ia->ref_count; +} + +/** + * soup_address_copy + * @ia: SoupAddress to copy + * + * Creates a copy of the given SoupAddress + **/ +SoupAddress * +soup_address_copy (SoupAddress* ia) +{ + SoupAddress* new_ia; + g_return_val_if_fail (ia != NULL, NULL); + + new_ia = g_new0(SoupAddress, 1); + new_ia->ref_count = 1; + + new_ia->name = g_strdup (ia->name); + memcpy (&new_ia->sa, &ia->sa, sizeof(struct sockaddr)); + + return new_ia; +} + +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; +} + +const gchar * +soup_address_get_name_sync (SoupAddress *addr) +{ + const char *ret = (const char *) 0xdeadbeef; + + soup_address_get_name (addr, + soup_address_get_name_sync_cb, + (gpointer) &ret); + + while (1) { + g_main_iteration (TRUE); + if (ret != (const char *) 0xdeadbeef) return ret; + } + + return ret; +} + +/** + * soup_address_get_canonical_name: + * @ia: Address to get the canonical name of. + * + * Get the "canonical" name of an address (eg, for IP4 the dotted + * decimal name 141.213.8.59). + * + * Returns: NULL if there was an error. The caller is responsible + * for deleting the returned string. + **/ +gchar* +soup_address_get_canonical_name (SoupAddress* ia) +{ + gchar buffer [INET_ADDRSTRLEN]; /* defined in netinet/in.h */ + guchar* p = (guchar*) &(SOUP_SOCKADDR_IN(ia->sa).sin_addr); + + g_return_val_if_fail (ia != NULL, NULL); + + g_snprintf(buffer, + sizeof (buffer), + "%d.%d.%d.%d", + p [0], + p [1], + p [2], + p [3]); + + return g_strdup (buffer); +} + +/** + * soup_address_get_port: + * @ia: Address to get the port number of. + * + * Get the port number. + * Returns: the port number. + */ +gint +soup_address_get_port (const SoupAddress* ia) +{ + g_return_val_if_fail(ia != NULL, -1); + + return (gint) g_ntohs (((struct sockaddr_in*) &ia->sa)->sin_port); +} + +/** + * soup_address_get_sockaddr: + * @ia: The %SoupAddress. + * @addrlen: Pointer to socklen_t the returned sockaddr's length is to be + * placed in. + * + * Return value: const pointer to @ia's sockaddr buffer. + **/ +const struct sockaddr * +soup_address_get_sockaddr (SoupAddress *ia, guint *addrlen) +{ + g_return_val_if_fail (ia != NULL, NULL); + + if (addrlen) + *addrlen = sizeof (struct sockaddr_in); + + return &ia->sa; +} + +/** + * soup_address_hash: + * @p: Pointer to an #SoupAddress. + * + * Hash the address. This is useful for glib containers. + * + * Returns: hash value. + **/ +guint +soup_address_hash (const gpointer p) +{ + const SoupAddress* ia; + guint32 port; + guint32 addr; + + g_assert(p != NULL); + + ia = (const SoupAddress*) p; + + /* + * We do pay attention to network byte order just in case the hash + * result is saved or sent to a different host. + */ + port = (guint32) g_ntohs (((struct sockaddr_in*) &ia->sa)->sin_port); + addr = g_ntohl (((struct sockaddr_in*) &ia->sa)->sin_addr.s_addr); + + return (port ^ addr); +} + +/** + * soup_address_equal: + * @p1: Pointer to first #SoupAddress. + * @p2: Pointer to second #SoupAddress. + * + * Compare two #SoupAddress's. + * + * Returns: 1 if they are the same; 0 otherwise. + **/ +gint +soup_address_equal (const gpointer p1, const gpointer p2) +{ + const SoupAddress* ia1 = (const SoupAddress*) p1; + const SoupAddress* ia2 = (const SoupAddress*) p2; + + g_assert (p1 != NULL && p2 != NULL); + + /* Note network byte order doesn't matter */ + return ((SOUP_SOCKADDR_IN(ia1->sa).sin_addr.s_addr == + SOUP_SOCKADDR_IN(ia2->sa).sin_addr.s_addr) && + (SOUP_SOCKADDR_IN(ia1->sa).sin_port == + SOUP_SOCKADDR_IN(ia2->sa).sin_port)); +} + +/** + * soup_address_noport_equal: + * @p1: Pointer to first SoupAddress. + * @p2: Pointer to second SoupAddress. + * + * Compare two #SoupAddress's, but does not compare the port numbers. + * + * Returns: 1 if they are the same; 0 otherwise. + **/ +gint +soup_address_noport_equal (const gpointer p1, const gpointer p2) +{ + const SoupAddress* ia1 = (const SoupAddress*) p1; + const SoupAddress* ia2 = (const SoupAddress*) p2; + + g_assert (p1 != NULL && p2 != NULL); + + /* Note network byte order doesn't matter */ + return (SOUP_SOCKADDR_IN(ia1->sa).sin_addr.s_addr == + SOUP_SOCKADDR_IN(ia2->sa).sin_addr.s_addr); +} + +/** + * soup_address_gethostaddr: + * + * Get the primary host's #SoupAddress. + * + * Returns: the #SoupAddress of the host; NULL if there was an error. + * The caller is responsible for deleting the returned #SoupAddress. + **/ +SoupAddress * +soup_address_gethostaddr (void) +{ + gchar* name; + struct sockaddr_in* sa_in, sa; + SoupAddress* ia = NULL; + + name = soup_address_gethostname (); + + if (name && soup_gethostbyname (name, &sa, NULL)) { + ia = g_new0 (SoupAddress, 1); + ia->name = g_strdup (name); + ia->ref_count = 1; + + sa_in = (struct sockaddr_in*) &ia->sa; + sa_in->sin_family = AF_INET; + sa_in->sin_port = 0; + memcpy (&sa_in->sin_addr, &sa.sin_addr, 4); + } + + return ia; +} + #ifdef G_ENABLE_DEBUG # include # ifndef PTRACE_ATTACH @@ -73,29 +322,12 @@ #undef SOUP_PTRACE_ATTACH #endif -#ifndef socklen_t -#define socklen_t size_t -#endif - -#ifndef INADDR_NONE -#define INADDR_NONE -1 -#endif - /* * Maintains a list of all currently valid SoupAddresses or active * SoupAddressState lookup requests. */ GHashTable *active_address_hash = NULL; -#ifndef INET_ADDRSTRLEN -#define INET_ADDRSTRLEN 16 -#define INET6_ADDRSTRLEN 46 -#endif - -#define SOUP_SOCKADDR_IN(s) (*((struct sockaddr_in*) &s)) -#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 { SoupAddressNewFn func; gpointer data; @@ -126,14 +358,6 @@ typedef struct { int len; } SoupAddressReverseState; -typedef struct { - gint sockfd; - SoupAddress *addr; - SoupSocketNewFn func; - gpointer data; - gint flags; - guint connect_watch; -} SoupSocketState; /* Testing Defines */ /* #undef HAVE_GETHOSTBYNAME_R_GLIBC */ @@ -1228,286 +1452,3 @@ soup_address_gethostname (void) return name; } - -static gboolean -soup_socket_new_cb (GIOChannel* iochannel, - GIOCondition condition, - gpointer data) -{ - SoupSocketState* state = (SoupSocketState*) data; - SoupSocket* s; - gint error = 0; - gint len = sizeof (gint); - - /* 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; - - if (fcntl (state->sockfd, F_SETFL, state->flags) != 0) - goto ERROR; - - s = g_new0 (SoupSocket, 1); - s->ref_count = 1; - s->sockfd = state->sockfd; - s->addr = state->addr; - - (*state->func) (s, SOUP_SOCKET_NEW_STATUS_OK, state->data); - - g_free (state); - - return FALSE; - - ERROR: - soup_address_unref (state->addr); - (*state->func) (NULL, SOUP_SOCKET_NEW_STATUS_ERROR, state->data); - g_free (state); - - return FALSE; -} - -/** - * soup_socket_new: - * @addr: Address to connect to. - * @func: Callback function. - * @data: User data passed when callback function is called. - * - * 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. - * - * Returns: ID of the connection which can be used with - * soup_socket_connect_cancel() to cancel it; NULL on - * failure. - **/ -SoupSocketNewId -soup_socket_new (SoupAddress *addr, - SoupSocketNewFn func, - gpointer data) -{ - gint sockfd; - gint flags; - SoupSocketState* state; - GIOChannel *chan; - - g_return_val_if_fail(addr != NULL, NULL); - g_return_val_if_fail(func != NULL, NULL); - - /* Create socket */ - sockfd = socket (AF_INET, SOCK_STREAM, 0); - if (sockfd < 0) { - (func) (NULL, SOUP_SOCKET_NEW_STATUS_ERROR, data); - 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); - return NULL; - } - - if (fcntl (sockfd, F_SETFL, flags | O_NONBLOCK) == -1) { - (func) (NULL, SOUP_SOCKET_NEW_STATUS_ERROR, data); - return NULL; - } - - errno = 0; - - /* Connect (but non-blocking!) */ - if (connect (sockfd, &addr->sa, sizeof (addr->sa)) < 0 && - errno != EINPROGRESS) { - (func) (NULL, SOUP_SOCKET_NEW_STATUS_ERROR, data); - return NULL; - } - - /* Unref in soup_socket_new_cb if failure */ - soup_address_ref (addr); - - /* Connect succeeded, return immediately */ - if (!errno) { - SoupSocket *s = g_new0 (SoupSocket, 1); - s->ref_count = 1; - s->sockfd = sockfd; - s->addr = addr; - - (*func) (s, SOUP_SOCKET_NEW_STATUS_OK, data); - return NULL; - } - - chan = g_io_channel_unix_new (sockfd); - - /* Wait for the connection */ - state = g_new0 (SoupSocketState, 1); - state->sockfd = sockfd; - state->addr = addr; - 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; -} - -/** - * soup_socket_new_cancel: - * @id: ID of the connection. - * - * Cancel an asynchronous connection that was started with - * soup_socket_new(). - **/ -void -soup_socket_new_cancel (SoupSocketNewId id) -{ - SoupSocketState* state = (SoupSocketState*) id; - - g_source_remove (state->connect_watch); - soup_address_unref (state->addr); - g_free (state); -} - -/** - * soup_socket_server_accept: - * @socket: #SoupSocket to accept connections from. - * - * 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. - * - * Returns: a new #SoupSocket if there is another connect, or NULL if - * there's an error. - **/ -SoupSocket * -soup_socket_server_accept (SoupSocket *socket) -{ - gint sockfd; - gint flags; - struct sockaddr sa; - socklen_t n; - fd_set fdset; - SoupSocket* s; - - g_return_val_if_fail (socket != NULL, NULL); - - try_again: - FD_ZERO (&fdset); - FD_SET (socket->sockfd, &fdset); - - if (select (socket->sockfd + 1, &fdset, NULL, NULL, NULL) == -1) { - if (errno == EINTR) goto try_again; - return NULL; - } - - n = sizeof(s->addr->sa); - - if ((sockfd = accept (socket->sockfd, &sa, &n)) == -1) { - if (errno == EWOULDBLOCK || - errno == ECONNABORTED || -#ifdef EPROTO /* OpenBSD does not have EPROTO */ - errno == EPROTO || -#endif - errno == EINTR) - goto try_again; - - return NULL; - } - - /* Get the flags (should all be 0?) */ - flags = fcntl (sockfd, F_GETFL, 0); - if (flags == -1) return NULL; - - /* Make the socket non-blocking */ - if (fcntl (sockfd, F_SETFL, flags | O_NONBLOCK) == -1) - return NULL; - - s = g_new0 (SoupSocket, 1); - s->ref_count = 1; - s->sockfd = sockfd; - - s->addr = g_new0 (SoupAddress, 1); - s->addr->ref_count = 1; - memcpy (&s->addr->sa, &sa, sizeof (s->addr->sa)); - - return s; -} - -/** - * 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) -{ - gint sockfd; - gint flags; - struct sockaddr sa; - socklen_t n; - fd_set fdset; - SoupSocket* s; - struct timeval tv = {0, 0}; - - g_return_val_if_fail (socket != NULL, NULL); - - try_again: - FD_ZERO (&fdset); - FD_SET (socket->sockfd, &fdset); - - if (select (socket->sockfd + 1, &fdset, NULL, NULL, &tv) == -1) { - if (errno == EINTR) goto try_again; - return NULL; - } - - n = sizeof(sa); - - if ((sockfd = accept (socket->sockfd, &sa, &n)) == -1) { - /* If we get an error, return. We don't want to try again as we - do in soup_socket_server_accept() - it might cause a - block. */ - return NULL; - } - - /* Get the flags (should all be 0?) */ - flags = fcntl (sockfd, F_GETFL, 0); - if (flags == -1) return NULL; - - /* Make the socket non-blocking */ - if (fcntl (sockfd, F_SETFL, flags | O_NONBLOCK) == -1) - return NULL; - - s = g_new0 (SoupSocket, 1); - s->ref_count = 1; - s->sockfd = sockfd; - - s->addr = g_new0 (SoupAddress, 1); - s->addr->ref_count = 1; - memcpy (&s->addr->sa, &sa, sizeof (s->addr->sa)); - - return s; -} diff --git a/libsoup/soup-address.h b/libsoup/soup-address.h new file mode 100644 index 0000000..faee829 --- /dev/null +++ b/libsoup/soup-address.h @@ -0,0 +1,86 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Authors: + * David Helder (dhelder@umich.edu) + * Alex Graveley (alex@ximian.com) + * + * Original code compliments of David Helder's GNET Networking Library. + * + * Copyright (C) 2000-2002, Ximian, Inc. + */ + +#ifndef SOUP_ADDRESS_H +#define SOUP_ADDRESS_H + +#include + +typedef struct _SoupAddress SoupAddress; + +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 gchar* name, + const gint port, + SoupAddressNewFn func, + gpointer data); + +void soup_address_new_cancel (SoupAddressNewId id); + +SoupAddress *soup_address_new_sync (const gchar *name, + const gint port); + +SoupAddress *soup_address_lookup_in_cache (const gchar *name, + const gint port); + +void soup_address_ref (SoupAddress* ia); + +void soup_address_unref (SoupAddress* ia); + +SoupAddress * soup_address_copy (SoupAddress* ia); + + +typedef gpointer SoupAddressGetNameId; + +typedef void (*SoupAddressGetNameFn) (SoupAddress *inetaddr, + SoupAddressStatus status, + const gchar *name, + gpointer user_data); + +SoupAddressGetNameId soup_address_get_name (SoupAddress* ia, + SoupAddressGetNameFn func, + gpointer data); + +void soup_address_get_name_cancel (SoupAddressGetNameId id); + +const gchar *soup_address_get_name_sync (SoupAddress *addr); + +gchar* soup_address_get_canonical_name (SoupAddress* ia); + +gint soup_address_get_port (const SoupAddress* ia); + +const struct sockaddr * + soup_address_get_sockaddr (SoupAddress *ia, + guint *addrlen); + +guint soup_address_hash (const gpointer p); + +gint soup_address_equal (const gpointer p1, + const gpointer p2); + +gint soup_address_noport_equal (const gpointer p1, + const gpointer p2); + +gchar* soup_address_gethostname (void); + +SoupAddress* soup_address_gethostaddr (void); + + +#endif /* SOUP_ADDRESS_H */ diff --git a/libsoup/soup-socket.c b/libsoup/soup-socket.c index 0b51ce3..75b56f9 100644 --- a/libsoup/soup-socket.c +++ b/libsoup/soup-socket.c @@ -15,6 +15,8 @@ #ifdef HAVE_CONFIG_H #include #endif + +#include #include #include #include @@ -27,11 +29,6 @@ # define socklen_t size_t #endif -#ifndef INET_ADDRSTRLEN -# define INET_ADDRSTRLEN 16 -# define INET6_ADDRSTRLEN 46 -#endif - #define SOUP_SOCKADDR_IN(s) (*((struct sockaddr_in*) &s)) typedef struct { @@ -43,262 +40,6 @@ typedef struct { } SoupSocketConnectState; static void -soup_address_new_sync_cb (SoupAddress *addr, - SoupAddressStatus status, - gpointer user_data) -{ - SoupAddress **ret = user_data; - *ret = addr; -} - -SoupAddress * -soup_address_new_sync (const gchar *name, const gint port) -{ - SoupAddress *ret = (SoupAddress *) 0xdeadbeef; - - soup_address_new (name, port, soup_address_new_sync_cb, &ret); - - while (1) { - g_main_iteration (TRUE); - if (ret != (SoupAddress *) 0xdeadbeef) return ret; - } - - return ret; -} - -/** - * soup_address_ref - * @ia: SoupAddress to reference - * - * Increment the reference counter of the SoupAddress. - **/ -void -soup_address_ref (SoupAddress* ia) -{ - g_return_if_fail (ia != NULL); - - ++ia->ref_count; -} - -/** - * soup_address_copy - * @ia: SoupAddress to copy - * - * Creates a copy of the given SoupAddress - **/ -SoupAddress * -soup_address_copy (SoupAddress* ia) -{ - SoupAddress* new_ia; - g_return_val_if_fail (ia != NULL, NULL); - - new_ia = g_new0(SoupAddress, 1); - new_ia->ref_count = 1; - - new_ia->name = g_strdup (ia->name); - memcpy (&new_ia->sa, &ia->sa, sizeof(struct sockaddr)); - - return new_ia; -} - -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; -} - -const gchar * -soup_address_get_name_sync (SoupAddress *addr) -{ - const char *ret = (const char *) 0xdeadbeef; - - soup_address_get_name (addr, - soup_address_get_name_sync_cb, - (gpointer) &ret); - - while (1) { - g_main_iteration (TRUE); - if (ret != (const char *) 0xdeadbeef) return ret; - } - - return ret; -} - -/** - * soup_address_get_canonical_name: - * @ia: Address to get the canonical name of. - * - * Get the "canonical" name of an address (eg, for IP4 the dotted - * decimal name 141.213.8.59). - * - * Returns: NULL if there was an error. The caller is responsible - * for deleting the returned string. - **/ -gchar* -soup_address_get_canonical_name (SoupAddress* ia) -{ - gchar buffer [INET_ADDRSTRLEN]; /* defined in netinet/in.h */ - guchar* p = (guchar*) &(SOUP_SOCKADDR_IN(ia->sa).sin_addr); - - g_return_val_if_fail (ia != NULL, NULL); - - g_snprintf(buffer, - sizeof (buffer), - "%d.%d.%d.%d", - p [0], - p [1], - p [2], - p [3]); - - return g_strdup (buffer); -} - -/** - * soup_address_get_port: - * @ia: Address to get the port number of. - * - * Get the port number. - * Returns: the port number. - */ -gint -soup_address_get_port (const SoupAddress* ia) -{ - g_return_val_if_fail(ia != NULL, -1); - - return (gint) g_ntohs (((struct sockaddr_in*) &ia->sa)->sin_port); -} - -/** - * soup_address_get_sockaddr: - * @ia: The %SoupAddress. - * @addrlen: Pointer to socklen_t the returned sockaddr's length is to be - * placed in. - * - * Return value: const pointer to @ia's sockaddr buffer. - **/ -const struct sockaddr * -soup_address_get_sockaddr (SoupAddress *ia, guint *addrlen) -{ - g_return_val_if_fail (ia != NULL, NULL); - - if (addrlen) - *addrlen = sizeof (struct sockaddr_in); - - return &ia->sa; -} - -/** - * soup_address_hash: - * @p: Pointer to an #SoupAddress. - * - * Hash the address. This is useful for glib containers. - * - * Returns: hash value. - **/ -guint -soup_address_hash (const gpointer p) -{ - const SoupAddress* ia; - guint32 port; - guint32 addr; - - g_assert(p != NULL); - - ia = (const SoupAddress*) p; - - /* - * We do pay attention to network byte order just in case the hash - * result is saved or sent to a different host. - */ - port = (guint32) g_ntohs (((struct sockaddr_in*) &ia->sa)->sin_port); - addr = g_ntohl (((struct sockaddr_in*) &ia->sa)->sin_addr.s_addr); - - return (port ^ addr); -} - -/** - * soup_address_equal: - * @p1: Pointer to first #SoupAddress. - * @p2: Pointer to second #SoupAddress. - * - * Compare two #SoupAddress's. - * - * Returns: 1 if they are the same; 0 otherwise. - **/ -gint -soup_address_equal (const gpointer p1, const gpointer p2) -{ - const SoupAddress* ia1 = (const SoupAddress*) p1; - const SoupAddress* ia2 = (const SoupAddress*) p2; - - g_assert (p1 != NULL && p2 != NULL); - - /* Note network byte order doesn't matter */ - return ((SOUP_SOCKADDR_IN(ia1->sa).sin_addr.s_addr == - SOUP_SOCKADDR_IN(ia2->sa).sin_addr.s_addr) && - (SOUP_SOCKADDR_IN(ia1->sa).sin_port == - SOUP_SOCKADDR_IN(ia2->sa).sin_port)); -} - -/** - * soup_address_noport_equal: - * @p1: Pointer to first SoupAddress. - * @p2: Pointer to second SoupAddress. - * - * Compare two #SoupAddress's, but does not compare the port numbers. - * - * Returns: 1 if they are the same; 0 otherwise. - **/ -gint -soup_address_noport_equal (const gpointer p1, const gpointer p2) -{ - const SoupAddress* ia1 = (const SoupAddress*) p1; - const SoupAddress* ia2 = (const SoupAddress*) p2; - - g_assert (p1 != NULL && p2 != NULL); - - /* Note network byte order doesn't matter */ - return (SOUP_SOCKADDR_IN(ia1->sa).sin_addr.s_addr == - SOUP_SOCKADDR_IN(ia2->sa).sin_addr.s_addr); -} - -/** - * soup_address_gethostaddr: - * - * Get the primary host's #SoupAddress. - * - * Returns: the #SoupAddress of the host; NULL if there was an error. - * The caller is responsible for deleting the returned #SoupAddress. - **/ -SoupAddress * -soup_address_gethostaddr (void) -{ - gchar* name; - struct sockaddr_in* sa_in, sa; - SoupAddress* ia = NULL; - - name = soup_address_gethostname (); - - if (name && soup_gethostbyname (name, &sa, NULL)) { - ia = g_new0 (SoupAddress, 1); - ia->name = g_strdup (name); - ia->ref_count = 1; - - sa_in = (struct sockaddr_in*) &ia->sa; - sa_in->sin_family = AF_INET; - sa_in->sin_port = 0; - memcpy (&sa_in->sin_addr, &sa.sin_addr, 4); - } - - return ia; -} - - -static void soup_socket_connect_tcp_cb (SoupSocket* socket, SoupSocketConnectStatus status, gpointer data) @@ -662,3 +403,299 @@ soup_socket_server_new (const gint port) g_free (s); return NULL; } + + +#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 { + gint sockfd; + SoupAddress *addr; + SoupSocketNewFn func; + gpointer data; + gint flags; + guint connect_watch; +} SoupSocketState; + +static gboolean +soup_socket_new_cb (GIOChannel* iochannel, + GIOCondition condition, + gpointer data) +{ + SoupSocketState* state = (SoupSocketState*) data; + SoupSocket* s; + gint error = 0; + gint len = sizeof (gint); + + /* 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; + + if (fcntl (state->sockfd, F_SETFL, state->flags) != 0) + goto ERROR; + + s = g_new0 (SoupSocket, 1); + s->ref_count = 1; + s->sockfd = state->sockfd; + s->addr = state->addr; + + (*state->func) (s, SOUP_SOCKET_NEW_STATUS_OK, state->data); + + g_free (state); + + return FALSE; + + ERROR: + soup_address_unref (state->addr); + (*state->func) (NULL, SOUP_SOCKET_NEW_STATUS_ERROR, state->data); + g_free (state); + + return FALSE; +} + +/** + * soup_socket_new: + * @addr: Address to connect to. + * @func: Callback function. + * @data: User data passed when callback function is called. + * + * 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. + * + * Returns: ID of the connection which can be used with + * soup_socket_connect_cancel() to cancel it; NULL on + * failure. + **/ +SoupSocketNewId +soup_socket_new (SoupAddress *addr, + SoupSocketNewFn func, + gpointer data) +{ + gint sockfd; + gint flags; + SoupSocketState* state; + GIOChannel *chan; + + g_return_val_if_fail(addr != NULL, NULL); + g_return_val_if_fail(func != NULL, NULL); + + /* Create socket */ + sockfd = socket (AF_INET, SOCK_STREAM, 0); + if (sockfd < 0) { + (func) (NULL, SOUP_SOCKET_NEW_STATUS_ERROR, data); + 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); + return NULL; + } + + if (fcntl (sockfd, F_SETFL, flags | O_NONBLOCK) == -1) { + (func) (NULL, SOUP_SOCKET_NEW_STATUS_ERROR, data); + return NULL; + } + + errno = 0; + + /* Connect (but non-blocking!) */ + if (connect (sockfd, &addr->sa, sizeof (addr->sa)) < 0 && + errno != EINPROGRESS) { + (func) (NULL, SOUP_SOCKET_NEW_STATUS_ERROR, data); + return NULL; + } + + /* Unref in soup_socket_new_cb if failure */ + soup_address_ref (addr); + + /* Connect succeeded, return immediately */ + if (!errno) { + SoupSocket *s = g_new0 (SoupSocket, 1); + s->ref_count = 1; + s->sockfd = sockfd; + s->addr = addr; + + (*func) (s, SOUP_SOCKET_NEW_STATUS_OK, data); + return NULL; + } + + chan = g_io_channel_unix_new (sockfd); + + /* Wait for the connection */ + state = g_new0 (SoupSocketState, 1); + state->sockfd = sockfd; + state->addr = addr; + 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; +} + +/** + * soup_socket_new_cancel: + * @id: ID of the connection. + * + * Cancel an asynchronous connection that was started with + * soup_socket_new(). + **/ +void +soup_socket_new_cancel (SoupSocketNewId id) +{ + SoupSocketState* state = (SoupSocketState*) id; + + g_source_remove (state->connect_watch); + soup_address_unref (state->addr); + g_free (state); +} + +/** + * soup_socket_server_accept: + * @socket: #SoupSocket to accept connections from. + * + * 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. + * + * Returns: a new #SoupSocket if there is another connect, or NULL if + * there's an error. + **/ +SoupSocket * +soup_socket_server_accept (SoupSocket *socket) +{ + gint sockfd; + gint flags; + struct sockaddr sa; + socklen_t n; + fd_set fdset; + SoupSocket* s; + + g_return_val_if_fail (socket != NULL, NULL); + + try_again: + FD_ZERO (&fdset); + FD_SET (socket->sockfd, &fdset); + + if (select (socket->sockfd + 1, &fdset, NULL, NULL, NULL) == -1) { + if (errno == EINTR) goto try_again; + return NULL; + } + + n = sizeof(s->addr->sa); + + if ((sockfd = accept (socket->sockfd, &sa, &n)) == -1) { + if (errno == EWOULDBLOCK || + errno == ECONNABORTED || +#ifdef EPROTO /* OpenBSD does not have EPROTO */ + errno == EPROTO || +#endif + errno == EINTR) + goto try_again; + + return NULL; + } + + /* Get the flags (should all be 0?) */ + flags = fcntl (sockfd, F_GETFL, 0); + if (flags == -1) return NULL; + + /* Make the socket non-blocking */ + if (fcntl (sockfd, F_SETFL, flags | O_NONBLOCK) == -1) + return NULL; + + s = g_new0 (SoupSocket, 1); + s->ref_count = 1; + s->sockfd = sockfd; + + s->addr = g_new0 (SoupAddress, 1); + s->addr->ref_count = 1; + memcpy (&s->addr->sa, &sa, sizeof (s->addr->sa)); + + return s; +} + +/** + * 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) +{ + gint sockfd; + gint flags; + struct sockaddr sa; + socklen_t n; + fd_set fdset; + SoupSocket* s; + struct timeval tv = {0, 0}; + + g_return_val_if_fail (socket != NULL, NULL); + + try_again: + FD_ZERO (&fdset); + FD_SET (socket->sockfd, &fdset); + + if (select (socket->sockfd + 1, &fdset, NULL, NULL, &tv) == -1) { + if (errno == EINTR) goto try_again; + return NULL; + } + + n = sizeof(sa); + + if ((sockfd = accept (socket->sockfd, &sa, &n)) == -1) { + /* If we get an error, return. We don't want to try again as we + do in soup_socket_server_accept() - it might cause a + block. */ + return NULL; + } + + /* Get the flags (should all be 0?) */ + flags = fcntl (sockfd, F_GETFL, 0); + if (flags == -1) return NULL; + + /* Make the socket non-blocking */ + if (fcntl (sockfd, F_SETFL, flags | O_NONBLOCK) == -1) + return NULL; + + s = g_new0 (SoupSocket, 1); + s->ref_count = 1; + s->sockfd = sockfd; + + s->addr = g_new0 (SoupAddress, 1); + s->addr->ref_count = 1; + memcpy (&s->addr->sa, &sa, sizeof (s->addr->sa)); + + return s; +} diff --git a/libsoup/soup-socket.h b/libsoup/soup-socket.h index e9d942c..da4bb82 100644 --- a/libsoup/soup-socket.h +++ b/libsoup/soup-socket.h @@ -15,75 +15,7 @@ #define SOUP_SOCKET_H 1 #include - -typedef struct _SoupAddress SoupAddress; - -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 gchar* name, - const gint port, - SoupAddressNewFn func, - gpointer data); - -void soup_address_new_cancel (SoupAddressNewId id); - -SoupAddress *soup_address_new_sync (const gchar *name, - const gint port); - -SoupAddress *soup_address_lookup_in_cache (const gchar *name, - const gint port); - -void soup_address_ref (SoupAddress* ia); - -void soup_address_unref (SoupAddress* ia); - -SoupAddress * soup_address_copy (SoupAddress* ia); - - -typedef gpointer SoupAddressGetNameId; - -typedef void (*SoupAddressGetNameFn) (SoupAddress *inetaddr, - SoupAddressStatus status, - const gchar *name, - gpointer user_data); - -SoupAddressGetNameId soup_address_get_name (SoupAddress* ia, - SoupAddressGetNameFn func, - gpointer data); - -void soup_address_get_name_cancel (SoupAddressGetNameId id); - -const gchar *soup_address_get_name_sync (SoupAddress *addr); - -gchar* soup_address_get_canonical_name (SoupAddress* ia); - -gint soup_address_get_port (const SoupAddress* ia); - -const struct sockaddr * - soup_address_get_sockaddr (SoupAddress *ia, - guint *addrlen); - -guint soup_address_hash (const gpointer p); - -gint soup_address_equal (const gpointer p1, - const gpointer p2); - -gint soup_address_noport_equal (const gpointer p1, - const gpointer p2); - -gchar* soup_address_gethostname (void); - -SoupAddress* soup_address_gethostaddr (void); - +#include typedef struct _SoupSocket SoupSocket; diff --git a/tests/.cvsignore b/tests/.cvsignore new file mode 100644 index 0000000..ee2b3d1 --- /dev/null +++ b/tests/.cvsignore @@ -0,0 +1,3 @@ +Makefile +Makefile.in +get diff --git a/tests/Makefile.am b/tests/Makefile.am new file mode 100644 index 0000000..8bc2102 --- /dev/null +++ b/tests/Makefile.am @@ -0,0 +1,9 @@ +noinst_PROGRAMS = get + +get_SOURCES = get.c + +INCLUDES = \ + -I$(top_srcdir) \ + $(GLIB_CFLAGS) + +get_LDADD = $(top_builddir)/libsoup/libsoup-2.0.la diff --git a/tests/get.c b/tests/get.c new file mode 100644 index 0000000..5d83d34 --- /dev/null +++ b/tests/get.c @@ -0,0 +1,35 @@ +#include +#include +#include + +int +main (int argc, char **argv) +{ + SoupContext *ctx; + SoupMessage *msg; + + if (argc != 2) { + fprintf (stderr, "Usage: %s URL\n", argv[0]); + exit (1); + } + + ctx = soup_context_get (argv[1]); + if (!ctx) { + fprintf (stderr, "Could not parse '%s' as a URL\n", argv[1]); + exit (1); + } + + msg = soup_message_new (ctx, SOUP_METHOD_GET); + soup_context_unref (ctx); + + soup_message_send (msg); + + printf ("%d %s\n", msg->errorcode, msg->errorphrase); + if (SOUP_ERROR_IS_SUCCESSFUL (msg->errorcode)) { + fwrite (msg->response.body, msg->response.length, 1, stdout); + printf ("\n"); + } + + soup_message_free (msg); + return 0; +} -- 2.7.4