diff options
-rw-r--r-- | ChangeLog | 27 | ||||
-rw-r--r-- | configure.in | 2 | ||||
-rw-r--r-- | libsoup/soup-address.c | 58 | ||||
-rw-r--r-- | libsoup/soup-address.h | 6 | ||||
-rw-r--r-- | libsoup/soup-connection.c | 15 | ||||
-rw-r--r-- | libsoup/soup-dns.c | 189 | ||||
-rw-r--r-- | libsoup/soup-dns.h | 8 | ||||
-rw-r--r-- | libsoup/soup-message-io.c | 6 | ||||
-rw-r--r-- | libsoup/soup-socket.c | 294 | ||||
-rw-r--r-- | libsoup/soup-socket.h | 14 | ||||
-rw-r--r-- | tests/Makefile.am | 2 | ||||
-rw-r--r-- | tests/dns.c | 3 | ||||
-rw-r--r-- | tests/revserver.c | 190 | ||||
-rw-r--r-- | tests/ssl-test.c | 16 |
14 files changed, 408 insertions, 422 deletions
@@ -1,3 +1,30 @@ +2008-01-14 Dan Winship <danw@gnome.org> + + * configure.in: require glib 2.15.0, and gio + + * libsoup/soup-dns.c (soup_dns_lookup_resolve) + (soup_dns_lookup_resolve_async): Add GCancellables, and support + cancellation of DNS lookups. + (resolve_address, resolve_name): If we get a DNS failure (eg, + because we're disconnected from the network), don't cache that + result, just try again next time someone asks. [#508593] + + * libsoup/soup-address.c (soup_address_resolve_async) + (soup_address_resolve_sync): Add GCancellables, pass them to + soup-dns. + + * libsoup/soup-socket.c (soup_socket_connect_async) + (soup_socket_connect_sync): Add GCancellables and implement + cancellation. + (soup_socket_start_ssl, soup_socket_start_proxy_ssl) + (soup_socket_read, soup_socket_read_until, soup_socket_write): add + GCancellables, though these routines don't actually implement + cancellation yet. + (soup_socket_disconnect): Don't close() the socket if someone is + doing I/O on it, as that creates a race condition. (The fd number + might be quickly recycled.) Instead, keep the socket open but + dead, via shutdown(). + 2008-01-14 Benjamin Otte <otte@gnome.org> * libsoup/soup-socket.c: (soup_socket_class_init): clarify docs for diff --git a/configure.in b/configure.in index a263de53..ac8c9df9 100644 --- a/configure.in +++ b/configure.in @@ -73,7 +73,7 @@ dnl *********************** dnl *** Checks for glib *** dnl *********************** -AM_PATH_GLIB_2_0(2.12.0,,,gobject gthread) +AM_PATH_GLIB_2_0(2.15.0,,,gobject gthread gio) PKG_CHECK_MODULES(XML, libxml-2.0) AC_SUBST(XML_CFLAGS) diff --git a/libsoup/soup-address.c b/libsoup/soup-address.c index 13509d4f..628f4ba2 100644 --- a/libsoup/soup-address.c +++ b/libsoup/soup-address.c @@ -374,39 +374,32 @@ typedef struct { } SoupAddressResolveAsyncData; static void -free_res_data (gpointer res_data, GObject *ex_addr) -{ - g_free (res_data); -} - -static void -lookup_resolved (SoupDNSLookup *lookup, gboolean success, gpointer user_data) +lookup_resolved (SoupDNSLookup *lookup, guint status, gpointer user_data) { SoupAddressResolveAsyncData *res_data = user_data; SoupAddress *addr; SoupAddressCallback callback; gpointer callback_data; - guint status; addr = res_data->addr; callback = res_data->callback; callback_data = res_data->callback_data; - g_object_weak_unref (G_OBJECT (addr), free_res_data, res_data); g_free (res_data); - if (success) + if (status == SOUP_STATUS_OK) update_address (addr, lookup); - if (callback) { - status = success ? SOUP_STATUS_OK : SOUP_STATUS_CANT_RESOLVE; + if (callback) callback (addr, status, callback_data); - } + + g_object_unref (addr); } /** * SoupAddressCallback: * @addr: the #SoupAddress that was resolved - * @status: %SOUP_STATUS_OK or %SOUP_STATUS_CANT_RESOLVE + * @status: %SOUP_STATUS_OK, %SOUP_STATUS_CANT_RESOLVE, or + * %SOUP_STATUS_CANCELLED * @data: the user data that was passed to * soup_address_resolve_async() * @@ -417,19 +410,23 @@ lookup_resolved (SoupDNSLookup *lookup, gboolean success, gpointer user_data) * soup_address_resolve_async: * @addr: a #SoupAddress * @async_context: the #GMainContext to call @callback from + * @cancellable: a #GCancellable object, or %NULL * @callback: callback to call with the result * @user_data: data for @callback * * 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). + * soup_address_new_any().) + * + * If @cancellable is non-%NULL, it can be used to cancel the + * resolution. @callback will still be invoked in this case, with a + * status of %SOUP_STATUS_CANCELLED. **/ void soup_address_resolve_async (SoupAddress *addr, GMainContext *async_context, - SoupAddressCallback callback, - gpointer user_data) + GCancellable *cancellable, + SoupAddressCallback callback, gpointer user_data) { SoupAddressPrivate *priv; SoupAddressResolveAsyncData *res_data; @@ -442,30 +439,39 @@ soup_address_resolve_async (SoupAddress *addr, GMainContext *async_context, res_data->callback = callback; res_data->callback_data = user_data; - g_object_weak_ref (G_OBJECT (addr), free_res_data, res_data); - soup_dns_lookup_resolve_async (priv->lookup, async_context, lookup_resolved, res_data); + g_object_ref (addr); + soup_dns_lookup_resolve_async (priv->lookup, async_context, cancellable, + lookup_resolved, res_data); } /** * soup_address_resolve_sync: * @addr: a #SoupAddress + * @cancellable: a #GCancellable object, or %NULL * * Synchronously resolves the missing half of @addr, as with * soup_address_resolve_async(). * - * Return value: %SOUP_STATUS_OK or %SOUP_STATUS_CANT_RESOLVE + * If @cancellable is non-%NULL, it can be used to cancel the + * resolution. soup_address_resolve_sync() will then return a status + * of %SOUP_STATUS_CANCELLED. + * + * Return value: %SOUP_STATUS_OK, %SOUP_STATUS_CANT_RESOLVE, or + * %SOUP_STATUS_CANCELLED. **/ guint -soup_address_resolve_sync (SoupAddress *addr) +soup_address_resolve_sync (SoupAddress *addr, GCancellable *cancellable) { SoupAddressPrivate *priv; - gboolean success; + guint status; g_return_val_if_fail (SOUP_IS_ADDRESS (addr), SOUP_STATUS_MALFORMED); priv = SOUP_ADDRESS_GET_PRIVATE (addr); - success = soup_dns_lookup_resolve (priv->lookup); - if (success) + g_object_ref (addr); + status = soup_dns_lookup_resolve (priv->lookup, cancellable); + if (status == SOUP_STATUS_OK) update_address (addr, priv->lookup); - return success ? SOUP_STATUS_OK : SOUP_STATUS_CANT_RESOLVE; + g_object_unref (addr); + return status; } diff --git a/libsoup/soup-address.h b/libsoup/soup-address.h index 6a2a1dcf..57aa9034 100644 --- a/libsoup/soup-address.h +++ b/libsoup/soup-address.h @@ -8,6 +8,8 @@ #include <sys/types.h> +#include <gio/gio.h> + #include <libsoup/soup-portability.h> #include <libsoup/soup-types.h> @@ -61,9 +63,11 @@ SoupAddress *soup_address_new_any (SoupAddressFamily family, void soup_address_resolve_async (SoupAddress *addr, GMainContext *async_context, + GCancellable *cancellable, SoupAddressCallback callback, gpointer user_data); -guint soup_address_resolve_sync (SoupAddress *addr); +guint soup_address_resolve_sync (SoupAddress *addr, + GCancellable *cancellable); const char *soup_address_get_name (SoupAddress *addr); const char *soup_address_get_physical (SoupAddress *addr); diff --git a/libsoup/soup-connection.c b/libsoup/soup-connection.c index c7216277..aae7f12a 100644 --- a/libsoup/soup-connection.c +++ b/libsoup/soup-connection.c @@ -385,7 +385,8 @@ tunnel_connect_finished (SoupMessage *msg, gpointer user_data) if (SOUP_STATUS_IS_SUCCESSFUL (status)) { if (soup_socket_start_proxy_ssl (priv->socket, - priv->origin_uri->host)) + priv->origin_uri->host, + NULL)) priv->connected = TRUE; else status = SOUP_STATUS_SSL_FAILED; @@ -440,7 +441,7 @@ socket_connect_result (SoupSocket *sock, guint status, gpointer user_data) goto done; if (soup_uri_is_https (priv->conn_uri)) { - if (!soup_socket_start_ssl (sock)) { + if (!soup_socket_start_ssl (sock, NULL)) { status = SOUP_STATUS_SSL_FAILED; goto done; } @@ -499,7 +500,8 @@ soup_connection_connect_async (SoupConnection *conn, SOUP_SOCKET_SSL_CREDENTIALS, priv->ssl_creds, SOUP_SOCKET_ASYNC_CONTEXT, priv->async_context, NULL); - soup_socket_connect_async (priv->socket, socket_connect_result, conn); + soup_socket_connect_async (priv->socket, NULL, + socket_connect_result, conn); g_signal_connect (priv->socket, "disconnected", G_CALLBACK (socket_disconnected), conn); @@ -534,7 +536,7 @@ soup_connection_connect_sync (SoupConnection *conn) SOUP_SOCKET_TIMEOUT, priv->timeout, NULL); - status = soup_socket_connect_sync (priv->socket); + status = soup_socket_connect_sync (priv->socket, NULL); g_object_unref (addr); if (!SOUP_STATUS_IS_SUCCESSFUL (status)) @@ -544,7 +546,7 @@ soup_connection_connect_sync (SoupConnection *conn) G_CALLBACK (socket_disconnected), conn); if (soup_uri_is_https (priv->conn_uri)) { - if (!soup_socket_start_ssl (priv->socket)) { + if (!soup_socket_start_ssl (priv->socket, NULL)) { status = SOUP_STATUS_SSL_FAILED; goto fail; } @@ -572,7 +574,8 @@ soup_connection_connect_sync (SoupConnection *conn) if (SOUP_STATUS_IS_SUCCESSFUL (status)) { if (!soup_socket_start_proxy_ssl (priv->socket, - priv->origin_uri->host)) + priv->origin_uri->host, + NULL)) status = SOUP_STATUS_SSL_FAILED; } } diff --git a/libsoup/soup-dns.c b/libsoup/soup-dns.c index 53262995..e0bb5df4 100644 --- a/libsoup/soup-dns.c +++ b/libsoup/soup-dns.c @@ -19,6 +19,7 @@ #include "soup-dns.h" #include "soup-misc.h" +#include "soup-status.h" #ifndef INET_ADDRSTRLEN # define INET_ADDRSTRLEN 16 @@ -122,7 +123,7 @@ typedef struct { gboolean resolved; GThread *resolver_thread; - GSList *lookups; + GSList *async_lookups; } SoupDNSCacheEntry; static GHashTable *soup_dns_cache; @@ -132,10 +133,9 @@ struct SoupDNSLookup { SoupDNSCacheEntry *entry; GMainContext *async_context; + GCancellable *cancellable; SoupDNSCallback callback; gpointer user_data; - - gboolean running; }; static GMutex *soup_dns_lock; @@ -320,8 +320,10 @@ resolve_address (SoupDNSCacheEntry *entry) retval = getaddrinfo (entry->hostname, NULL, &hints, &res); if (retval == 0) { entry->sockaddr = g_memdup (res->ai_addr, res->ai_addrlen); + entry->resolved = TRUE; freeaddrinfo (res); - } + } else + entry->resolved = (retval != EAI_AGAIN); #else /* !HAVE_GETADDRINFO */ @@ -336,7 +338,10 @@ resolve_address (SoupDNSCacheEntry *entry) sin.sin_family = AF_INET; memcpy (&sin.sin_addr, h->h_addr_list[0], sizeof (struct in_addr)); entry->sockaddr = g_memdup (&sin, sizeof (struct sockaddr_in)); - } + entry->resolved = TRUE; + } else + entry->resolved = (h || h_errno != TRY_AGAIN); + g_mutex_unlock (soup_gethost_lock); @@ -363,10 +368,13 @@ resolve_name (SoupDNSCacheEntry *entry) #endif ); - if (retval == 0) + if (retval == 0) { entry->hostname = name; - else + entry->resolved = TRUE; + } else { g_free (name); + entry->resolved = (retval != EAI_AGAIN); + } #else /* !HAVE_GETNAMEINFO */ @@ -377,8 +385,11 @@ resolve_name (SoupDNSCacheEntry *entry) if (sin->sin_family == AF_INET) { h = gethostbyaddr (&sin->sin_addr, sizeof (sin->sin_addr), AF_INET); - if (h) + if (h) { entry->hostname = g_strdup (h->h_name); + entry->resolved = TRUE; + } else + entry->resolved = (h_errno != TRY_AGAIN); } g_mutex_unlock (soup_gethost_lock); @@ -466,18 +477,30 @@ soup_dns_lookup_address (struct sockaddr *sockaddr) return lookup; } +static inline guint +resolve_status (SoupDNSCacheEntry *entry, GCancellable *cancellable) +{ + if (entry->resolved) + return SOUP_STATUS_OK; + else if (g_cancellable_is_cancelled (cancellable)) + return SOUP_STATUS_CANCELLED; + else + return SOUP_STATUS_CANT_RESOLVE; +} + +static void async_cancel (GCancellable *cancellable, gpointer user_data); + static gboolean do_async_callback (gpointer user_data) { SoupDNSLookup *lookup = user_data; + SoupDNSCacheEntry *entry = lookup->entry; + GCancellable *cancellable = lookup->cancellable; - if (lookup->running) { - SoupDNSCacheEntry *entry = lookup->entry; - gboolean success = (entry->hostname != NULL && entry->sockaddr != NULL); - - lookup->running = FALSE; - lookup->callback (lookup, success, lookup->user_data); - } + lookup->callback (lookup, resolve_status (entry, cancellable), + lookup->user_data); + if (cancellable) + g_signal_handlers_disconnect_by_func (cancellable, async_cancel, lookup); return FALSE; } @@ -486,27 +509,26 @@ static gpointer resolver_thread (gpointer user_data) { SoupDNSCacheEntry *entry = user_data; - GSList *lookups; + GSList *async_lookups; SoupDNSLookup *lookup; if (entry->hostname == NULL) resolve_name (entry); - if (entry->sockaddr == NULL) + else if (entry->sockaddr == NULL) resolve_address (entry); - entry->resolved = TRUE; entry->resolver_thread = NULL; g_mutex_lock (soup_dns_lock); - lookups = entry->lookups; - entry->lookups = NULL; + async_lookups = entry->async_lookups; + entry->async_lookups = NULL; g_mutex_unlock (soup_dns_lock); g_cond_broadcast (soup_dns_cond); - while (lookups) { - lookup = lookups->data; - lookups = g_slist_remove (lookups, lookup); + while (async_lookups) { + lookup = async_lookups->data; + async_lookups = g_slist_remove (async_lookups, lookup); soup_add_idle (lookup->async_context, do_async_callback, lookup); } @@ -515,52 +537,100 @@ resolver_thread (gpointer user_data) return NULL; } +static void +sync_cancel (GCancellable *cancellable, gpointer user_data) +{ + /* We can't actually cancel the resolver thread. So we just + * wake up the blocking thread, which will see that + * @cancellable has been cancelled and then stop waiting for + * the result. If the resolver thread eventually finishes, + * its result will make it to the cache. + */ + g_cond_broadcast (soup_dns_cond); +} + /** * soup_dns_lookup_resolve: * @lookup: a #SoupDNSLookup + * @cancellable: a #GCancellable, or %NULL * - * Synchronously resolves @lookup. You can cancel a pending resolution - * using soup_dns_lookup_cancel(). + * Synchronously resolves @lookup. * - * Return value: success or failure. + * Return value: %SOUP_STATUS_OK, %SOUP_STATUS_CANT_RESOLVE, or + * %SOUP_STATUS_CANCELLED **/ -gboolean -soup_dns_lookup_resolve (SoupDNSLookup *lookup) +guint +soup_dns_lookup_resolve (SoupDNSLookup *lookup, GCancellable *cancellable) { SoupDNSCacheEntry *entry = lookup->entry; + guint cancel_id = 0; g_mutex_lock (soup_dns_lock); - lookup->running = TRUE; + if (!entry->resolved) { + if (!entry->resolver_thread) { + soup_dns_cache_entry_ref (entry); + entry->resolver_thread = + g_thread_create (resolver_thread, entry, + FALSE, NULL); + } - if (!entry->resolved && !entry->resolver_thread) { - soup_dns_cache_entry_ref (entry); - entry->resolver_thread = - g_thread_create (resolver_thread, entry, FALSE, NULL); + if (cancellable) { + cancel_id = g_signal_connect (cancellable, "cancelled", + G_CALLBACK (sync_cancel), + NULL); + } } - while (!entry->resolved && lookup->running) + while (entry->resolver_thread && + !g_cancellable_is_cancelled (cancellable)) g_cond_wait (soup_dns_cond, soup_dns_lock); - lookup->running = FALSE; + if (cancel_id) + g_signal_handler_disconnect (cancellable, cancel_id); + + g_mutex_unlock (soup_dns_lock); + + return resolve_status (entry, cancellable); +} + +static void +async_cancel (GCancellable *cancellable, gpointer user_data) +{ + SoupDNSLookup *lookup = user_data; + SoupDNSCacheEntry *entry = lookup->entry; + + /* We can't actually cancel the resolver thread. So we just + * remove @lookup from the list of pending async lookups and + * invoke its callback now. If the resolver thread eventually + * finishes, its result will make it to the cache. + */ + g_mutex_lock (soup_dns_lock); + + if (g_slist_find (entry->async_lookups, lookup)) { + entry->async_lookups = g_slist_remove (entry->async_lookups, + lookup); + soup_add_idle (lookup->async_context, do_async_callback, lookup); + } g_mutex_unlock (soup_dns_lock); - return entry->hostname != NULL && entry->sockaddr != NULL; } /** * soup_dns_lookup_resolve_async: * @lookup: a #SoupDNSLookup * @async_context: #GMainContext to call @callback in + * @cancellable: a #GCancellable, or %NULL * @callback: callback to call when @lookup is resolved * @user_data: data to pass to @callback; * * Tries to asynchronously resolve @lookup. Invokes @callback when it - * has succeeded or failed. You can cancel a pending resolution using - * soup_dns_lookup_cancel(). + * has succeeded or failed. **/ void -soup_dns_lookup_resolve_async (SoupDNSLookup *lookup, GMainContext *async_context, +soup_dns_lookup_resolve_async (SoupDNSLookup *lookup, + GMainContext *async_context, + GCancellable *cancellable, SoupDNSCallback callback, gpointer user_data) { SoupDNSCacheEntry *entry = lookup->entry; @@ -568,16 +638,23 @@ soup_dns_lookup_resolve_async (SoupDNSLookup *lookup, GMainContext *async_contex g_mutex_lock (soup_dns_lock); lookup->async_context = async_context; + lookup->cancellable = cancellable; lookup->callback = callback; lookup->user_data = user_data; - lookup->running = TRUE; if (!entry->resolved) { - entry->lookups = g_slist_prepend (entry->lookups, lookup); + entry->async_lookups = g_slist_prepend (entry->async_lookups, + lookup); + if (cancellable) { + g_signal_connect (cancellable, "cancelled", + G_CALLBACK (async_cancel), lookup); + } + if (!entry->resolver_thread) { soup_dns_cache_entry_ref (entry); entry->resolver_thread = - g_thread_create (resolver_thread, entry, FALSE, NULL); + g_thread_create (resolver_thread, entry, + FALSE, NULL); } } else soup_add_idle (lookup->async_context, do_async_callback, lookup); @@ -586,28 +663,6 @@ soup_dns_lookup_resolve_async (SoupDNSLookup *lookup, GMainContext *async_contex } /** - * soup_dns_lookup_cancel: - * @lookup: a #SoupDNSLookup - * - * Cancels @lookup. If @lookup was running synchronously in another - * thread, it will immediately return %FALSE. If @lookup was running - * asynchronously, its callback function will not be called. - **/ -void -soup_dns_lookup_cancel (SoupDNSLookup *lookup) -{ - /* We never really cancel the DNS lookup itself (since GThread - * doesn't have a kill function, and it might mess up - * underlying resolver data anyway). But clearing lookup->running - * and broadcasting on soup_dns_cond will immediately stop any - * blocking synchronous lookups, and clearing lookup->running - * will also make sure that its async callback is never invoked. - */ - lookup->running = FALSE; - g_cond_broadcast (soup_dns_cond); -} - -/** * soup_dns_lookup_get_hostname: * @lookup: a #SoupDNSLookup * @@ -642,14 +697,12 @@ soup_dns_lookup_get_address (SoupDNSLookup *lookup) * soup_dns_lookup_free: * @lookup: a #SoupDNSLookup * - * Frees @lookup. If @lookup is still running, it will be canceled - * first. + * Frees @lookup. It is an error to cancel a lookup while it is + * running. **/ void soup_dns_lookup_free (SoupDNSLookup *lookup) { - if (lookup->running) - soup_dns_lookup_cancel (lookup); soup_dns_cache_entry_unref (lookup->entry); g_slice_free (SoupDNSLookup, lookup); } diff --git a/libsoup/soup-dns.h b/libsoup/soup-dns.h index 6ff030c0..6519a9c5 100644 --- a/libsoup/soup-dns.h +++ b/libsoup/soup-dns.h @@ -7,6 +7,7 @@ #define SOUP_DNS_H #include <glib.h> +#include <gio/gio.h> #include <sys/types.h> #include <libsoup/soup-portability.h> @@ -20,14 +21,15 @@ SoupDNSLookup *soup_dns_lookup_name (const char *name); SoupDNSLookup *soup_dns_lookup_address (struct sockaddr *sockaddr); void soup_dns_lookup_free (SoupDNSLookup *lookup); -typedef void (*SoupDNSCallback) (SoupDNSLookup *lookup, gboolean success, gpointer user_data); +typedef void (*SoupDNSCallback) (SoupDNSLookup *lookup, guint status, gpointer user_data); -gboolean soup_dns_lookup_resolve (SoupDNSLookup *lookup); +guint soup_dns_lookup_resolve (SoupDNSLookup *lookup, + GCancellable *cancellable); void soup_dns_lookup_resolve_async (SoupDNSLookup *lookup, GMainContext *async_context, + GCancellable *cancellable, SoupDNSCallback callback, gpointer user_data); -void soup_dns_lookup_cancel (SoupDNSLookup *lookup); char *soup_dns_lookup_get_hostname (SoupDNSLookup *lookup); struct sockaddr *soup_dns_lookup_get_address (SoupDNSLookup *lookup); diff --git a/libsoup/soup-message-io.c b/libsoup/soup-message-io.c index 37fbdd65..f10d4c39 100644 --- a/libsoup/soup-message-io.c +++ b/libsoup/soup-message-io.c @@ -232,7 +232,7 @@ read_metadata (SoupMessage *msg, const char *boundary) status = soup_socket_read_until (io->sock, read_buf, sizeof (read_buf), boundary, boundary_len, - &nread, &done, &error); + &nread, &done, NULL, &error); switch (status) { case SOUP_SOCKET_OK: g_byte_array_append (io->read_meta_buf, read_buf, nread); @@ -278,7 +278,7 @@ read_body_chunk (SoupMessage *msg) len = MIN (len, io->read_length); status = soup_socket_read (io->sock, read_buf, len, - &nread, &error); + &nread, NULL, &error); switch (status) { case SOUP_SOCKET_OK: @@ -331,7 +331,7 @@ write_data (SoupMessage *msg, const char *data, guint len) status = soup_socket_write (io->sock, data + io->written, len - io->written, - &nwrote, &error); + &nwrote, NULL, &error); switch (status) { case SOUP_SOCKET_EOF: case SOUP_SOCKET_ERROR: diff --git a/libsoup/soup-socket.c b/libsoup/soup-socket.c index 6d5d04d1..953c6f8e 100644 --- a/libsoup/soup-socket.c +++ b/libsoup/soup-socket.c @@ -87,12 +87,10 @@ static void get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); #ifdef G_OS_WIN32 -#define SOUP_CLOSE_SOCKET(socket) closesocket (socket) #define SOUP_IS_SOCKET_ERROR(status) ((status) == SOCKET_ERROR) #define SOUP_IS_INVALID_SOCKET(socket) ((socket) == INVALID_SOCKET) #define SOUP_IS_CONNECT_STATUS_INPROGRESS() (WSAGetLastError () == WSAEWOULDBLOCK) #else -#define SOUP_CLOSE_SOCKET(socket) close (socket) #define SOUP_IS_SOCKET_ERROR(status) ((status) == -1) #define SOUP_IS_INVALID_SOCKET(socket) ((socket) < 0) #define SOUP_IS_CONNECT_STATUS_INPROGRESS() (errno == EINPROGRESS) @@ -295,12 +293,12 @@ soup_socket_class_init (SoupSocketClass *socket_class) static void -update_fdflags (SoupSocketPrivate *priv) +set_nonblocking (SoupSocketPrivate *priv) { - int opt; - struct timeval timeout; #ifndef G_OS_WIN32 int flags; +#else + u_log val; #endif if (priv->sockfd == -1) @@ -315,20 +313,32 @@ update_fdflags (SoupSocketPrivate *priv) flags &= ~O_NONBLOCK; fcntl (priv->sockfd, F_SETFL, flags); } - flags = fcntl (priv->sockfd, F_GETFD, 0); - if (flags != -1) { - flags |= FD_CLOEXEC; - fcntl (priv->sockfd, F_SETFD, flags); - } - #else - if (priv->non_blocking) { - u_long val = 1; - ioctlsocket (priv->sockfd, FIONBIO, &val); - } else { - u_long val = 0; - ioctlsocket (priv->sockfd, FIONBIO, &val); - } + val = priv->non_blocking ? 1 : 0; + ioctlsocket (priv->sockfd, FIONBIO, &val); +#endif +} + +static void +set_fdflags (SoupSocketPrivate *priv) +{ + int opt; + struct timeval timeout; +#ifndef G_OS_WIN32 + int flags; +#endif + + if (priv->sockfd == -1) + return; + + set_nonblocking (priv); + +#ifndef G_OS_WIN32 + flags = fcntl (priv->sockfd, F_GETFD, 0); + if (flags != -1) { + flags |= FD_CLOEXEC; + fcntl (priv->sockfd, F_SETFD, flags); + } #endif opt = 1; @@ -346,6 +356,17 @@ update_fdflags (SoupSocketPrivate *priv) timeout.tv_usec = 0; setsockopt (priv->sockfd, SOL_SOCKET, SO_SNDTIMEO, (void *) &timeout, sizeof (timeout)); + +#ifndef G_OS_WIN32 + priv->iochannel = + g_io_channel_unix_new (priv->sockfd); +#else + priv->iochannel = + g_io_channel_win32_new_socket (priv->sockfd); +#endif + g_io_channel_set_close_on_unref (priv->iochannel, TRUE); + g_io_channel_set_encoding (priv->iochannel, NULL, NULL); + g_io_channel_set_buffered (priv->iochannel, FALSE); } static void @@ -363,7 +384,7 @@ set_property (GObject *object, guint prop_id, break; case PROP_NON_BLOCKING: priv->non_blocking = g_value_get_boolean (value); - update_fdflags (priv); + set_nonblocking (priv); break; case PROP_SSL_CREDENTIALS: priv->ssl_creds = g_value_get_pointer (value); @@ -438,28 +459,10 @@ soup_socket_new (const char *optname1, ...) return sock; } -static GIOChannel * -get_iochannel (SoupSocketPrivate *priv) -{ - g_mutex_lock (priv->iolock); - if (!priv->iochannel) { -#ifndef G_OS_WIN32 - priv->iochannel = - g_io_channel_unix_new (priv->sockfd); -#else - priv->iochannel = - g_io_channel_win32_new_socket (priv->sockfd); -#endif - g_io_channel_set_close_on_unref (priv->iochannel, TRUE); - g_io_channel_set_encoding (priv->iochannel, NULL, NULL); - g_io_channel_set_buffered (priv->iochannel, FALSE); - } - g_mutex_unlock (priv->iolock); - return priv->iochannel; -} - typedef struct { SoupSocket *sock; + GCancellable *cancellable; + guint cancel_id; SoupSocketCallback callback; gpointer user_data; } SoupSocketAsyncConnectData; @@ -469,12 +472,21 @@ idle_connect_result (gpointer user_data) { SoupSocketAsyncConnectData *sacd = user_data; SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sacd->sock); + guint status; priv->watch_src = NULL; + if (sacd->cancel_id) + g_signal_handler_disconnect (sacd->cancellable, sacd->cancel_id); + + if (priv->sockfd == -1) { + if (g_cancellable_is_cancelled (sacd->cancellable)) + status = SOUP_STATUS_CANCELLED; + else + status = SOUP_STATUS_CANT_CONNECT; + } else + status = SOUP_STATUS_OK; - sacd->callback (sacd->sock, - priv->sockfd != -1 ? SOUP_STATUS_OK : SOUP_STATUS_CANT_CONNECT, - sacd->user_data); + sacd->callback (sacd->sock, status, sacd->user_data); g_slice_free (SoupSocketAsyncConnectData, sacd); return FALSE; } @@ -491,21 +503,13 @@ connect_watch (GIOChannel* iochannel, GIOCondition condition, gpointer data) g_source_destroy (priv->watch_src); priv->watch_src = NULL; - if (condition & ~(G_IO_IN | G_IO_OUT)) - goto cant_connect; - - if (getsockopt (priv->sockfd, SOL_SOCKET, SO_ERROR, - (void *)&error, (void *)&len) != 0) - goto cant_connect; - if (error) - goto cant_connect; + if ((condition & ~(G_IO_IN | G_IO_OUT)) || + (getsockopt (priv->sockfd, SOL_SOCKET, SO_ERROR, + (void *)&error, (void *)&len) != 0) || + error) + disconnect_internal (priv); return idle_connect_result (sacd); - - cant_connect: - sacd->callback (sacd->sock, SOUP_STATUS_CANT_CONNECT, sacd->user_data); - g_slice_free (SoupSocketAsyncConnectData, sacd); - return FALSE; } static void @@ -519,10 +523,52 @@ got_address (SoupAddress *addr, guint status, gpointer user_data) return; } - soup_socket_connect_async (sacd->sock, sacd->callback, sacd->user_data); + soup_socket_connect_async (sacd->sock, sacd->cancellable, + sacd->callback, sacd->user_data); g_slice_free (SoupSocketAsyncConnectData, sacd); } +static void +async_cancel (GCancellable *cancellable, gpointer user_data) +{ + SoupSocketAsyncConnectData *sacd = user_data; + SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sacd->sock); + + if (priv->watch_src) + g_source_destroy (priv->watch_src); + disconnect_internal (priv); + priv->watch_src = soup_add_idle (priv->async_context, + idle_connect_result, sacd); +} + +static guint +socket_connect_internal (SoupSocket *sock) +{ + SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock); + struct sockaddr *sa; + int len, status; + + sa = soup_address_get_sockaddr (priv->remote_addr, &len); + if (!sa) + return SOUP_STATUS_CANT_RESOLVE; + + priv->sockfd = socket (sa->sa_family, SOCK_STREAM, 0); + if (SOUP_IS_INVALID_SOCKET (priv->sockfd)) + return SOUP_STATUS_CANT_CONNECT; + set_fdflags (priv); + + status = connect (priv->sockfd, sa, len); + + if (SOUP_IS_SOCKET_ERROR (status)) { + if (SOUP_IS_CONNECT_STATUS_INPROGRESS ()) + return SOUP_STATUS_CONTINUE; + + disconnect_internal (priv); + return SOUP_STATUS_CANT_CONNECT; + } else + return SOUP_STATUS_OK; +} + /** * SoupSocketCallback: * @sock: the #SoupSocket @@ -535,67 +581,92 @@ got_address (SoupAddress *addr, guint status, gpointer user_data) /** * soup_socket_connect_async: * @sock: a client #SoupSocket (which must not already be connected) + * @cancellable: a #GCancellable, or %NULL * @callback: callback to call after connecting * @user_data: data to pass to @callback * * Begins asynchronously connecting to @sock's remote address. The * socket will call @callback when it succeeds or fails (but not * before returning from this function). + * + * If @cancellable is non-%NULL, it can be used to cancel the + * connection. @callback will still be invoked in this case, with a + * status of %SOUP_STATUS_CANCELLED. **/ void -soup_socket_connect_async (SoupSocket *sock, SoupSocketCallback callback, - gpointer user_data) +soup_socket_connect_async (SoupSocket *sock, GCancellable *cancellable, + SoupSocketCallback callback, gpointer user_data) { SoupSocketPrivate *priv; SoupSocketAsyncConnectData *sacd; - int status; + guint status; g_return_if_fail (SOUP_IS_SOCKET (sock)); priv = SOUP_SOCKET_GET_PRIVATE (sock); g_return_if_fail (priv->remote_addr != NULL); - sacd = g_slice_new (SoupSocketAsyncConnectData); + sacd = g_slice_new0 (SoupSocketAsyncConnectData); sacd->sock = sock; + sacd->cancellable = cancellable; sacd->callback = callback; sacd->user_data = user_data; if (!soup_address_get_sockaddr (priv->remote_addr, NULL)) { soup_address_resolve_async (priv->remote_addr, priv->async_context, + cancellable, got_address, sacd); return; } - status = soup_socket_connect_sync (sock); + status = socket_connect_internal (sock); if (status == SOUP_STATUS_CONTINUE) { /* Wait for connect to succeed or fail */ priv->watch_src = soup_add_io_watch (priv->async_context, - get_iochannel (priv), + priv->iochannel, G_IO_IN | G_IO_OUT | G_IO_PRI | G_IO_ERR | G_IO_HUP | G_IO_NVAL, connect_watch, sacd); + if (cancellable) { + sacd->cancel_id = + g_signal_connect (cancellable, "cancelled", + G_CALLBACK (async_cancel), + sacd); + } } else { priv->watch_src = soup_add_idle (priv->async_context, idle_connect_result, sacd); } } +static void +sync_cancel (GCancellable *cancellable, gpointer sock) +{ + SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock); + + shutdown (priv->sockfd, SHUT_RDWR); +} + /** * soup_socket_connect_sync: * @sock: a client #SoupSocket (which must not already be connected) + * @cancellable: a #GCancellable, or %NULL * * Attempt to synchronously connect @sock to its remote address. * + * If @cancellable is non-%NULL, it can be used to cancel the + * connection, in which case soup_socket_connect_sync() will return + * %SOUP_STATUS_CANCELLED. + * * Return value: a success or failure code. **/ guint -soup_socket_connect_sync (SoupSocket *sock) +soup_socket_connect_sync (SoupSocket *sock, GCancellable *cancellable) { SoupSocketPrivate *priv; - struct sockaddr *sa; - int len, status; + guint status, cancel_id; g_return_val_if_fail (SOUP_IS_SOCKET (sock), SOUP_STATUS_MALFORMED); priv = SOUP_SOCKET_GET_PRIVATE (sock); @@ -603,34 +674,30 @@ soup_socket_connect_sync (SoupSocket *sock) g_return_val_if_fail (priv->sockfd == -1, SOUP_STATUS_MALFORMED); g_return_val_if_fail (priv->remote_addr != NULL, SOUP_STATUS_MALFORMED); - sa = soup_address_get_sockaddr (priv->remote_addr, &len); - if (!sa) { - status = soup_address_resolve_sync (priv->remote_addr); + if (!soup_address_get_sockaddr (priv->remote_addr, NULL)) { + status = soup_address_resolve_sync (priv->remote_addr, + cancellable); if (!SOUP_STATUS_IS_SUCCESSFUL (status)) return status; - sa = soup_address_get_sockaddr (priv->remote_addr, &len); - if (!sa) - return SOUP_STATUS_CANT_RESOLVE; } - priv->sockfd = socket (sa->sa_family, SOCK_STREAM, 0); - if (SOUP_IS_INVALID_SOCKET (priv->sockfd)) - return SOUP_STATUS_CANT_CONNECT; - update_fdflags (priv); - - status = connect (priv->sockfd, sa, len); + if (cancellable) { + cancel_id = g_signal_connect (cancellable, "cancelled", + G_CALLBACK (sync_cancel), sock); + } - if (SOUP_IS_SOCKET_ERROR (status)) { - if (SOUP_IS_CONNECT_STATUS_INPROGRESS ()) - return SOUP_STATUS_CONTINUE; + status = socket_connect_internal (sock); - SOUP_CLOSE_SOCKET (priv->sockfd); - priv->sockfd = -1; - return SOUP_STATUS_CANT_CONNECT; + if (cancellable) { + if (status != SOUP_STATUS_OK && + g_cancellable_is_cancelled (cancellable)) { + status = SOUP_STATUS_CANCELLED; + disconnect_internal (priv); + } + g_signal_handler_disconnect (cancellable, cancel_id); } - get_iochannel (priv); - return SOUP_STATUS_OK; + return status; } static gboolean @@ -660,17 +727,16 @@ listen_watch (GIOChannel* iochannel, GIOCondition condition, gpointer data) new_priv->non_blocking = priv->non_blocking; new_priv->is_server = TRUE; new_priv->ssl_creds = priv->ssl_creds; - update_fdflags (new_priv); + set_fdflags (new_priv); new_priv->remote_addr = soup_address_new_from_sockaddr ((struct sockaddr *)&sa, sa_len); if (new_priv->ssl_creds) { - if (!soup_socket_start_ssl (new)) { + if (!soup_socket_start_ssl (new, NULL)) { g_object_unref (new); return TRUE; } - } else - get_iochannel (new_priv); + } g_signal_emit (sock, signals[NEW_CONNECTION], 0, new); g_object_unref (new); @@ -715,7 +781,7 @@ soup_socket_listen (SoupSocket *sock) priv->sockfd = socket (sa->sa_family, SOCK_STREAM, 0); if (SOUP_IS_INVALID_SOCKET (priv->sockfd)) goto cant_listen; - update_fdflags (priv); + set_fdflags (priv); /* Bind */ if (bind (priv->sockfd, sa, sa_len) != 0) @@ -729,16 +795,14 @@ soup_socket_listen (SoupSocket *sock) goto cant_listen; priv->watch_src = soup_add_io_watch (priv->async_context, - get_iochannel (priv), + priv->iochannel, G_IO_IN | G_IO_ERR | G_IO_HUP, listen_watch, sock); return TRUE; cant_listen: - if (priv->sockfd != -1) { - SOUP_CLOSE_SOCKET (priv->sockfd); - priv->sockfd = -1; - } + if (priv->iochannel) + disconnect_internal (priv); return FALSE; } @@ -746,23 +810,25 @@ soup_socket_listen (SoupSocket *sock) /** * soup_socket_start_ssl: * @sock: the socket + * @cancellable: a #GCancellable * * Starts using SSL on @socket. * * Return value: success or failure **/ gboolean -soup_socket_start_ssl (SoupSocket *sock) +soup_socket_start_ssl (SoupSocket *sock, GCancellable *cancellable) { SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock); - return soup_socket_start_proxy_ssl (sock, soup_address_get_name (priv->remote_addr)); + return soup_socket_start_proxy_ssl (sock, soup_address_get_name (priv->remote_addr), cancellable); } /** * soup_socket_start_proxy_ssl: * @sock: the socket * @ssl_host: hostname of the SSL server + * @cancellable: a #GCancellable * * Starts using SSL on @socket, expecting to find a host named * @ssl_host. @@ -770,13 +836,14 @@ soup_socket_start_ssl (SoupSocket *sock) * Return value: success or failure **/ gboolean -soup_socket_start_proxy_ssl (SoupSocket *sock, const char *ssl_host) +soup_socket_start_proxy_ssl (SoupSocket *sock, const char *ssl_host, + GCancellable *cancellable) { SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock); GIOChannel *ssl_chan; GIOChannel *real_chan; - real_chan = get_iochannel (priv); + real_chan = priv->iochannel; ssl_chan = soup_ssl_wrap_iochannel ( real_chan, priv->is_server ? SOUP_SSL_TYPE_SERVER : SOUP_SSL_TYPE_CLIENT, @@ -825,19 +892,18 @@ soup_socket_disconnect (SoupSocket *sock) int sockfd; /* Another thread is currently doing IO, so - * we can't close the iochannel. So just kick - * the file descriptor out from under it. + * we can't close the iochannel. So just shutdown + * the file descriptor to force the I/O to fail. + * (It will actually be closed when the socket is + * destroyed.) */ - sockfd = priv->sockfd; priv->sockfd = -1; + if (sockfd == -1) already_disconnected = TRUE; - else { - g_io_channel_set_close_on_unref (priv->iochannel, - FALSE); - SOUP_CLOSE_SOCKET (sockfd); - } + else + shutdown (sockfd, SHUT_RDWR); } if (already_disconnected) @@ -1042,6 +1108,7 @@ read_from_buf (SoupSocket *sock, gpointer buffer, gsize len, gsize *nread) * @buffer: buffer to read into * @len: size of @buffer in bytes * @nread: on return, the number of bytes read into @buffer + * @cancellable: a #GCancellable, or %NULL * @error: error pointer * * Attempts to read up to @len bytes from @sock into @buffer. If some @@ -1058,12 +1125,12 @@ read_from_buf (SoupSocket *sock, gpointer buffer, gsize len, gsize *nread) * * Return value: a #SoupSocketIOStatus, as described above (or * %SOUP_SOCKET_EOF if the socket is no longer connected, or - * %SOUP_SOCKET_ERROR on any other error, in which case @error - * will also be set). + * %SOUP_SOCKET_ERROR on any other error, in which case @error will + * also be set). **/ SoupSocketIOStatus soup_socket_read (SoupSocket *sock, gpointer buffer, gsize len, - gsize *nread, GError **error) + gsize *nread, GCancellable *cancellable, GError **error) { SoupSocketPrivate *priv; SoupSocketIOStatus status; @@ -1091,6 +1158,7 @@ soup_socket_read (SoupSocket *sock, gpointer buffer, gsize len, * @nread: on return, the number of bytes read into @buffer * @got_boundary: on return, whether or not the data in @buffer * ends with the boundary string + * @cancellable: a #GCancellable, or %NULL * @error: error pointer * * Like soup_socket_read(), but reads no further than the first @@ -1104,7 +1172,7 @@ SoupSocketIOStatus soup_socket_read_until (SoupSocket *sock, gpointer buffer, gsize len, gconstpointer boundary, gsize boundary_len, gsize *nread, gboolean *got_boundary, - GError **error) + GCancellable *cancellable, GError **error) { SoupSocketPrivate *priv; SoupSocketIOStatus status; @@ -1181,6 +1249,7 @@ socket_write_watch (GIOChannel *chan, GIOCondition cond, gpointer user_data) * @buffer: data to write * @len: size of @buffer, in bytes * @nwrote: on return, number of bytes written + * @cancellable: a #GCancellable, or %NULL * @error: error pointer * * Attempts to write @len bytes from @buffer to @sock. If some data is @@ -1200,7 +1269,8 @@ socket_write_watch (GIOChannel *chan, GIOCondition cond, gpointer user_data) **/ SoupSocketIOStatus soup_socket_write (SoupSocket *sock, gconstpointer buffer, - gsize len, gsize *nwrote, GError **error) + gsize len, gsize *nwrote, + GCancellable *cancellable, GError **error) { SoupSocketPrivate *priv; GIOStatus status; diff --git a/libsoup/soup-socket.h b/libsoup/soup-socket.h index 71e9545c..710f6fec 100644 --- a/libsoup/soup-socket.h +++ b/libsoup/soup-socket.h @@ -7,6 +7,7 @@ #define SOUP_SOCKET_H 1 #include <libsoup/soup-types.h> +#include <gio/gio.h> G_BEGIN_DECLS @@ -51,15 +52,19 @@ SoupSocket *soup_socket_new (const char *optname1, ...) G_GNUC_NULL_TERMINATED; void soup_socket_connect_async (SoupSocket *sock, + GCancellable *cancellable, SoupSocketCallback callback, gpointer user_data); -guint soup_socket_connect_sync (SoupSocket *sock); +guint soup_socket_connect_sync (SoupSocket *sock, + GCancellable *cancellable); gboolean soup_socket_listen (SoupSocket *sock); -gboolean soup_socket_start_ssl (SoupSocket *sock); +gboolean soup_socket_start_ssl (SoupSocket *sock, + GCancellable *cancellable); gboolean soup_socket_start_proxy_ssl (SoupSocket *sock, - const char *ssl_host); + const char *ssl_host, + GCancellable *cancellable); gboolean soup_socket_is_ssl (SoupSocket *sock); void soup_socket_disconnect (SoupSocket *sock); @@ -80,6 +85,7 @@ SoupSocketIOStatus soup_socket_read (SoupSocket *sock, gpointer buffer, gsize len, gsize *nread, + GCancellable *cancellable, GError **error); SoupSocketIOStatus soup_socket_read_until (SoupSocket *sock, gpointer buffer, @@ -88,12 +94,14 @@ SoupSocketIOStatus soup_socket_read_until (SoupSocket *sock, gsize boundary_len, gsize *nread, gboolean *got_boundary, + GCancellable *cancellable, GError **error); SoupSocketIOStatus soup_socket_write (SoupSocket *sock, gconstpointer buffer, gsize len, gsize *nwrote, + GCancellable *cancellable, GError **error); G_END_DECLS diff --git a/tests/Makefile.am b/tests/Makefile.am index 13d97590..3f9ce551 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -16,7 +16,6 @@ noinst_PROGRAMS = \ getbug \ header-parsing \ ntlm-test \ - revserver \ simple-httpd \ simple-proxy \ uri-parsing \ @@ -39,7 +38,6 @@ ntlm_test_SOURCES = ntlm-test.c $(TEST_SRCS) proxy_test_SOURCES = proxy-test.c $(TEST_SRCS) pull_api_SOURCES = pull-api.c $(TEST_SRCS) query_test_SOURCES = query-test.c $(TEST_SRCS) -revserver_SOURCES = revserver.c server_auth_test_SOURCES = server-auth-test.c $(TEST_SRCS) simple_httpd_SOURCES = simple-httpd.c simple_proxy_SOURCES = simple-proxy.c diff --git a/tests/dns.c b/tests/dns.c index c9e3559b..10d6b98c 100644 --- a/tests/dns.c +++ b/tests/dns.c @@ -51,7 +51,8 @@ main (int argc, char **argv) exit (1); } - soup_address_resolve_async (addr, NULL, resolve_callback, NULL); + soup_address_resolve_async (addr, NULL, NULL, + resolve_callback, NULL); nlookups++; } diff --git a/tests/revserver.c b/tests/revserver.c deleted file mode 100644 index 9be0cdf3..00000000 --- a/tests/revserver.c +++ /dev/null @@ -1,190 +0,0 @@ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <ctype.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include <libsoup/soup-address.h> -#include <libsoup/soup-socket.h> - -#include <glib/gthread.h> - -static void rev_read (SoupSocket *sock, GString *buf); -static void rev_write (SoupSocket *sock, GString *buf); - -static void -reverse (GString *buf) -{ - char tmp, *a, *b; - - a = buf->str; - b = buf->str + buf->len - 1; - - while (isspace ((unsigned char)*b) && b > a) - b--; - - while (a < b) { - tmp = *a; - *a++ = *b; - *b-- = tmp; - } -} - -static void -rev_done (SoupSocket *sock, GString *buf) -{ - g_object_unref (sock); - g_string_free (buf, TRUE); -} - -static void -rev_write (SoupSocket *sock, GString *buf) -{ - SoupSocketIOStatus status; - gsize nwrote; - - do { - status = soup_socket_write (sock, buf->str, buf->len, - &nwrote, NULL); - memmove (buf->str, buf->str + nwrote, buf->len - nwrote); - buf->len -= nwrote; - } while (status == SOUP_SOCKET_OK && buf->len); - - switch (status) { - case SOUP_SOCKET_OK: - rev_read (sock, buf); - break; - - case SOUP_SOCKET_WOULD_BLOCK: - g_error ("Can't happen"); - break; - - default: - g_warning ("Socket error"); - /* fall through */ - - case SOUP_SOCKET_EOF: - rev_done (sock, buf); - break; - } -} - -static void -rev_read (SoupSocket *sock, GString *buf) -{ - SoupSocketIOStatus status; - char tmp[10]; - gsize nread; - gboolean eol; - - do { - status = soup_socket_read_until (sock, tmp, sizeof (tmp), - "\n", 1, &nread, &eol, NULL); - if (status == SOUP_SOCKET_OK) - g_string_append_len (buf, tmp, nread); - } while (status == SOUP_SOCKET_OK && !eol); - - switch (status) { - case SOUP_SOCKET_OK: - reverse (buf); - rev_write (sock, buf); - break; - - case SOUP_SOCKET_WOULD_BLOCK: - g_error ("Can't happen"); - break; - - default: - g_warning ("Socket error"); - /* fall through */ - - case SOUP_SOCKET_EOF: - rev_done (sock, buf); - break; - } -} - -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) -{ - GThread *thread; - GError *error = NULL; - - g_object_ref (client); - g_object_set (G_OBJECT (client), - SOUP_SOCKET_FLAG_NONBLOCKING, FALSE, - NULL); - - thread = g_thread_create (start_thread, client, FALSE, &error); - if (thread == NULL) { - g_warning ("Could not start thread: %s", error->message); - g_error_free (error); - g_object_unref (client); - } -} - -int -main (int argc, char **argv) -{ - SoupSocket *listener; - SoupAddressFamily family = SOUP_ADDRESS_FAMILY_IPV4; - guint port = SOUP_ADDRESS_ANY_PORT; - SoupAddress *addr; - GMainLoop *loop; - int opt; - - g_type_init (); - g_thread_init (NULL); - - while ((opt = getopt (argc, argv, "6p:")) != -1) { - switch (opt) { - case '6': - family = SOUP_ADDRESS_FAMILY_IPV6; - break; - case 'p': - port = atoi (optarg); - break; - default: - fprintf (stderr, "Usage: %s [-6] [-p port]\n", - argv[0]); - exit (1); - } - } - - addr = soup_address_new_any (family, port); - if (!addr) { - fprintf (stderr, "Could not create listener address\n"); - exit (1); - } - - listener = soup_socket_new (SOUP_SOCKET_LOCAL_ADDRESS, addr, - NULL); - g_object_unref (addr); - if (!listener || !soup_socket_listen (listener)) { - fprintf (stderr, "Could not create listening socket\n"); - exit (1); - } - g_signal_connect (listener, "new_connection", - G_CALLBACK (new_connection), NULL); - printf ("Listening on port %d\n", - soup_address_get_port ( - soup_socket_get_local_address (listener))); - - loop = g_main_loop_new (NULL, TRUE); - g_main_loop_run (loop); - - g_object_unref (listener); - return 0; -} diff --git a/tests/ssl-test.c b/tests/ssl-test.c index c773d707..13eed661 100644 --- a/tests/ssl-test.c +++ b/tests/ssl-test.c @@ -137,7 +137,8 @@ async_read (SoupSocket *sock, gpointer user_data) do { status = soup_socket_read (sock, data->readbuf + data->total, - BUFSIZE - data->total, &n, &error); + BUFSIZE - data->total, &n, + NULL, &error); if (status == SOUP_SOCKET_OK) data->total += n; } while (status == SOUP_SOCKET_OK && data->total < BUFSIZE); @@ -165,7 +166,8 @@ async_write (SoupSocket *sock, gpointer user_data) do { status = soup_socket_write (sock, data->writebuf + data->total, - BUFSIZE - data->total, &n, &error); + BUFSIZE - data->total, &n, + NULL, &error); if (status == SOUP_SOCKET_OK) data->total += n; } while (status == SOUP_SOCKET_OK && data->total < BUFSIZE); @@ -283,13 +285,13 @@ main (int argc, char **argv) SOUP_SOCKET_SSL_CREDENTIALS, creds, NULL); g_object_unref (addr); - status = soup_socket_connect_sync (sock); + status = soup_socket_connect_sync (sock, NULL); if (status != SOUP_STATUS_OK) { g_error ("Could not create client socket: %s", soup_status_get_phrase (status)); } - soup_socket_start_ssl (sock); + soup_socket_start_ssl (sock, NULL); /* Now spawn server thread */ server = g_thread_create (server_thread, GINT_TO_POINTER (listener), @@ -302,7 +304,8 @@ main (int argc, char **argv) total = 0; while (total < BUFSIZE) { status = soup_socket_write (sock, writebuf + total, - BUFSIZE - total, &n, &error); + BUFSIZE - total, &n, + NULL, &error); if (status != SOUP_SOCKET_OK) g_error ("Sync write got status %d: %s", status, error ? error->message : "(unknown)"); @@ -312,7 +315,8 @@ main (int argc, char **argv) total = 0; while (total < BUFSIZE) { status = soup_socket_read (sock, readbuf + total, - BUFSIZE - total, &n, &error); + BUFSIZE - total, &n, + NULL, &error); if (status != SOUP_SOCKET_OK) g_error ("Sync read got status %d: %s", status, error ? error->message : "(unknown)"); |