+2003-09-09 Dan Winship <danw@ximian.com>
+
+ Beginnings of improved synchronous API support
+
+ * libsoup/soup-dns.c: Simplify this by making it not automatically
+ return the result: force the caller to poll. (This isn't really a
+ performance issue: the results should come back quickly anyway.)
+ Also, make the cache thread-safe.
+ (soup_dns_entry_from_name): Was soup_gethostbyname
+ (soup_dns_entry_from_addr): Was soup_gethostbyaddr
+ (soup_dns_entry_check_lookup): Used to poll to see if DNS is done
+ (soup_dns_entry_get_hostent): Gets the hostent from an entry (and
+ blocks if it's not resolved yet).
+
+ * libsoup/soup-address.c: Update for soup-dns changes.
+ (soup_address_new): Don't automatically start resolving the
+ hostname now, since we don't know if the caller is going to want
+ it resolved synchronously or asynchronously.
+ (soup_address_resolve_async): Renamed from soup_address_resolve.
+ (soup_address_resolve_sync): New routine to do blocking
+ synchronous DNS.
+
+ * libsoup/soup-socket.c (soup_socket_connect): Now returns a
+ status value directly when connecting synchronously.
+ (soup_socket_client_new_async, soup_socket_client_new_sync):
+ Separate async/sync client socket functions.
+ (soup_socket_get_iochannel): Made static since it was not used
+ outside soup-socket.
+
+ * libsoup/soup-connection.c (soup_connection_new,
+ soup_connection_new_proxy, soup_connection_new_tunnel): Just set
+ up the data, don't actually start connecting.
+ (soup_connection_connect_async, soup_connection_connect_sync): New
+ async and sync SoupConnection connecting routines.
+ (soup_connection_get_socket): Remove this since it wasn't being
+ used.
+
+ * libsoup/soup-session.c (final_finished): Run the queue since a
+ connection is now freed up.
+ (run_queue): Update for soup_connection_new* changes.
+
+ * libsoup/soup-misc.c (soup_substring_index): Remove, since it
+ wasn't being used any more.
+
+ * libsoup/soup-private.h: Remove some prototypes for functions
+ that no longer exist.
+
+ * libsoup/soup-uri.c (soup_uri_copy_root): New utility function
+ (copies the protocol, host, and port of a SoupUri).
+
+ * tests/auth-test.c:
+ * tests/get.c:
+ * tests/simple-proxy.c: belatedly update for soup-session change
+
+ * tests/revserver.c: Handle each new connection in its own thread,
+ using synchronous SoupSocket calls.
+
2003-09-05 Dan Winship <danw@ximian.com>
* libsoup/soup-session.c: Move a bunch of logic here from
char *name, *physical;
guint port;
- SoupDNSHandle lookup;
+ SoupDNSEntry *lookup;
guint idle_id;
};
g_free (addr->priv->physical);
if (addr->priv->lookup)
- soup_gethostby_cancel (addr->priv->lookup);
+ soup_dns_entry_cancel_lookup (addr->priv->lookup);
if (addr->priv->idle_id)
g_source_remove (addr->priv->idle_id);
* @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.
- *
- * The DNS lookup can be cancelled by destroying the address object.
+ * address may not be available right away; the caller can call
+ * soup_address_resolve_async() or soup_address_resolve_sync() to
+ * force a DNS resolution.
*
* Return value: a #SoupAddress
**/
addr->priv->name = g_strdup (name);
addr->priv->port = port;
- /* Start a lookup */
- soup_address_resolve (addr, NULL, NULL);
-
return addr;
}
return NULL;
if (!addr->priv->physical) {
- addr->priv->physical = soup_ntop (SOUP_ADDRESS_DATA (addr),
- SOUP_ADDRESS_FAMILY (addr));
+ addr->priv->physical =
+ soup_dns_ntop (SOUP_ADDRESS_DATA (addr),
+ SOUP_ADDRESS_FAMILY (addr));
}
return addr->priv->physical;
}
-
-static void
-got_addr (SoupDNSHandle handle, guint status, struct hostent *h, gpointer data)
+static guint
+update_address_from_entry (SoupAddress *addr, SoupDNSEntry *entry)
{
- SoupAddress *addr = data;
+ struct hostent *h;
- addr->priv->lookup = NULL;
+ h = soup_dns_entry_get_hostent (addr->priv->lookup);
- if (status == SOUP_STATUS_OK) {
- if (!SOUP_ADDRESS_FAMILY_IS_VALID (h->h_addrtype)) {
- status = SOUP_STATUS_CANT_RESOLVE;
- goto done;
- }
- if (SOUP_ADDRESS_FAMILY_DATA_SIZE (h->h_addrtype) != h->h_length) {
- status = SOUP_STATUS_MALFORMED;
- goto done;
- }
+ if (!addr->priv->name)
+ addr->priv->name = g_strdup (h->h_name);
+ if (!addr->priv->sockaddr &&
+ SOUP_ADDRESS_FAMILY_IS_VALID (h->h_addrtype) &&
+ SOUP_ADDRESS_FAMILY_DATA_SIZE (h->h_addrtype) == h->h_length) {
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);
}
- done:
- g_signal_emit (addr, signals[DNS_RESULT], 0, status);
-}
-
-static void
-got_name (SoupDNSHandle handle, guint status, struct hostent *h, gpointer data)
-{
- SoupAddress *addr = data;
-
- addr->priv->lookup = NULL;
-
- if (status == SOUP_STATUS_OK)
- addr->priv->name = g_strdup (h->h_name);
-
- g_signal_emit (addr, signals[DNS_RESULT], 0, status);
+ if (addr->priv->name && addr->priv->sockaddr)
+ return SOUP_STATUS_OK;
+ else
+ return SOUP_STATUS_CANT_RESOLVE;
}
static gboolean
-idle_dns_result (gpointer user_data)
+idle_check_lookup (gpointer user_data)
{
SoupAddress *addr = user_data;
+ guint status;
+ if (addr->priv->name && addr->priv->sockaddr) {
+ addr->priv->idle_id = 0;
+ g_signal_emit (addr, signals[DNS_RESULT], 0, SOUP_STATUS_OK);
+ return FALSE;
+ }
+
+ if (!soup_dns_entry_check_lookup (addr->priv->lookup))
+ return TRUE;
+
+ status = update_address_from_entry (addr, addr->priv->lookup);
+ addr->priv->lookup = NULL;
addr->priv->idle_id = 0;
- g_signal_emit (addr, signals[DNS_RESULT], 0, SOUP_STATUS_OK);
+
+ g_signal_emit (addr, signals[DNS_RESULT], 0, status);
return FALSE;
}
/**
- * soup_address_resolve:
+ * soup_address_resolve_async:
* @addr: a #SoupAddress
* @callback: callback to call with the result
* @user_data: data for @callback
*
- * 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
+ * Asynchronously resolves the missing half of @addr. (Its IP address
+ * if it was created with soup_address_new(), or its 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).
**/
void
-soup_address_resolve (SoupAddress *addr,
- SoupAddressCallback callback, gpointer user_data)
+soup_address_resolve_async (SoupAddress *addr,
+ SoupAddressCallback callback,
+ gpointer user_data)
{
g_return_if_fail (SOUP_IS_ADDRESS (addr));
G_CALLBACK (callback), user_data);
}
- if (addr->priv->lookup)
- return;
-
- if (addr->priv->name && addr->priv->sockaddr) {
- addr->priv->idle_id = g_idle_add (idle_dns_result, addr);
+ if (addr->priv->idle_id)
return;
- }
- if (addr->priv->name) {
+ if (!addr->priv->sockaddr) {
addr->priv->lookup =
- soup_gethostbyname (addr->priv->name,
- got_addr, addr);
- } else {
+ soup_dns_entry_from_name (addr->priv->name);
+ } else if (!addr->priv->name) {
addr->priv->lookup =
- soup_gethostbyaddr (SOUP_ADDRESS_DATA (addr),
- SOUP_ADDRESS_FAMILY (addr),
- got_name, addr);
+ soup_dns_entry_from_addr (SOUP_ADDRESS_DATA (addr),
+ SOUP_ADDRESS_FAMILY (addr));
+ }
+
+ addr->priv->idle_id = g_idle_add (idle_check_lookup, addr);
+}
+
+/**
+ * soup_address_resolve_sync:
+ * @addr: a #SoupAddress
+ *
+ * Synchronously resolves the missing half of @addr, as with
+ * soup_address_resolve_async().
+ *
+ * Return value: %SOUP_STATUS_OK or %SOUP_ERROR_CANT_RESOLVE
+ **/
+guint
+soup_address_resolve_sync (SoupAddress *addr)
+{
+ SoupDNSEntry *entry;
+
+ g_return_val_if_fail (SOUP_IS_ADDRESS (addr), SOUP_STATUS_MALFORMED);
+
+ if (addr->priv->name)
+ entry = soup_dns_entry_from_name (addr->priv->name);
+ else {
+ entry = soup_dns_entry_from_addr (SOUP_ADDRESS_DATA (addr),
+ SOUP_ADDRESS_FAMILY (addr));
}
+
+ return update_address_from_entry (addr, entry);
}
typedef void (*SoupAddressCallback) (SoupAddress *addr,
guint status,
gpointer data);
-void soup_address_resolve (SoupAddress *addr,
+void soup_address_resolve_async (SoupAddress *addr,
SoupAddressCallback cb,
gpointer data);
+guint soup_address_resolve_sync (SoupAddress *addr);
const char *soup_address_get_name (SoupAddress *addr);
struct sockaddr *soup_address_get_sockaddr (SoupAddress *addr,
struct SoupConnectionPrivate {
SoupSocket *socket;
- SoupUri *dest_uri;
- gboolean is_proxy;
+ SoupUri *proxy_uri, *dest_uri;
time_t last_used;
SoupMessage *cur_req;
{
SoupConnection *conn = SOUP_CONNECTION (object);
+ if (conn->priv->proxy_uri)
+ soup_uri_free (conn->priv->proxy_uri);
if (conn->priv->dest_uri)
soup_uri_free (conn->priv->dest_uri);
SOUP_MAKE_TYPE (soup_connection, SoupConnection, class_init, init, PARENT_TYPE)
-static void
-socket_disconnected (SoupSocket *sock, gpointer conn)
-{
- soup_connection_disconnect (conn);
-}
-
-static SoupConnection *
-connection_new (const SoupUri *uri, gboolean is_proxy,
- SoupSocketCallback connect_callback,
- SoupConnectionCallback user_callback,
- gpointer user_data)
+/**
+ * soup_connection_new:
+ * @uri: remote machine to connect to
+ *
+ * Creates a connection to @uri. You must call
+ * soup_connection_connect_async() or soup_connection_connect_sync()
+ * to connect it after creating it.
+ *
+ * Return value: the new connection (not yet ready for use).
+ **/
+SoupConnection *
+soup_connection_new (const SoupUri *uri)
{
SoupConnection *conn;
conn = g_object_new (SOUP_TYPE_CONNECTION, NULL);
- conn->priv->is_proxy = is_proxy;
-
- soup_signal_connect_once (conn, "connect_result",
- G_CALLBACK (user_callback), user_data);
+ conn->priv->dest_uri = soup_uri_copy_root (uri);
- conn->priv->socket = soup_socket_client_new (uri->host, uri->port,
- uri->protocol == SOUP_PROTOCOL_HTTPS,
- connect_callback, conn);
- g_signal_connect (conn->priv->socket, "disconnected",
- G_CALLBACK (socket_disconnected), conn);
return conn;
}
-static void
-socket_connected (SoupSocket *sock, guint status, gpointer conn)
-{
- g_signal_emit (conn, signals[CONNECT_RESULT], 0, status);
-}
-
/**
- * soup_connection_new:
- * @uri: remote machine to connect to
- * @callback: callback to call after connecting
- * @user_data: data for @callback
+ * soup_connection_new_proxy:
+ * @proxy_uri: proxy to connect to
*
- * Creates a connection to @uri. @callback will be called when the
- * connection completes (or fails).
+ * Creates a connection to @proxy_uri. As with soup_connection_new(),
+ * the returned object is not yet connected.
*
* Return value: the new connection (not yet ready for use).
**/
SoupConnection *
-soup_connection_new (const SoupUri *uri,
- SoupConnectionCallback callback, gpointer user_data)
+soup_connection_new_proxy (const SoupUri *proxy_uri)
{
- return connection_new (uri, FALSE, socket_connected,
- callback, user_data);
-}
+ SoupConnection *conn;
-static void
-proxy_socket_connected (SoupSocket *sock, guint status, gpointer conn)
-{
- if (status == SOUP_STATUS_CANT_RESOLVE)
- status = SOUP_STATUS_CANT_RESOLVE_PROXY;
- else if (status == SOUP_STATUS_CANT_CONNECT)
- status = SOUP_STATUS_CANT_CONNECT_PROXY;
+ conn = g_object_new (SOUP_TYPE_CONNECTION, NULL);
+ conn->priv->proxy_uri = soup_uri_copy_root (proxy_uri);
- g_signal_emit (conn, signals[CONNECT_RESULT], 0, status);
+ return conn;
}
/**
- * soup_connection_new_proxy:
+ * soup_connection_new_tunnel:
* @proxy_uri: proxy to connect to
- * @callback: callback to call after connecting
- * @user_data: data for @callback
+ * @dest_uri: remote machine to ask the proxy to connect to
*
- * Creates a connection to @proxy_uri. @callback will be called when
- * the connection completes (or fails).
+ * Creates a connection to @uri via @proxy_uri. As with
+ * soup_connection_new(), the returned object is not yet connected.
*
* Return value: the new connection (not yet ready for use).
**/
SoupConnection *
-soup_connection_new_proxy (const SoupUri *proxy_uri,
- SoupConnectionCallback callback,
- gpointer user_data)
+soup_connection_new_tunnel (const SoupUri *proxy_uri, const SoupUri *dest_uri)
{
- return connection_new (proxy_uri, TRUE, proxy_socket_connected,
- callback, user_data);
+ SoupConnection *conn;
+
+ conn = g_object_new (SOUP_TYPE_CONNECTION, NULL);
+ conn->priv->dest_uri = soup_uri_copy_root (dest_uri);
+ conn->priv->proxy_uri = soup_uri_copy_root (proxy_uri);
+
+ return conn;
}
+
static void
-tunnel_connected (SoupMessage *msg, gpointer user_data)
+socket_disconnected (SoupSocket *sock, gpointer conn)
{
- SoupConnection *conn = user_data;
+ soup_connection_disconnect (conn);
+}
- if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code))
- soup_socket_start_ssl (conn->priv->socket);
+static inline guint
+proxified_status (SoupConnection *conn, guint status)
+{
+ if (!conn->priv->proxy_uri)
+ return status;
- proxy_socket_connected (NULL, msg->status_code, conn);
- g_object_unref (msg);
+ if (status == SOUP_STATUS_CANT_RESOLVE)
+ return SOUP_STATUS_CANT_RESOLVE_PROXY;
+ else if (status == SOUP_STATUS_CANT_CONNECT)
+ return SOUP_STATUS_CANT_CONNECT_PROXY;
+ else
+ return status;
}
static void
-tunnel_failed (SoupMessage *msg, gpointer conn)
+tunnel_connect_finished (SoupMessage *msg, gpointer user_data)
{
+ SoupConnection *conn = user_data;
+
+ if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code))
+ soup_socket_start_ssl (conn->priv->socket);
+
g_signal_emit (conn, signals[CONNECT_RESULT], 0,
- SOUP_STATUS_CANT_CONNECT);
+ proxified_status (conn, msg->status_code));
g_object_unref (msg);
}
static void
-tunnel_socket_connected (SoupSocket *sock, guint status, gpointer user_data)
+socket_connect_result (SoupSocket *sock, guint status, gpointer user_data)
{
SoupConnection *conn = user_data;
- SoupMessage *connect_msg;
if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
- socket_connected (sock, status, conn);
+ g_signal_emit (conn, signals[CONNECT_RESULT], 0,
+ proxified_status (conn, status));
return;
}
- connect_msg = soup_message_new_from_uri (SOUP_METHOD_CONNECT,
- conn->priv->dest_uri);
- g_signal_connect (connect_msg, "read_body",
- G_CALLBACK (tunnel_connected), conn);
- g_signal_connect (connect_msg, "write_error",
- G_CALLBACK (tunnel_failed), conn);
- g_signal_connect (connect_msg, "read_error",
- G_CALLBACK (tunnel_failed), conn);
+ /* See if we need to tunnel */
+ if (conn->priv->proxy_uri && conn->priv->dest_uri) {
+ SoupMessage *connect_msg;
- soup_connection_send_request (conn, connect_msg);
+ connect_msg = soup_message_new_from_uri (SOUP_METHOD_CONNECT,
+ conn->priv->dest_uri);
+ g_signal_connect (connect_msg, "finished",
+ G_CALLBACK (tunnel_connect_finished), conn);
+
+ soup_connection_send_request (conn, connect_msg);
+ return;
+ }
+
+ g_signal_emit (conn, signals[CONNECT_RESULT], 0, status);
}
/**
- * soup_connection_new_tunnel:
- * @proxy_uri: proxy to connect to
- * @dest_uri: remote machine to ask the proxy to connect to
- * @callback: callback to call after connecting
+ * soup_connection_connect_async:
+ * @conn: the connection
+ * @ac: the async context to use
+ * @callback: callback to call when the connection succeeds or fails
* @user_data: data for @callback
*
- * Creates a connection to @uri via @proxy_uri. @callback will be
- * called when the connection completes (or fails).
+ * Asynchronously connects @conn.
+ **/
+void
+soup_connection_connect_async (SoupConnection *conn,
+ SoupConnectionCallback callback,
+ gpointer user_data)
+{
+ const SoupUri *uri;
+
+ g_return_if_fail (SOUP_IS_CONNECTION (conn));
+ g_return_if_fail (conn->priv->socket == NULL);
+
+ if (callback) {
+ soup_signal_connect_once (conn, "connect_result",
+ G_CALLBACK (callback), user_data);
+ }
+
+ if (conn->priv->proxy_uri)
+ uri = conn->priv->proxy_uri;
+ else
+ uri = conn->priv->dest_uri;
+
+ conn->priv->socket =
+ soup_socket_client_new_async (uri->host, uri->port,
+ uri->protocol == SOUP_PROTOCOL_HTTPS,
+ socket_connect_result, conn);
+ g_signal_connect (conn->priv->socket, "disconnected",
+ G_CALLBACK (socket_disconnected), conn);
+}
+
+/**
+ * soup_connection_connect_sync:
+ * @conn: the connection
+ *
+ * Synchronously connects @conn.
*
- * Return value: the new connection (not yet ready for use).
+ * Return value: the soup status
**/
-SoupConnection *
-soup_connection_new_tunnel (const SoupUri *proxy_uri, const SoupUri *dest_uri,
- SoupConnectionCallback callback,
- gpointer user_data)
+guint
+soup_connection_connect_sync (SoupConnection *conn)
{
- SoupConnection *conn;
+ const SoupUri *uri;
+ guint status;
+
+ g_return_val_if_fail (SOUP_IS_CONNECTION (conn), SOUP_STATUS_MALFORMED);
+ g_return_val_if_fail (conn->priv->socket == NULL, SOUP_STATUS_MALFORMED);
+
+ if (conn->priv->proxy_uri)
+ uri = conn->priv->proxy_uri;
+ else
+ uri = conn->priv->dest_uri;
+
+ conn->priv->socket =
+ soup_socket_client_new_sync (uri->host, uri->port,
+ uri->protocol == SOUP_PROTOCOL_HTTPS,
+ &status);
+
+ if (SOUP_STATUS_IS_SUCCESSFUL (status) &&
+ conn->priv->proxy_uri && conn->priv->dest_uri) {
+ SoupMessage *connect_msg;
+
+ connect_msg = soup_message_new_from_uri (SOUP_METHOD_CONNECT,
+ conn->priv->dest_uri);
+ soup_connection_send_request (conn, connect_msg);
+ status = connect_msg->status_code;
+ g_object_unref (connect_msg);
+ }
- conn = connection_new (proxy_uri, TRUE, tunnel_socket_connected,
- callback, user_data);
- conn->priv->dest_uri = soup_uri_copy (dest_uri);
- return conn;
+ if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
+ if (conn->priv->socket)
+ g_object_unref (conn->priv->socket);
+ conn->priv->socket = NULL;
+ }
+
+ return proxified_status (conn, status);
}
+
/**
* soup_connection_disconnect:
* @conn: a connection
return conn->priv->socket != NULL;
}
-/**
- * soup_connection_get_socket:
- * @conn: a #SoupConnection.
- *
- * Return value: @conn's socket
- */
-SoupSocket *
-soup_connection_get_socket (SoupConnection *conn)
-{
- g_return_val_if_fail (SOUP_IS_CONNECTION (conn), NULL);
-
- return conn->priv->socket;
-}
-
/**
* soup_connection_is_in_use:
g_signal_connect (req, "finished", G_CALLBACK (request_done), conn);
soup_message_send_request (req, conn->priv->socket,
- conn->priv->is_proxy);
+ conn->priv->proxy_uri != NULL);
}
GType soup_connection_get_type (void);
-typedef void (*SoupConnectionCallback) (SoupConnection *sock,
- guint status,
- gpointer data);
+typedef void (*SoupConnectionCallback) (SoupConnection *sock,
+ guint status,
+ gpointer data);
-SoupConnection *soup_connection_new (const SoupUri *uri,
- SoupConnectionCallback,
- gpointer data);
-SoupConnection *soup_connection_new_proxy (const SoupUri *proxy_uri,
- SoupConnectionCallback,
- gpointer data);
-SoupConnection *soup_connection_new_tunnel (const SoupUri *proxy_uri,
- const SoupUri *dest_uri,
- SoupConnectionCallback,
- gpointer data);
+SoupConnection *soup_connection_new (const SoupUri *uri);
+SoupConnection *soup_connection_new_proxy (const SoupUri *proxy_uri);
+SoupConnection *soup_connection_new_tunnel (const SoupUri *proxy_uri,
+ const SoupUri *dest_uri);
-gboolean soup_connection_is_proxy (SoupConnection *conn);
-
-void soup_connection_disconnect (SoupConnection *conn);
-gboolean soup_connection_is_connected (SoupConnection *conn);
+void soup_connection_connect_async (SoupConnection *conn,
+ SoupConnectionCallback,
+ gpointer user_data);
+guint soup_connection_connect_sync (SoupConnection *conn);
-SoupSocket *soup_connection_get_socket (SoupConnection *conn);
+void soup_connection_disconnect (SoupConnection *conn);
+gboolean soup_connection_is_connected (SoupConnection *conn);
-gboolean soup_connection_is_new (SoupConnection *conn);
-gboolean soup_connection_is_in_use (SoupConnection *conn);
-time_t soup_connection_last_used (SoupConnection *conn);
+gboolean soup_connection_is_new (SoupConnection *conn);
+gboolean soup_connection_is_in_use (SoupConnection *conn);
+time_t soup_connection_last_used (SoupConnection *conn);
-void soup_connection_send_request (SoupConnection *conn,
- SoupMessage *req);
+void soup_connection_send_request (SoupConnection *conn,
+ SoupMessage *req);
#endif /* SOUP_CONNECTION_H */
#include <string.h>
#include <time.h>
#include <unistd.h>
+#include <sys/select.h>
#include <sys/types.h>
-#include <sys/uio.h>
#include <sys/wait.h>
#include <arpa/inet.h>
h->h_length, h->h_addr_list[0]);
}
-static void
-free_hostent (struct hostent *h)
+void
+soup_dns_free_hostent (struct hostent *h)
{
g_free (h->h_name);
g_free (h->h_addr_list[0]);
}
char *
-soup_ntop (gconstpointer addr, int family)
+soup_dns_ntop (gconstpointer addr, int family)
{
switch (family) {
case AF_INET:
}
-/* Testing Defines */
-/* #undef HAVE_GETHOSTBYNAME_R_GLIBC */
-/* #define HAVE_GETHOSTBYNAME_R_GLIB_MUTEX */
-
-#ifdef HAVE_GETHOSTBYNAME_R_GLIB_MUTEX
-G_LOCK_DEFINE (gethostbyname);
-#endif
-
static struct hostent *
soup_gethostbyname_internal (const char *hostname)
{
}
#else
{
-#if defined(HAVE_GETHOSTBYNAME_R_GLIB_MUTEX)
- G_LOCK (gethostbyname);
-#endif
result = gethostbyname (hostname);
}
#endif
if (buf)
g_free (buf);
-#if defined(HAVE_GETHOSTBYNAME_R_GLIB_MUTEX)
- G_UNLOCK (gethostbyname);
-#endif
return out;
}
static struct hostent *
-soup_gethostbyaddr_internal (gpointer addr, int family)
+soup_gethostbyaddr_internal (gconstpointer addr, int family)
{
struct hostent result_buf, *result = &result_buf, *out;
char *buf = NULL;
}
#else
{
-#if defined(HAVE_GETHOSTBYNAME_R_GLIB_MUTEX)
- G_LOCK (gethostbyname);
-#endif
result = gethostbyaddr (addr, length, family);
}
#endif
if (buf)
g_free (buf);
-#if defined(HAVE_GETHOSTBYNAME_R_GLIB_MUTEX)
- G_UNLOCK (gethostbyname);
-#endif
return out;
}
/* Cache */
-typedef struct {
+struct SoupDNSEntry {
char *name;
struct hostent *h;
- time_t expires;
+ gboolean resolved;
- GSList *lookups;
+ time_t expires;
+ guint ref_count;
- guint source_id;
pid_t lookup_pid;
int fd;
-} SoupDNSEntry;
+};
static GHashTable *soup_dns_entries;
+
#define SOUP_DNS_ENTRIES_MAX 20
+static GStaticMutex soup_dns_mutex = G_STATIC_MUTEX_INIT;
+#define soup_dns_lock() g_static_mutex_lock (&soup_dns_mutex)
+#define soup_dns_unlock() g_static_mutex_unlock (&soup_dns_mutex)
+
+static void
+soup_dns_entry_ref (SoupDNSEntry *entry)
+{
+ entry->ref_count++;
+}
+
+static void
+soup_dns_entry_unref (SoupDNSEntry *entry)
+{
+ if (!--entry->ref_count) {
+ g_free (entry->name);
+ soup_dns_free_hostent (entry->h);
+
+ if (entry->fd)
+ close (entry->fd);
+ if (entry->lookup_pid) {
+ kill (entry->lookup_pid, SIGKILL);
+ waitpid (entry->lookup_pid, NULL, 0);
+ }
+
+ g_free (entry);
+ }
+}
+
static void
-free_entry (SoupDNSEntry *entry)
+uncache_entry (SoupDNSEntry *entry)
{
g_hash_table_remove (soup_dns_entries, entry->name);
- g_free (entry->name);
- free_hostent (entry->h);
- g_free (entry);
+ soup_dns_entry_unref (entry);
}
static void
{
SoupDNSEntry *entry = value, **prune_entry = data;
- if (entry->lookups)
- return;
if (!*prune_entry || (*prune_entry)->expires > entry->expires)
*prune_entry = entry;
}
-static void
-cache_entry (SoupDNSEntry *entry)
+static SoupDNSEntry *
+soup_dns_entry_new (const char *name)
{
+ SoupDNSEntry *entry;
+
+ entry = g_new0 (SoupDNSEntry, 1);
+ entry->name = g_strdup (name);
+ entry->ref_count = 2; /* One for the caller, one for the cache */
+
if (!soup_dns_entries) {
soup_dns_entries = g_hash_table_new (soup_str_case_hash,
soup_str_case_equal);
g_hash_table_foreach (soup_dns_entries, prune_cache_cb,
&prune_entry);
if (prune_entry)
- free_entry (prune_entry);
+ uncache_entry (prune_entry);
}
entry->expires = time (0) + 60 * 60;
g_hash_table_insert (soup_dns_entries, entry->name, entry);
+
+ return entry;
}
static SoupDNSEntry *
-lookup_entry (const char *name)
+soup_dns_lookup_entry (const char *name)
{
+ SoupDNSEntry *entry;
+
if (!soup_dns_entries)
return NULL;
- return g_hash_table_lookup (soup_dns_entries, name);
-}
-
-
-typedef struct {
- SoupGetHostByFn func;
- gpointer data;
-
- SoupDNSEntry *entry;
-} SoupDNSLookupInfo;
-
-static gboolean
-soup_gothost (gpointer user_data)
-{
- SoupDNSEntry *entry = user_data;
- SoupDNSLookupInfo *info;
-
- if (entry->source_id) {
- g_source_remove (entry->source_id);
- entry->source_id = 0;
- }
-
- while (entry->lookups) {
- info = entry->lookups->data;
- entry->lookups = g_slist_remove (entry->lookups, info);
-
- (*info->func) (info, entry->h ? SOUP_STATUS_OK : SOUP_STATUS_CANT_RESOLVE, entry->h, info->data);
- g_free (info);
- }
-
- return FALSE;
-}
-
-static gboolean
-soup_gethostby_cb (GIOChannel *iochannel,
- GIOCondition condition,
- gpointer data)
-{
- SoupDNSEntry *entry = data;
- char buf[256], *namelenp, *name, *typep, *addrlenp, *addr;
- int nread;
-
- if (condition & G_IO_IN)
- nread = read (entry->fd, buf, sizeof (buf));
- else
- nread = 0;
-
- close (entry->fd);
- entry->fd = -1;
- kill (entry->lookup_pid, SIGKILL);
- waitpid (entry->lookup_pid, NULL, 0);
- entry->lookup_pid = 0;
-
- if (nread < 1)
- return soup_gothost (entry);
-
- namelenp = buf;
- name = namelenp + 1;
- typep = name + *namelenp;
- addrlenp = typep + 1;
- addr = addrlenp + 1;
- if (addrlenp < buf + nread && (addr + *addrlenp) == buf + nread)
- entry->h = new_hostent (name, *typep, *addrlenp, addr);
- return soup_gothost (entry);
-}
-
-static SoupDNSLookupInfo *
-lookup_info (SoupDNSEntry *entry, SoupGetHostByFn func, gpointer data)
-{
- SoupDNSLookupInfo *info;
-
- info = g_new0 (SoupDNSLookupInfo, 1);
- info->func = func;
- info->data = data;
- info->entry = entry;
- entry->lookups = g_slist_prepend (entry->lookups, info);
- if (!entry->source_id)
- entry->source_id = g_idle_add (soup_gothost, entry);
-
- return info;
+ entry = g_hash_table_lookup (soup_dns_entries, name);
+ if (entry)
+ soup_dns_entry_ref (entry);
+ return entry;
}
/**
- * soup_gethostbyname:
+ * soup_dns_entry_from_name:
* @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 @func is called.
*
- * Resolves a DNS name asynchronously. @func will be called with the
- * result (or an error).
+ * Begins asynchronous resolution of @name. The caller should
+ * periodically call soup_entry_check_lookup() to see if it is done,
+ * and call soup_entry_get_hostent() when soup_entry_check_lookup()
+ * returns %TRUE.
*
- * Currently this routine forks and does the lookup, which can cause
+ * 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_gethostbyname_cancel() to cancel it.
+ * Returns: a #SoupDNSEntry, which will be freed when you call
+ * soup_entry_get_hostent() or soup_entry_cancel_lookup().
**/
-SoupDNSHandle
-soup_gethostbyname (const char *name, SoupGetHostByFn func, gpointer data)
+SoupDNSEntry *
+soup_dns_entry_from_name (const char *name)
{
SoupDNSEntry *entry;
int pipes[2];
- GIOChannel *chan;
+
+ soup_dns_lock ();
/* Try the cache */
- entry = lookup_entry (name);
+ entry = soup_dns_lookup_entry (name);
if (entry) {
- if (entry->expires < time (0) && !entry->source_id)
- free_entry (entry);
- else
- return lookup_info (entry, func, data);
+ soup_dns_unlock ();
+ return entry;
}
- entry = g_new0 (SoupDNSEntry, 1);
- entry->name = g_strdup (name);
- cache_entry (entry);
+ entry = soup_dns_entry_new (name);
/* Try to read the name as if it were dotted decimal */
entry->h = new_hostent_from_phys (name);
- if (entry->h)
- return lookup_info (entry, func, data);
+ if (entry->h) {
+ entry->resolved = TRUE;
+ soup_dns_unlock ();
+ return entry;
+ }
/* Check to see if we are doing synchronous DNS lookups */
if (getenv ("SOUP_SYNC_DNS")) {
entry->h = soup_gethostbyname_internal (name);
- return lookup_info (entry, func, data);
+ entry->resolved = TRUE;
+ soup_dns_unlock ();
+ return entry;
}
/* Ok, we need to start a new lookup */
- if (pipe (pipes) == -1)
- return lookup_info (entry, func, data);
+ if (pipe (pipes) == -1) {
+ entry->resolved = TRUE;
+ soup_dns_unlock ();
+ return entry;
+ }
entry->lookup_pid = fork ();
switch (entry->lookup_pid) {
close (pipes[0]);
close (pipes[1]);
- return lookup_info (entry, func, data);
+ entry->resolved = TRUE;
+ soup_dns_unlock ();
+ return entry;
case 0:
/* Child */
close (pipes[1]);
entry->fd = pipes[0];
-
- /* Set up a watch to read from the pipe */
- chan = g_io_channel_unix_new (pipes[0]);
- entry->source_id =
- g_io_add_watch (
- chan,
- G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
- soup_gethostby_cb,
- entry);
- g_io_channel_unref (chan);
-
- return lookup_info (entry, func, data);
+ soup_dns_unlock ();
+ return entry;
}
}
-SoupDNSHandle
-soup_gethostbyaddr (gpointer addr, int family,
- SoupGetHostByFn func, gpointer data)
+/**
+ * soup_dns_entry_from_addr:
+ * @addr: pointer to address data (eg, an #in_addr_t)
+ * @family: address family of @addr
+ *
+ * Begins asynchronous resolution of @addr. The caller should
+ * periodically call soup_entry_check_lookup() to see if it is done,
+ * and call soup_entry_get_hostent() when soup_entry_check_lookup()
+ * returns %TRUE.
+ *
+ * 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.
+ *
+ * Returns: a #SoupDNSEntry, which will be freed when you call
+ * soup_entry_get_hostent() or soup_entry_cancel_lookup().
+ **/
+SoupDNSEntry *
+soup_dns_entry_from_addr (gconstpointer addr, int family)
{
SoupDNSEntry *entry;
int pipes[2];
- GIOChannel *chan;
char *name;
- name = soup_ntop (addr, family);
+ name = soup_dns_ntop (addr, family);
g_return_val_if_fail (name != NULL, NULL);
+ soup_dns_lock ();
+
/* Try the cache */
- entry = lookup_entry (name);
+ entry = soup_dns_lookup_entry (name);
if (entry) {
- if (entry->expires > time (0))
- free_entry (entry);
- else {
- g_free (name);
- return lookup_info (entry, func, data);
- }
+ g_free (name);
+ soup_dns_unlock ();
+ return entry;
}
- entry = g_new0 (SoupDNSEntry, 1);
- entry->name = name;
- cache_entry (entry);
+ entry = soup_dns_entry_new (name);
/* Check to see if we are doing synchronous DNS lookups */
if (getenv ("SOUP_SYNC_DNS")) {
entry->h = soup_gethostbyaddr_internal (addr, family);
- return lookup_info (entry, func, data);
+ entry->resolved = TRUE;
+ soup_dns_unlock ();
+ return entry;
}
- if (pipe (pipes) != 0)
- return lookup_info (entry, func, data);
+ if (pipe (pipes) != 0) {
+ entry->resolved = TRUE;
+ soup_dns_unlock ();
+ return entry;
+ }
entry->lookup_pid = fork ();
switch (entry->lookup_pid) {
close (pipes[1]);
g_warning ("Fork error: %s (%d)\n", g_strerror(errno), errno);
- return lookup_info (entry, func, data);
+ entry->resolved = TRUE;
+ soup_dns_unlock ();
+ return entry;
case 0:
/* Child */
close (pipes[1]);
entry->fd = pipes[0];
-
- /* Set up a watch to read from the pipe */
- chan = g_io_channel_unix_new (pipes[0]);
- entry->source_id =
- g_io_add_watch (
- chan,
- G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
- soup_gethostby_cb,
- entry);
- g_io_channel_unref (chan);
-
- return lookup_info (entry, func, data);
+ soup_dns_unlock ();
+ return entry;
}
}
-void
-soup_gethostby_cancel (SoupDNSHandle id)
+static void
+check_hostent (SoupDNSEntry *entry, gboolean block)
{
- SoupDNSLookupInfo *info = id;
- SoupDNSEntry *entry = info->entry;
+ char buf[256], *namelenp, *name, *typep, *addrlenp, *addr;
+ int nread;
+ fd_set readfds;
+ struct timeval tv = { 0, 0 };
- entry->lookups = g_slist_remove (entry->lookups, info);
- g_free (info);
+ soup_dns_lock ();
- if (!entry->lookups && entry->source_id) {
- g_source_remove (entry->source_id);
- if (entry->lookup_pid) {
- close (entry->fd);
- kill (entry->lookup_pid, SIGKILL);
- waitpid (entry->lookup_pid, NULL, 0);
- free_entry (entry);
- }
+ if (entry->resolved || !entry->fd) {
+ soup_dns_unlock ();
+ return;
+ }
+
+ FD_ZERO (&readfds);
+ FD_SET (entry->fd, &readfds);
+ if (select (entry->fd + 1, &readfds, NULL, NULL, &tv) != 0) {
+ soup_dns_unlock ();
+ return;
+ }
+
+ nread = read (entry->fd, buf, sizeof (buf));
+ close (entry->fd);
+ entry->fd = -1;
+ kill (entry->lookup_pid, SIGKILL);
+ waitpid (entry->lookup_pid, NULL, 0);
+ entry->lookup_pid = 0;
+ entry->resolved = TRUE;
+
+ if (nread < 1) {
+ soup_dns_unlock ();
+ return;
}
+
+ namelenp = buf;
+ name = namelenp + 1;
+ typep = name + *namelenp;
+ addrlenp = typep + 1;
+ addr = addrlenp + 1;
+
+ if (addrlenp < buf + nread && (addr + *addrlenp) == buf + nread)
+ entry->h = new_hostent (name, *typep, *addrlenp, addr);
+ soup_dns_unlock ();
+}
+
+gboolean
+soup_dns_entry_check_lookup (SoupDNSEntry *entry)
+{
+ check_hostent (entry, FALSE);
+ return entry->resolved;
+}
+
+struct hostent *
+soup_dns_entry_get_hostent (SoupDNSEntry *entry)
+{
+ struct hostent *h;
+
+ check_hostent (entry, TRUE);
+ h = copy_hostent (entry->h);
+ soup_dns_entry_unref (entry);
+
+ return h;
+}
+
+void
+soup_dns_entry_cancel_lookup (SoupDNSEntry *entry)
+{
+ soup_dns_entry_unref (entry);
}
#include <sys/socket.h>
#include <netdb.h>
-typedef gpointer SoupDNSHandle;
-typedef void (*SoupGetHostByFn) (SoupDNSHandle handle,
- guint status,
- struct hostent *h,
- gpointer user_data);
+typedef struct SoupDNSEntry SoupDNSEntry;
-SoupDNSHandle soup_gethostbyname (const char *name,
- SoupGetHostByFn func,
- gpointer data);
+SoupDNSEntry *soup_dns_entry_from_name (const char *name);
+SoupDNSEntry *soup_dns_entry_from_addr (gconstpointer addr,
+ int family);
-SoupDNSHandle soup_gethostbyaddr (gpointer addr,
- int family,
- SoupGetHostByFn func,
- gpointer data);
+gboolean soup_dns_entry_check_lookup (SoupDNSEntry *entry);
+void soup_dns_entry_cancel_lookup (SoupDNSEntry *entry);
-void soup_gethostby_cancel (SoupDNSHandle id);
+struct hostent *soup_dns_entry_get_hostent (SoupDNSEntry *entry);
+void soup_dns_free_hostent (struct hostent *h);
-char *soup_ntop (gconstpointer addr,
- int family);
+char *soup_dns_ntop (gconstpointer addr,
+ int family);
#endif /* SOUP_DNS_H */
return g_strcasecmp (string1, string2) == 0;
}
-gint
-soup_substring_index (gchar *str, gint len, gchar *substr)
-{
- int i, sublen = strlen (substr);
-
- for (i = 0; i <= len - sublen; ++i)
- if (str[i] == substr[0])
- if (memcmp (&str[i], substr, sublen) == 0)
- return i;
-
- return -1;
-}
-
/* Base64 utils (straight from camel-mime-utils.c) */
#define d(x)
#include <libsoup/soup-auth.h>
#include <libsoup/soup-misc.h>
-#ifdef __cplusplus
-extern "C" {
-#endif
-
#define RESPONSE_BLOCK_SIZE 8192
extern gboolean soup_initialized;
#define soup_sockaddr_max sockaddr_in
#endif
-/* from soup-context.c */
-
-SoupAuth *soup_context_lookup_auth (SoupContext *ctx,
- SoupMessage *msg);
-
-gboolean soup_context_update_auth (SoupContext *ctx,
- SoupMessage *msg);
-
-gboolean soup_context_authenticate_auth (SoupContext *ctx,
- SoupAuth *auth);
-
-void soup_context_invalidate_auth (SoupContext *ctx,
- SoupAuth *auth);
-
/* from soup-misc.c */
guint soup_str_case_hash (gconstpointer key);
gboolean soup_str_case_equal (gconstpointer v1,
gconstpointer v2);
-gint soup_substring_index (gchar *str,
- gint len,
- gchar *substr);
-
-
#define SOUP_MAKE_TYPE(l,t,ci,i,parent) \
GType l##_get_type(void)\
{\
return type; \
}
-#ifdef __cplusplus
-}
-#endif
-
#endif /*SOUP_PRIVATE_H*/
SOUP_MAKE_TYPE (soup_session, SoupSession, class_init, init, PARENT_TYPE)
SoupSession *
-soup_session_new (void)
+soup_session_new_default (void)
{
return g_object_new (SOUP_TYPE_SESSION, NULL);
}
{
SoupSession *session;
- session = soup_session_new ();
+ session = soup_session_new_default ();
if (proxy_uri)
session->priv->proxy_uri = soup_uri_copy (proxy_uri);
SoupSession *
soup_session_new_full (const SoupUri *proxy_uri,
- guint max_conns, guint max_per_host)
+ guint max_conns, guint max_conns_per_host)
{
SoupSession *session;
return host;
host = g_new0 (SoupSessionHost, 1);
- host->root_uri = g_new0 (SoupUri, 1);
- host->root_uri->protocol = source->protocol;
- host->root_uri->host = g_strdup (source->host);
- host->root_uri->port = source->port;
+ host->root_uri = soup_uri_copy_root (source);
g_hash_table_insert (session->priv->hosts, host->root_uri, host);
return host;
g_signal_handlers_disconnect_by_func (req, request_finished, session);
g_signal_handlers_disconnect_by_func (req, final_finished, session);
g_object_unref (req);
+
+ run_queue (session, FALSE);
}
}
if (session->priv->proxy_uri &&
host->root_uri->protocol == SOUP_PROTOCOL_HTTPS) {
conn = soup_connection_new_tunnel (
- session->priv->proxy_uri, host->root_uri,
- got_connection, session);
+ session->priv->proxy_uri, host->root_uri);
} else if (session->priv->proxy_uri) {
conn = soup_connection_new_proxy (
- session->priv->proxy_uri,
- got_connection, session);
+ session->priv->proxy_uri);
} else {
- conn = soup_connection_new (host->root_uri,
- got_connection, session);
+ conn = soup_connection_new (host->root_uri);
}
+ soup_connection_connect_async (conn, got_connection, session);
g_signal_connect (conn, "disconnected",
G_CALLBACK (connection_closed), session);
g_hash_table_insert (session->priv->conns, conn, host);
GIOChannel *iochannel;
guint watch;
- gboolean server, ssl;
+ guint flags;
guint read_tag, write_tag, error_tag;
GByteArray *read_buf;
};
+#define SOUP_SOCKET_FLAG_SSL (1<<8)
+
+#define SOUP_SOCKET_SET_FLAG(sock, flag) (sock)->priv->flags |= (flag)
+#define SOUP_SOCKET_CLEAR_FLAG(sock, flag) (sock)->priv->flags &= ~(flag)
+#define SOUP_SOCKET_CHECK_FLAG(sock, flag) ((sock)->priv->flags & (flag))
+
static void
init (GObject *object)
{
return g_object_new (SOUP_TYPE_SOCKET, NULL);
}
-void
-soup_socket_set_flag (SoupSocket *sock, SoupSocketFlag flag, gboolean value)
+static void
+update_fdflags (SoupSocket *sock, guint mask)
{
- int fdflags, opt;
+ int flags, opt;
+
+ if (mask & SOUP_SOCKET_FLAG_NONBLOCKING) {
+ flags = fcntl (sock->priv->sockfd, F_GETFL, 0);
+ g_return_if_fail (flags != -1);
+
+ if (sock->priv->flags & SOUP_SOCKET_FLAG_NONBLOCKING)
+ flags |= O_NONBLOCK;
+ else
+ flags &= ~O_NONBLOCK;
+ fcntl (sock->priv->sockfd, F_SETFL, flags);
+ }
+ if (mask & SOUP_SOCKET_FLAG_NODELAY) {
+ opt = (sock->priv->flags & SOUP_SOCKET_FLAG_NODELAY) != 0;
+ setsockopt (sock->priv->sockfd, IPPROTO_TCP,
+ TCP_NODELAY, &opt, sizeof (opt));
+ }
+ if (mask & SOUP_SOCKET_FLAG_REUSEADDR) {
+ opt = (sock->priv->flags & SOUP_SOCKET_FLAG_REUSEADDR) != 0;
+ setsockopt (sock->priv->sockfd, SOL_SOCKET,
+ SO_REUSEADDR, &opt, sizeof (opt));
+ }
+}
+void
+soup_socket_set_flags (SoupSocket *sock, guint mask, guint flags)
+{
g_return_if_fail (SOUP_IS_SOCKET (sock));
- g_return_if_fail (sock->priv->sockfd != -1);
- switch (flag) {
- case SOUP_SOCKET_FLAG_NONBLOCKING:
- fdflags = fcntl (sock->priv->sockfd, F_GETFL, 0);
- g_return_if_fail (fdflags != -1);
+ sock->priv->flags |= mask & flags;
+ sock->priv->flags &= ~(mask & ~flags);
- if (value)
- fdflags |= O_NONBLOCK;
- else
- fdflags &= ~O_NONBLOCK;
-
- fcntl (sock->priv->sockfd, F_SETFL, fdflags);
- break;
-
- case SOUP_SOCKET_FLAG_NODELAY:
- opt = (value != FALSE);
- setsockopt (sock->priv->sockfd, IPPROTO_TCP, TCP_NODELAY,
- &opt, sizeof (opt));
- break;
-
- case SOUP_SOCKET_FLAG_REUSEADDR:
- opt = (value != FALSE);
- setsockopt (sock->priv->sockfd, SOL_SOCKET, SO_REUSEADDR,
- &opt, sizeof (opt));
- break;
+ if (sock->priv->sockfd)
+ update_fdflags (sock, mask);
+}
+
+static GIOChannel *
+get_iochannel (SoupSocket *sock)
+{
+ 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);
+ g_io_channel_set_encoding (sock->priv->iochannel, NULL, NULL);
+ g_io_channel_set_buffered (sock->priv->iochannel, FALSE);
}
+ return sock->priv->iochannel;
}
static gboolean
if (error)
goto cant_connect;
- if (sock->priv->ssl)
+ if (SOUP_SOCKET_CHECK_FLAG (sock, SOUP_SOCKET_FLAG_SSL))
soup_socket_start_ssl (sock);
g_signal_emit (sock, signals[CONNECT_RESULT], 0, SOUP_STATUS_OK);
return;
}
- soup_socket_connect (sock, addr);
+ soup_socket_connect (sock, sock->priv->remote_addr);
/* soup_socket_connect re-reffed addr */
g_object_unref (addr);
}
/**
* soup_socket_connect:
- * @sock: a #SoupSocket (which must not be connected or listening)
+ * @sock: a client #SoupSocket (which must not already be connected)
* @remote_addr: address to connect to
*
- * 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).
+ * If %SOUP_SOCKET_FLAG_NONBLOCKING has been set on the socket, this
+ * begins asynchronously connecting to the given address. The socket
+ * will emit %connect_result when it succeeds or fails (but not before
+ * returning from this function).
+ *
+ * If %SOUP_SOCKET_FLAG_NONBLOCKING has not been set, this will
+ * attempt to synchronously connect.
+ *
+ * Return value: %SOUP_STATUS_CONTINUE if connecting asynchronously,
+ * otherwise a success or failure code.
**/
-void
+guint
soup_socket_connect (SoupSocket *sock, SoupAddress *remote_addr)
{
- struct sockaddr *sa = NULL;
+ struct sockaddr *sa;
int len, status;
+ gboolean sync;
- 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);
+ g_return_val_if_fail (SOUP_IS_SOCKET (sock), SOUP_STATUS_MALFORMED);
+ g_return_val_if_fail (!SOUP_SOCKET_CHECK_FLAG (sock, SOUP_SOCKET_FLAG_SERVER), FALSE);
+ g_return_val_if_fail (sock->priv->sockfd == -1, SOUP_STATUS_MALFORMED);
+ g_return_val_if_fail (SOUP_IS_ADDRESS (remote_addr), SOUP_STATUS_MALFORMED);
+
+ sync = !(sock->priv->flags & SOUP_SOCKET_FLAG_NONBLOCKING);
sock->priv->remote_addr = g_object_ref (remote_addr);
+ if (sync) {
+ status = soup_address_resolve_sync (remote_addr);
+ if (!SOUP_STATUS_IS_SUCCESSFUL (status))
+ return status;
+ }
+
sa = soup_address_get_sockaddr (sock->priv->remote_addr, &len);
if (!sa) {
- soup_address_resolve (sock->priv->remote_addr,
- got_address, sock);
- return;
+ if (sync)
+ return SOUP_STATUS_CANT_RESOLVE;
+
+ soup_address_resolve_async (remote_addr, got_address, sock);
+ return SOUP_STATUS_CONTINUE;
}
sock->priv->sockfd = socket (sa->sa_family, SOCK_STREAM, 0);
- if (sock->priv->sockfd < 0)
- goto cant_connect;
- soup_socket_set_flag (sock, SOUP_SOCKET_FLAG_NONBLOCKING, TRUE);
- soup_socket_set_flag (sock, SOUP_SOCKET_FLAG_NODELAY, TRUE);
+ if (sock->priv->sockfd == -1) {
+ g_free (sa);
+ goto done;
+ }
+ update_fdflags (sock, SOUP_SOCKET_FLAG_ALL);
- /* Connect (non-blocking) */
status = connect (sock->priv->sockfd, sa, len);
g_free (sa);
- sa = NULL;
- if (status == 0) {
- /* Connect already succeeded */
- sock->priv->watch = g_idle_add (idle_connect_result, sock);
- return;
+ if (status == -1) {
+ if (errno == EINPROGRESS) {
+ /* Wait for connect to succeed or fail */
+ sock->priv->watch =
+ g_io_add_watch (get_iochannel (sock),
+ G_IO_IN | G_IO_OUT |
+ G_IO_PRI | G_IO_ERR |
+ G_IO_HUP | G_IO_NVAL,
+ connect_watch, sock);
+ return SOUP_STATUS_CONTINUE;
+ } else {
+ close (sock->priv->sockfd);
+ sock->priv->sockfd = -1;
+ }
}
- 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;
+ done:
+ if (sync) {
+ return sock->priv->sockfd != -1 ?
+ SOUP_STATUS_OK : SOUP_STATUS_CANT_CONNECT;
+ } else {
+ sock->priv->watch = g_idle_add (idle_connect_result, sock);
+ return SOUP_STATUS_CONTINUE;
}
- sock->priv->watch = g_idle_add (idle_connect_result, sock);
}
static gboolean
new = soup_socket_new ();
new->priv->sockfd = sockfd;
- soup_socket_set_flag (new, SOUP_SOCKET_FLAG_NONBLOCKING, TRUE);
- soup_socket_set_flag (new, SOUP_SOCKET_FLAG_NODELAY, TRUE);
+ new->priv->flags = (SOUP_SOCKET_FLAG_NONBLOCKING |
+ SOUP_SOCKET_FLAG_NODELAY |
+ SOUP_SOCKET_FLAG_SERVER |
+ (sock->priv->flags & SOUP_SOCKET_FLAG_SSL));
+ update_fdflags (new, SOUP_SOCKET_FLAG_ALL);
new->priv->remote_addr = soup_address_new_from_sockaddr ((struct sockaddr *)&sa, sa_len);
- new->priv->server = TRUE;
- if (sock->priv->ssl) {
- new->priv->ssl = TRUE;
+ if (SOUP_SOCKET_CHECK_FLAG (new, SOUP_SOCKET_FLAG_SSL))
soup_socket_start_ssl (new);
- } else
- soup_socket_get_iochannel (new);
+ else
+ get_iochannel (new);
g_signal_emit (sock, signals[NEW_CONNECTION], 0, new);
g_object_unref (new);
/**
* soup_socket_listen:
- * @sock: a #SoupSocket (which must not be connected or listening)
+ * @sock: a server #SoupSocket (which must not already be connected or
+ * listening)
* @local_addr: Local address to bind to.
*
* Makes @sock start listening on the given interface and port. When
int sa_len;
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 (SOUP_SOCKET_CHECK_FLAG (sock, SOUP_SOCKET_FLAG_SERVER), FALSE);
g_return_val_if_fail (sock->priv->sockfd == -1, FALSE);
+ g_return_val_if_fail (SOUP_IS_ADDRESS (local_addr), FALSE);
/* @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
sock->priv->sockfd = socket (sa->sa_family, SOCK_STREAM, 0);
if (sock->priv->sockfd < 0)
goto cant_listen;
- soup_socket_set_flag (sock, SOUP_SOCKET_FLAG_NONBLOCKING, TRUE);
- soup_socket_set_flag (sock, SOUP_SOCKET_FLAG_REUSEADDR, TRUE);
+ update_fdflags (sock, SOUP_SOCKET_FLAG_ALL);
/* Bind */
if (bind (sock->priv->sockfd, sa, sa_len) != 0)
if (listen (sock->priv->sockfd, 10) != 0)
goto cant_listen;
- sock->priv->server = TRUE;
-
- soup_socket_get_iochannel (sock);
- sock->priv->watch = g_io_add_watch (sock->priv->iochannel,
+ sock->priv->watch = g_io_add_watch (get_iochannel (sock),
G_IO_IN | G_IO_ERR | G_IO_HUP,
listen_watch, sock);
return TRUE;
{
GIOChannel *chan;
- chan = soup_socket_get_iochannel (sock);
- sock->priv->iochannel = sock->priv->server ?
+ chan = get_iochannel (sock);
+ sock->priv->iochannel =
+ SOUP_SOCKET_CHECK_FLAG (sock, SOUP_SOCKET_FLAG_SERVER) ?
soup_ssl_get_server_iochannel (chan) :
soup_ssl_get_iochannel (chan);
- sock->priv->ssl = TRUE;
+ SOUP_SOCKET_SET_FLAG (sock, SOUP_SOCKET_FLAG_SSL);
}
/**
- * soup_socket_client_new:
+ * soup_socket_client_new_async:
* @hostname: remote machine to connect to
* @port: remote port to connect to
* @ssl: whether or not to use SSL
* 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)
+soup_socket_client_new_async (const char *hostname, guint port, gboolean ssl,
+ SoupSocketCallback callback, gpointer user_data)
{
SoupSocket *sock;
g_return_val_if_fail (hostname != NULL, NULL);
sock = soup_socket_new ();
- sock->priv->ssl = ssl;
+ sock->priv->flags = (SOUP_SOCKET_FLAG_NONBLOCKING |
+ (ssl ? SOUP_SOCKET_FLAG_SSL : 0));
soup_socket_connect (sock, soup_address_new (hostname, port));
if (callback) {
}
/**
+ * soup_socket_client_new_sync:
+ * @hostname: remote machine to connect to
+ * @port: remote port to connect to
+ * @ssl: whether or not to use SSL
+ * @status_ret: pointer to return the soup status in
+ *
+ * Creates a connection to @hostname and @port. If @status_ret is not
+ * %NULL, it will contain a status code on return.
+ *
+ * Return value: the new socket, or %NULL if it could not connect.
+ **/
+SoupSocket *
+soup_socket_client_new_sync (const char *hostname, guint port, gboolean ssl,
+ guint *status_ret)
+{
+ SoupSocket *sock;
+ guint status;
+
+ g_return_val_if_fail (hostname != NULL, NULL);
+
+ sock = soup_socket_new ();
+ sock->priv->flags = ssl ? SOUP_SOCKET_FLAG_SSL : 0;
+ status = soup_socket_connect (sock, soup_address_new (hostname, port));
+
+ if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
+ g_object_unref (sock);
+ sock = NULL;
+ }
+
+ if (status_ret)
+ *status_ret = status;
+ return sock;
+}
+
+/**
* soup_socket_server_new:
* @local_addr: Local address to bind to. (Use soup_address_any_new() to
* accept connections on any local address)
g_return_val_if_fail (SOUP_IS_ADDRESS (local_addr), NULL);
sock = soup_socket_new ();
- sock->priv->ssl = ssl;
-
+ sock->priv->flags = (SOUP_SOCKET_FLAG_SERVER |
+ SOUP_SOCKET_FLAG_NONBLOCKING |
+ (ssl ? SOUP_SOCKET_FLAG_SSL : 0));
if (!soup_socket_listen (sock, local_addr)) {
g_object_unref (sock);
return NULL;
}
-/**
- * soup_socket_get_iochannel:
- * @sock: #SoupSocket to get #GIOChannel from.
- *
- * Get the #GIOChannel for the #SoupSocket.
- *
- * If you ref the iochannel, it will remain valid after @sock is
- * destroyed.
- *
- * Returns: A #GIOChannel; %NULL on failure.
- **/
-GIOChannel *
-soup_socket_get_iochannel (SoupSocket *sock)
-{
- 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);
- g_io_channel_set_encoding (sock->priv->iochannel, NULL, NULL);
- g_io_channel_set_buffered (sock->priv->iochannel, FALSE);
- }
- return sock->priv->iochannel;
-}
-
void
soup_socket_disconnect (SoupSocket *sock)
{
void (*new_connection) (SoupSocket *, SoupSocket *);
} SoupSocketClass;
-typedef enum {
- SOUP_SOCKET_FLAG_NONBLOCKING,
- SOUP_SOCKET_FLAG_NODELAY,
- SOUP_SOCKET_FLAG_REUSEADDR,
-} SoupSocketFlag;
+#define SOUP_SOCKET_FLAG_NONBLOCKING (1<<0)
+#define SOUP_SOCKET_FLAG_NODELAY (1<<1)
+#define SOUP_SOCKET_FLAG_SERVER (1<<2)
+#define SOUP_SOCKET_FLAG_REUSEADDR (1<<3)
+#define SOUP_SOCKET_FLAG_ALL ( ~0 )
GType soup_socket_get_type (void);
SoupSocket *soup_socket_new (void);
-void soup_socket_set_flag (SoupSocket *sock,
- SoupSocketFlag flag,
- gboolean value);
+void soup_socket_set_flags (SoupSocket *sock,
+ guint mask,
+ guint flags);
-void soup_socket_connect (SoupSocket *sock,
+guint soup_socket_connect (SoupSocket *sock,
SoupAddress *rem_addr);
gboolean soup_socket_listen (SoupSocket *sock,
SoupAddress *local_addr);
SoupSocket *sock,
gpointer user_data);
-SoupSocket *soup_socket_client_new (const char *hostname,
+SoupSocket *soup_socket_client_new_async (const char *hostname,
guint port,
gboolean ssl,
SoupSocketCallback callback,
gpointer user_data);
+SoupSocket *soup_socket_client_new_sync (const char *hostname,
+ guint port,
+ gboolean ssl,
+ guint *status);
SoupSocket *soup_socket_server_new (SoupAddress *local_addr,
gboolean ssl,
SoupSocketListenerCallback,
gpointer user_data);
-
-GIOChannel *soup_socket_get_iochannel (SoupSocket *sock);
-
SoupAddress *soup_socket_get_local_address (SoupSocket *sock);
SoupAddress *soup_socket_get_remote_address (SoupSocket *sock);
return dup;
}
+SoupUri *
+soup_uri_copy_root (const SoupUri *uri)
+{
+ SoupUri *dup;
+
+ g_return_val_if_fail (uri != NULL, NULL);
+
+ dup = g_new0 (SoupUri, 1);
+ dup->protocol = uri->protocol;
+ dup->host = g_strdup (uri->host);
+ dup->port = uri->port;
+
+ return dup;
+}
+
static inline gboolean
parts_equal (const char *one, const char *two)
{
gboolean just_path);
SoupUri *soup_uri_copy (const SoupUri *uri);
+SoupUri *soup_uri_copy_root (const SoupUri *uri);
gboolean soup_uri_equal (const SoupUri *uri1,
const SoupUri *uri2);
noinst_PROGRAMS = get revserver simple-httpd simple-proxy
get_SOURCES = get.c
-revserver_SOURCES = revserver.c
simple_httpd_SOURCES = simple-httpd.c
simple_proxy_SOURCES = simple-proxy.c
+revserver_SOURCES = revserver.c
+revserver_LDFLAGS = `pkg-config --libs gthread-2.0`
+
check_PROGRAMS = auth-test uri-parsing
auth_test_SOURCES = auth-test.c
static int
identify_auth (SoupMessage *msg)
{
- SoupAuth *auth;
const char *header;
int num;
int i;
g_type_init ();
- session = soup_session_new ();
+ session = soup_session_new_default ();
for (i = 0; i < ntests; i++) {
printf ("Test %d: %s\n", i + 1, tests[i].explanation);
int opt;
g_type_init ();
- session = soup_session_new ();
+ session = soup_session_new_default ();
while ((opt = getopt (argc, argv, "r")) != -1) {
switch (opt) {
#endif
#include <ctype.h>
+#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static void rev_read (SoupSocket *sock, GString *buf);
static void rev_write (SoupSocket *sock, GString *buf);
-gboolean nonblocking = TRUE;
-
static void
reverse (GString *buf)
{
static void
rev_done (SoupSocket *sock, GString *buf)
{
- g_signal_handlers_disconnect_by_func (sock, rev_read, buf);
- g_signal_handlers_disconnect_by_func (sock, rev_write, buf);
g_object_unref (sock);
g_string_free (buf, TRUE);
}
break;
case SOUP_SOCKET_WOULD_BLOCK:
- g_assert (nonblocking == TRUE);
+ g_error ("Can't happen");
break;
default:
break;
case SOUP_SOCKET_WOULD_BLOCK:
- g_assert (nonblocking == TRUE);
+ g_error ("Can't happen");
break;
default:
}
}
+static void *
+start_thread (void *client)
+{
+ rev_read (client, g_string_new (NULL));
+
+ return NULL;
+}
+
static void
new_connection (SoupSocket *listener, SoupSocket *client, gpointer user_data)
{
- GString *buf;
+ pthread_t pth;
g_object_ref (client);
- buf = g_string_new (NULL);
-
- if (nonblocking) {
- g_signal_connect (client, "readable",
- G_CALLBACK (rev_read), buf);
- g_signal_connect (client, "writable",
- G_CALLBACK (rev_write), buf);
- } else
- soup_socket_set_flag (client, SOUP_SOCKET_FLAG_NONBLOCKING, FALSE);
+ soup_socket_set_flags (client, SOUP_SOCKET_FLAG_NONBLOCKING, 0);
- rev_read (client, buf);
+ if (pthread_create (&pth, NULL, start_thread, client) != 0) {
+ g_warning ("Could not start thread");
+ g_object_unref (client);
+ }
}
int
g_type_init ();
- while ((opt = getopt (argc, argv, "6bp:")) != -1) {
+ while ((opt = getopt (argc, argv, "6p:")) != -1) {
switch (opt) {
case '6':
family = SOUP_ADDRESS_FAMILY_IPV6;
break;
- case 'b':
- nonblocking = FALSE;
- break;
case 'p':
port = atoi (optarg);
break;
default:
- fprintf (stderr, "Usage: %s [-6] [-b] [-p port]\n",
+ fprintf (stderr, "Usage: %s [-6] [-p port]\n",
argv[0]);
exit (1);
}
soup_server_get_port (server));
soup_server_run_async (server);
- session = soup_session_new ();
+ session = soup_session_new_default ();
printf ("\nWaiting for requests...\n");