New API for doing socket IO. Works both synchronously and asynchronously,
authorDan Winship <danw@src.gnome.org>
Tue, 19 Aug 2003 19:04:09 +0000 (19:04 +0000)
committerDan Winship <danw@src.gnome.org>
Tue, 19 Aug 2003 19:04:09 +0000 (19:04 +0000)
* libsoup/soup-socket.c (soup_socket_read,
soup_socket_read_until, soup_socket_write): New API for doing
socket IO. Works both synchronously and asynchronously, and
buffers data to prevent the "100 Continue" problem.
(soup_socket_set_flag): Replaces formerly-private
soup_set_sockopts. (primarily to let the caller turn off
SOUP_SOCKET_FLAG_NONBLOCKING).

* libsoup/soup-transfer.c (soup_transfer_read,
soup_transfer_write, soup_transfer_write_simple): Take a
SoupSocket instead of a GIOChannel. Use the new socket IO api.
Changed the prototypes of some of the callbacks to be less
hackish.

* libsoup/soup-connection.c (soup_connection_get_socket): Replaces
soup_connection_get_iochannel.

* libsoup/soup-message.c: Fix up for soup-transfer changes

* libsoup/soup-queue.c: Likewise

* libsoup/soup-server.c: Likewise

* tests/revserver.c: A slightly more complicated replacement for
timeserver. (Does both reads and writes)

16 files changed:
ChangeLog
libsoup/soup-connection.c
libsoup/soup-connection.h
libsoup/soup-error.h
libsoup/soup-message.c
libsoup/soup-queue.c
libsoup/soup-server.c
libsoup/soup-socket.c
libsoup/soup-socket.h
libsoup/soup-transfer.c
libsoup/soup-transfer.h
libsoup/soup-uri.c
tests/.cvsignore
tests/Makefile.am
tests/revserver.c [new file with mode: 0644]
tests/timeserver.c [deleted file]

index 0dd77bf..086cfcb 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,8 +1,36 @@
 2003-08-19  Dan Winship  <danw@ximian.com>
 
+       * libsoup/soup-socket.c (soup_socket_read,
+       soup_socket_read_until, soup_socket_write): New API for doing
+       socket IO. Works both synchronously and asynchronously, and
+       buffers data to prevent the "100 Continue" problem.
+       (soup_socket_set_flag): Replaces formerly-private
+       soup_set_sockopts. (primarily to let the caller turn off
+       SOUP_SOCKET_FLAG_NONBLOCKING).
+
+       * libsoup/soup-transfer.c (soup_transfer_read,
+       soup_transfer_write, soup_transfer_write_simple): Take a
+       SoupSocket instead of a GIOChannel. Use the new socket IO api.
+       Changed the prototypes of some of the callbacks to be less
+       hackish.
+
+       * libsoup/soup-connection.c (soup_connection_get_socket): Replaces
+       soup_connection_get_iochannel.
+
+       * libsoup/soup-message.c: Fix up for soup-transfer changes
+
+       * libsoup/soup-queue.c: Likewise
+
+       * libsoup/soup-server.c: Likewise
+
+       * tests/revserver.c: A slightly more complicated replacement for
+       timeserver. (Does both reads and writes)
+
+2003-08-19  Dan Winship  <danw@ximian.com>
+
        * libsoup/soup-socks.[ch]: Remove this. RC doesn't let you
-       configure it anyway, and no one has complained, and it looks like
-       the SOCKS5 auth code doesn't actually work anyway...
+       configure it, and no one has complained, and it looks like the
+       SOCKS5 auth code doesn't actually work anyway...
 
        * libsoup/soup-queue.c (proxy_connect): Remove SOCKS code.
 
index d5f1f19..20d104c 100644 (file)
@@ -155,23 +155,17 @@ soup_connection_is_connected (SoupConnection *conn)
 }
 
 /**
- * soup_connection_get_iochannel:
+ * soup_connection_get_socket:
  * @conn: a #SoupConnection.
  *
- * Returns a #GIOChannel used for IO operations on the network
- * connection represented by @conn.
- *
- * Return value: a pointer to the #GIOChannel used for IO on @conn
+ * Return value: @conn's socket
  */
-GIOChannel *
-soup_connection_get_iochannel (SoupConnection *conn)
+SoupSocket *
+soup_connection_get_socket (SoupConnection *conn)
 {
        g_return_val_if_fail (SOUP_IS_CONNECTION (conn), NULL);
 
-       if (!conn->priv->socket)
-               return NULL;
-
-       return soup_socket_get_iochannel (conn->priv->socket);
+       return conn->priv->socket;
 }
 
 
index 70d4d4c..8d19722 100644 (file)
@@ -41,7 +41,7 @@ void            soup_connection_start_ssl      (SoupConnection *conn);
 void            soup_connection_disconnect     (SoupConnection *conn);
 gboolean        soup_connection_is_connected   (SoupConnection *conn);
 
-GIOChannel     *soup_connection_get_iochannel  (SoupConnection *conn);
+SoupSocket     *soup_connection_get_socket     (SoupConnection *conn);
 
 void            soup_connection_set_in_use     (SoupConnection *conn, 
                                                gboolean        in_use);
index 51803b6..717e7fa 100644 (file)
@@ -37,13 +37,13 @@ typedef enum {
        SOUP_ERROR_CANCELLED                = 1,
        SOUP_ERROR_CANT_RESOLVE             = 2,
        SOUP_ERROR_CANT_RESOLVE_PROXY       = 3,
-       SOUP_ERROR_CANT_CONNECT             = 2,
-       SOUP_ERROR_CANT_CONNECT_PROXY       = 3,
-       SOUP_ERROR_IO                       = 4,
-       SOUP_ERROR_MALFORMED                = 5,
-       SOUP_ERROR_CANT_AUTHENTICATE        = 6,
-       SOUP_ERROR_CANT_AUTHENTICATE_PROXY  = 7,
-       SOUP_ERROR_SSL_FAILED               = 8,
+       SOUP_ERROR_CANT_CONNECT             = 4,
+       SOUP_ERROR_CANT_CONNECT_PROXY       = 5,
+       SOUP_ERROR_IO                       = 6,
+       SOUP_ERROR_MALFORMED                = 7,
+       SOUP_ERROR_CANT_AUTHENTICATE        = 8,
+       SOUP_ERROR_CANT_AUTHENTICATE_PROXY  = 9,
+       SOUP_ERROR_SSL_FAILED               = 10,
 
        /*
         * HTTP Response Codes
index e64ae89..b477063 100644 (file)
@@ -144,16 +144,15 @@ soup_message_copy (SoupMessage *req)
 }
 
 static void 
-release_connection (const SoupDataBuffer *data,
-                   gpointer              user_data)
+release_connection (char     *body,
+                   guint     len,
+                   gpointer  user_data)
 {
        SoupConnection *conn = user_data;
 
        soup_connection_set_in_use (conn, FALSE);
        g_object_unref (conn);
-
-       if (data->owner == SOUP_BUFFER_SYSTEM_OWNED)
-               g_free (data->body);
+       g_free (body);
 }
 
 static void 
@@ -553,14 +552,14 @@ requeue_read_error (gboolean body_started, gpointer user_data)
 }
 
 static void
-requeue_read_finished (const SoupDataBuffer *buf,
-                      gpointer        user_data)
+requeue_read_finished (char     *body,
+                      guint     len,
+                      gpointer  user_data)
 {
        SoupMessage *msg = user_data;
        SoupConnection *conn = msg->connection;
 
-       if (buf->owner == SOUP_BUFFER_SYSTEM_OWNED)
-               g_free (buf->body);
+       g_free (body);
 
        if (soup_connection_is_connected (conn)) {
                soup_connection_mark_old (conn);
index 575b7fa..9672883 100644 (file)
@@ -116,9 +116,10 @@ soup_queue_error_cb (gboolean body_started, gpointer user_data)
 }
 
 static void
-soup_queue_read_headers_cb (const GString        *headers,
+soup_queue_read_headers_cb (char                 *headers,
+                           guint                 headers_len,
                             SoupTransferEncoding *encoding,
-                           gint                 *content_len,
+                           int                  *content_len,
                            gpointer              user_data)
 {
        SoupMessage *req = user_data;
@@ -127,12 +128,11 @@ soup_queue_read_headers_cb (const GString        *headers,
        GHashTable *resp_hdrs;
        SoupMethodId meth_id;
 
-       if (!soup_headers_parse_response (headers->str, 
-                                         headers->len, 
+       if (!soup_headers_parse_response (headers, headers_len,
                                          req->response_headers,
                                          &version,
                                          &req->errorcode,
-                                         (gchar **) &req->errorphrase)) {
+                                         (char **) &req->errorphrase)) {
                soup_message_set_error_full (req, 
                                             SOUP_ERROR_MALFORMED,
                                             "Unable to parse response "
@@ -210,14 +210,15 @@ soup_queue_read_headers_cb (const GString        *headers,
 }
 
 static void
-soup_queue_read_chunk_cb (const SoupDataBuffer *data,
-                         gpointer              user_data)
+soup_queue_read_chunk_cb (const char *chunk,
+                         guint       len,
+                         gpointer    user_data)
 {
        SoupMessage *req = user_data;
 
-       req->response.owner = data->owner;
-       req->response.length = data->length;
-       req->response.body = data->body;
+       req->response.owner = SOUP_BUFFER_STATIC;
+       req->response.length = len;
+       req->response.body = (char *)chunk;
 
        soup_message_run_handlers (req, SOUP_HANDLER_BODY_CHUNK);
 
@@ -225,8 +226,9 @@ soup_queue_read_chunk_cb (const SoupDataBuffer *data,
 }
 
 static void
-soup_queue_read_done_cb (const SoupDataBuffer *data,
-                        gpointer              user_data)
+soup_queue_read_done_cb (char     *body,
+                        guint     len,
+                        gpointer  user_data)
 {
        SoupMessage *req = user_data;
        const char *connection;
@@ -240,21 +242,21 @@ soup_queue_read_done_cb (const SoupDataBuffer *data,
        else
                soup_connection_mark_old (req->connection);
 
-       req->response.owner = data->owner;
-       req->response.length = data->length;
-       req->response.body = data->body;
+       req->response.owner = SOUP_BUFFER_SYSTEM_OWNED;
+       req->response.length = len;
+       req->response.body = body;
 
        soup_transfer_read_unref (req->priv->read_tag);
 
        if (req->errorclass == SOUP_ERROR_CLASS_INFORMATIONAL) {
-               GIOChannel *channel;
+               SoupSocket *sock;
                gboolean overwrt;
 
-               channel = soup_connection_get_iochannel (req->connection);
+               sock = soup_connection_get_socket (req->connection);
                overwrt = req->priv->msg_flags & SOUP_MESSAGE_OVERWRITE_CHUNKS;
 
                req->priv->read_tag = 
-                       soup_transfer_read (channel,
+                       soup_transfer_read (sock,
                                            overwrt,
                                            soup_queue_read_headers_cb,
                                            soup_queue_read_chunk_cb,
@@ -450,11 +452,11 @@ soup_queue_write_done_cb (gpointer user_data)
 static void
 start_request (SoupContext *ctx, SoupMessage *req)
 {
-       GIOChannel *channel;
+       SoupSocket *sock;
        gboolean overwrt; 
 
-       channel = soup_connection_get_iochannel (req->connection);
-       if (!channel) {
+       sock = soup_connection_get_socket (req->connection);
+       if (!socket) {  /* FIXME */
                SoupProtocol proto;
                gchar *phrase;
 
@@ -481,7 +483,7 @@ start_request (SoupContext *ctx, SoupMessage *req)
        }
 
        req->priv->write_tag = 
-               soup_transfer_write_simple (channel,
+               soup_transfer_write_simple (sock,
                                            soup_get_request_header (req),
                                            &req->request,
                                            soup_queue_write_done_cb,
@@ -491,7 +493,7 @@ start_request (SoupContext *ctx, SoupMessage *req)
        overwrt = req->priv->msg_flags & SOUP_MESSAGE_OVERWRITE_CHUNKS;
 
        req->priv->read_tag = 
-               soup_transfer_read (channel,
+               soup_transfer_read (sock,
                                    overwrt,
                                    soup_queue_read_headers_cb,
                                    soup_queue_read_chunk_cb,
@@ -581,7 +583,8 @@ soup_queue_connect_cb (SoupContext          *ctx,
        SoupMessage *req = user_data;
 
        req->priv->connect_tag = NULL;
-       g_object_ref (conn);
+       if (conn)
+               g_object_ref (conn);
        if (req->connection)
                g_object_unref (req->connection);
        req->connection = conn;
index 3a2aa51..9490459 100644 (file)
 #include <config.h>
 #endif
 
-#ifdef HAVE_SYS_FILIO_H
-#include <sys/filio.h>
-#endif
-
-#ifdef HAVE_SYS_IOCTL_H
-#include <sys/ioctl.h>
-#endif
-
-#include <string.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 #include <unistd.h>
 
 #include "soup-server.h"
@@ -42,6 +34,7 @@ struct SoupServerPrivate {
        GMainLoop         *loop;
 
        SoupSocket        *listen_sock;
+       GSList            *client_socks;
 
        GHashTable        *handlers; /* KEY: path, VALUE: SoupServerHandler */
        SoupServerHandler *default_handler;
@@ -54,7 +47,6 @@ struct SoupServerMessage {
        gboolean     finished;
 };
 
-
 static void
 init (GObject *object)
 {
@@ -94,6 +86,14 @@ finalize (GObject *object)
        if (server->priv->listen_sock)
                g_object_unref (server->priv->listen_sock);
 
+       while (server->priv->client_socks) {
+               SoupSocket *sock = server->priv->client_socks->data;
+
+               soup_socket_disconnect (sock);
+               server->priv->client_socks =
+                       g_slist_remove (server->priv->client_socks, sock);
+       }
+
        if (server->priv->default_handler)
                free_handler (server, server->priv->default_handler);
 
@@ -189,14 +189,7 @@ free_chunk (gpointer chunk, gpointer notused)
        g_free (buf);
 }
 
-typedef struct {
-       SoupServer *server;
-       SoupSocket *server_sock;
-} ServerConnectData;
-
-static gboolean start_another_request (GIOChannel    *server_chan,
-                                      GIOCondition   condition, 
-                                      gpointer       user_data);
+static void start_request (SoupServer *, SoupSocket *);
 
 static gboolean
 check_close_connection (SoupMessage *msg)
@@ -223,7 +216,7 @@ check_close_connection (SoupMessage *msg)
        }
 
        return close_connection;
-} /* check_close_connection */
+}
 
 static void
 destroy_message (SoupMessage *msg)
@@ -233,35 +226,15 @@ destroy_message (SoupMessage *msg)
        SoupServerMessage *server_msg = msg->priv->server_msg;
 
        if (server_sock) {
-               GIOChannel *chan;
-
-               chan = soup_socket_get_iochannel (server_sock);
-
                /*
                 * Close the socket if we're using HTTP/1.0 and
                 * "Connection: keep-alive" isn't specified, or if we're
                 * using HTTP/1.1 and "Connection: close" was specified.
                 */
-               if (check_close_connection (msg)) {
-                       g_io_channel_close (chan);
-                       g_object_unref (server_sock);
-               }
-               else {
-                       /*
-                        * Listen for another request on this connection
-                        */
-                       ServerConnectData *data;
-
-                       data = g_new0 (ServerConnectData, 1);
-                       data->server = msg->priv->server;
-                       data->server_sock = server_sock;
-
-                       g_io_add_watch (chan,
-                                       G_IO_IN|G_IO_PRI|
-                                       G_IO_ERR|G_IO_HUP|G_IO_NVAL,
-                                       start_another_request,
-                                       data);
-               }
+               if (check_close_connection (msg))
+                       soup_socket_disconnect (server_sock);
+               else
+                       start_request (server, server_sock);
        }
 
        if (server_msg) {
@@ -269,11 +242,10 @@ destroy_message (SoupMessage *msg)
                g_slist_free (server_msg->chunks);
                g_free (server_msg);
        }
-
-       g_object_unref (server);
-
        g_free ((char *) msg->method);
        soup_message_free (msg);
+
+       g_object_unref (server);
 }
 
 static void 
@@ -354,7 +326,6 @@ static void
 issue_bad_request (SoupMessage *msg)
 {
        GString *header;
-       GIOChannel *channel;
 
        set_response_error (msg, SOUP_ERROR_BAD_REQUEST, NULL, NULL);
 
@@ -362,9 +333,8 @@ issue_bad_request (SoupMessage *msg)
                                      FALSE,
                                      SOUP_TRANSFER_CONTENT_LENGTH);
 
-       channel = soup_socket_get_iochannel (msg->priv->server_sock);
        msg->priv->write_tag =
-               soup_transfer_write_simple (channel,
+               soup_transfer_write_simple (msg->priv->server_sock,
                                            header,
                                            &msg->response,
                                            write_done_cb,
@@ -373,17 +343,17 @@ issue_bad_request (SoupMessage *msg)
 }
 
 static void
-read_headers_cb (const GString        *headers,
+read_headers_cb (char                 *headers,
+                guint                 headers_len,
                 SoupTransferEncoding *encoding,
-                gint                 *content_len,
+                int                  *content_len,
                 gpointer              user_data)
 {
        SoupMessage *msg = user_data;
        SoupContext *ctx;
        char *req_path = NULL;
 
-       if (!soup_headers_parse_request (headers->str, 
-                                        headers->len, 
+       if (!soup_headers_parse_request (headers, headers_len,
                                         msg->request_headers, 
                                         (char **) &msg->method, 
                                         &req_path,
@@ -495,7 +465,8 @@ read_headers_cb (const GString        *headers,
 
 static void
 call_handler (SoupMessage          *req,
-             const SoupDataBuffer *req_data,
+             char                 *body,
+             guint                 len,
              const char           *handler_path)
 {
        SoupServer *server = req->priv->server;
@@ -504,9 +475,9 @@ call_handler (SoupMessage          *req,
 
        g_return_if_fail (req != NULL);
 
-       req->request.owner = req_data->owner;
-       req->request.length = req_data->length;
-       req->request.body = req_data->body;
+       req->request.owner = SOUP_BUFFER_SYSTEM_OWNED;
+       req->request.length = len;
+       req->request.body = body;
 
        req->status = SOUP_STATUS_FINISHED;
 
@@ -625,19 +596,18 @@ get_chunk_cb (SoupDataBuffer *out_next, gpointer user_data)
 }
 
 static void
-read_done_cb (const SoupDataBuffer *data,
-             gpointer              user_data)
+read_done_cb (char     *body,
+             guint     len,
+             gpointer  user_data)
 {
        SoupMessage *req = user_data;
        SoupSocket *server_sock = req->priv->server_sock;
-       GIOChannel *channel;
 
        soup_transfer_read_unref (req->priv->read_tag);
        req->priv->read_tag = 0;
 
-       call_handler (req, data, soup_context_get_uri (req->context)->path);
-
-       channel = soup_socket_get_iochannel (server_sock);
+       call_handler (req, body, len,
+                     soup_context_get_uri (req->context)->path);
 
        if (req->priv->server_msg) {
                SoupTransferEncoding encoding;
@@ -648,7 +618,7 @@ read_done_cb (const SoupDataBuffer *data,
                        encoding = SOUP_TRANSFER_CHUNKED;
 
                req->priv->write_tag = 
-                       soup_transfer_write (channel,
+                       soup_transfer_write (server_sock,
                                             encoding,
                                             get_header_cb,
                                             get_chunk_cb,
@@ -667,7 +637,7 @@ read_done_cb (const SoupDataBuffer *data,
                                              TRUE, 
                                              SOUP_TRANSFER_CONTENT_LENGTH);
                req->priv->write_tag = 
-                       soup_transfer_write_simple (channel,
+                       soup_transfer_write_simple (server_sock,
                                                    header,
                                                    &req->response,
                                                    write_done_cb,
@@ -678,82 +648,44 @@ read_done_cb (const SoupDataBuffer *data,
        return;
 }
 
-static SoupMessage *
-message_new (SoupServer *server)
+static void
+start_request (SoupServer *server, SoupSocket *server_sock)
 {
        SoupMessage *msg;
 
-       /*
-        * Create an empty message to hold request state.
-        */
+       /* Listen for another request on this connection */
        msg = soup_message_new (NULL, NULL);
-       if (msg) {
-               msg->method = NULL;
-               msg->priv->server = server;
-               g_object_ref (server);
-       }
-
-       return msg;
+       msg->method = NULL;
+       msg->priv->server = g_object_ref (server);
+       msg->priv->server_sock = server_sock;
+       msg->priv->read_tag = 
+               soup_transfer_read (server_sock,
+                                   FALSE,
+                                   read_headers_cb,
+                                   NULL,
+                                   read_done_cb,
+                                   error_cb,
+                                   msg);
 }
 
-static gboolean
-start_another_request (GIOChannel    *server_chan,
-                      GIOCondition   condition, 
-                      gpointer       user_data)
+static void
+socket_disconnected (SoupSocket *sock, SoupServer *server)
 {
-       ServerConnectData *data = user_data;
-       SoupMessage *msg;
-       int fd, cnt;
-
-       fd = g_io_channel_unix_get_fd (server_chan);
-
-       if (!(condition & G_IO_IN) || 
-           ioctl (fd, FIONREAD, &cnt) < 0 ||
-           cnt <= 0)
-               g_object_unref (data->server_sock);
-       else {
-               msg = message_new (data->server);
-               if (!msg) {
-                       g_warning ("Unable to create new incoming message\n");
-                       g_object_unref (data->server_sock);
-               } else {
-                       msg->priv->server_sock = data->server_sock;
-                       msg->priv->read_tag = 
-                               soup_transfer_read (server_chan,
-                                                   FALSE,
-                                                   read_headers_cb,
-                                                   NULL,
-                                                   read_done_cb,
-                                                   error_cb,
-                                                   msg);
-               }
-       }
-
-       g_free (data);
-       return FALSE;
+       server->priv->client_socks =
+               g_slist_remove (server->priv->client_socks, sock);
+       g_signal_handlers_disconnect_by_func (sock, socket_disconnected, server);
 }
 
 static void
 new_connection (SoupSocket *listner, SoupSocket *sock, gpointer user_data)
 {
        SoupServer *server = user_data;
-       SoupMessage *msg;
-
-       msg = message_new (server);
-       if (!msg) {
-               g_warning ("Unable to create new incoming message\n");
-               return;
-       }
 
-       msg->priv->server_sock = g_object_ref (sock);
-       msg->priv->read_tag = 
-               soup_transfer_read (soup_socket_get_iochannel (sock),
-                                   FALSE,
-                                   read_headers_cb,
-                                   NULL,
-                                   read_done_cb,
-                                   error_cb,
-                                   msg);
+       server->priv->client_socks =
+               g_slist_prepend (server->priv->client_socks, sock);
+       g_signal_connect (sock, "disconnected",
+                         G_CALLBACK (socket_disconnected), server);
+       start_request (server, sock);
 }
 
 void
index 6c24e8f..3014974 100644 (file)
@@ -2,10 +2,7 @@
 /*
  * soup-socket.c: Socket networking code.
  *
- * Based on code in David Helder's GNET Networking Library,
- * Copyright (C) 2000  David Helder & Andrew Lanoix.
- *
- * All else Copyright (C) 2000-2003, Ximian, Inc.
+ * Copyright (C) 2000-2003, Ximian, Inc.
  */
 
 #ifdef HAVE_CONFIG_H
@@ -14,6 +11,7 @@
 
 #include <errno.h>
 #include <fcntl.h>
+#include <signal.h>
 #include <string.h>
 #include <unistd.h>
 
@@ -30,6 +28,9 @@ static GObjectClass *parent_class;
 
 enum {
        CONNECT_RESULT,
+       READABLE,
+       WRITABLE,
+       DISCONNECTED,
        NEW_CONNECTION,
        LAST_SIGNAL
 };
@@ -43,6 +44,9 @@ struct SoupSocketPrivate {
 
        guint watch;
        gboolean server, ssl;
+
+       guint           read_tag, write_tag, error_tag;
+       GByteArray     *read_buf;
 };
 
 static void
@@ -93,6 +97,30 @@ class_init (GObjectClass *object_class)
                              soup_marshal_NONE__INT,
                              G_TYPE_NONE, 1,
                              G_TYPE_INT);
+       signals[READABLE] =
+               g_signal_new ("readable",
+                             G_OBJECT_CLASS_TYPE (object_class),
+                             G_SIGNAL_RUN_LAST,
+                             G_STRUCT_OFFSET (SoupSocketClass, readable),
+                             NULL, NULL,
+                             soup_marshal_NONE__NONE,
+                             G_TYPE_NONE, 0);
+       signals[WRITABLE] =
+               g_signal_new ("writable",
+                             G_OBJECT_CLASS_TYPE (object_class),
+                             G_SIGNAL_RUN_LAST,
+                             G_STRUCT_OFFSET (SoupSocketClass, writable),
+                             NULL, NULL,
+                             soup_marshal_NONE__NONE,
+                             G_TYPE_NONE, 0);
+       signals[DISCONNECTED] =
+               g_signal_new ("disconnected",
+                             G_OBJECT_CLASS_TYPE (object_class),
+                             G_SIGNAL_RUN_LAST,
+                             G_STRUCT_OFFSET (SoupSocketClass, disconnected),
+                             NULL, NULL,
+                             soup_marshal_NONE__NONE,
+                             G_TYPE_NONE, 0);
        signals[NEW_CONNECTION] =
                g_signal_new ("new_connection",
                              G_OBJECT_CLASS_TYPE (object_class),
@@ -118,31 +146,38 @@ soup_socket_new (void)
        return g_object_new (SOUP_TYPE_SOCKET, NULL);
 }
 
-#define SOUP_SOCKET_NONBLOCKING (1<<0)
-#define SOUP_SOCKET_NONBUFFERED (1<<1)
-#define SOUP_SOCKET_REUSEADDR   (1<<2)
-
-static void
-soup_set_sockopts (int sockfd, int opts)
+void
+soup_socket_set_flag (SoupSocket *sock, SoupSocketFlag flag, gboolean value)
 {
-       int flags;
-
-       if (opts & SOUP_SOCKET_NONBLOCKING) {
-               flags = fcntl (sockfd, F_GETFL, 0);
-               if (flags != -1)
-                       fcntl (sockfd, F_SETFL, flags | O_NONBLOCK);
-       }
+       int fdflags, opt;
 
-       if (opts & SOUP_SOCKET_NONBUFFERED) {
-               flags = 1;
-               setsockopt (sockfd, IPPROTO_TCP, TCP_NODELAY,
-                           &flags, sizeof (flags));
-       }
-
-       if (opts & SOUP_SOCKET_REUSEADDR) {
-               flags = 1;
-               setsockopt (sockfd, SOL_SOCKET, SO_REUSEADDR,
-                           &flags, sizeof (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);
+
+               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;
        }
 }
 
@@ -234,8 +269,8 @@ soup_socket_connect (SoupSocket *sock, SoupAddress *remote_addr)
        sock->priv->sockfd = socket (sa->sa_family, SOCK_STREAM, 0);
        if (sock->priv->sockfd < 0)
                goto cant_connect;
-       soup_set_sockopts (sock->priv->sockfd,
-                          SOUP_SOCKET_NONBLOCKING | SOUP_SOCKET_NONBUFFERED);
+       soup_socket_set_flag (sock, SOUP_SOCKET_FLAG_NONBLOCKING, TRUE);
+       soup_socket_set_flag (sock, SOUP_SOCKET_FLAG_NODELAY, TRUE);
 
        /* Connect (non-blocking) */
        status = connect (sock->priv->sockfd, sa, len);
@@ -285,18 +320,19 @@ listen_watch (GIOChannel* iochannel, GIOCondition condition, gpointer data)
        if (sockfd == -1)
                return TRUE;
 
-       soup_set_sockopts (sockfd,
-                          SOUP_SOCKET_NONBLOCKING | SOUP_SOCKET_NONBUFFERED);
-
        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->remote_addr = soup_address_new_from_sockaddr ((struct sockaddr *)&sa, sa_len);
 
        new->priv->server = TRUE;
        if (sock->priv->ssl) {
                new->priv->ssl = TRUE;
                soup_socket_start_ssl (new);
-       }
+       } else
+               soup_socket_get_iochannel (new);
 
        g_signal_emit (sock, signals[NEW_CONNECTION], 0, new);
        g_object_unref (new);
@@ -336,8 +372,8 @@ soup_socket_listen (SoupSocket *sock, SoupAddress *local_addr)
        sock->priv->sockfd = socket (sa->sa_family, SOCK_STREAM, 0);
        if (sock->priv->sockfd < 0)
                goto cant_listen;
-       soup_set_sockopts (sock->priv->sockfd,
-                          SOUP_SOCKET_NONBLOCKING | SOUP_SOCKET_REUSEADDR);
+       soup_socket_set_flag (sock, SOUP_SOCKET_FLAG_NONBLOCKING, TRUE);
+       soup_socket_set_flag (sock, SOUP_SOCKET_FLAG_REUSEADDR, TRUE);
 
        /* Bind */
        if (bind (sock->priv->sockfd, sa, sa_len) != 0)
@@ -476,10 +512,53 @@ soup_socket_get_iochannel (SoupSocket *sock)
                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)
+{
+       g_return_if_fail (SOUP_IS_SOCKET (sock));
+
+       if (!sock->priv->iochannel)
+               return;
+
+       g_io_channel_unref (sock->priv->iochannel);
+       sock->priv->iochannel = NULL;
+
+       if (sock->priv->read_tag) {
+               g_source_remove (sock->priv->read_tag);
+               sock->priv->read_tag = 0;
+       }
+       if (sock->priv->write_tag) {
+               g_source_remove (sock->priv->write_tag);
+               sock->priv->write_tag = 0;
+       }
+       if (sock->priv->error_tag) {
+               g_source_remove (sock->priv->error_tag);
+               sock->priv->error_tag = 0;
+       }
+
+       /* Give all readers a chance to notice the connection close */
+       g_signal_emit (sock, signals[READABLE], 0);
+
+       /* FIXME: can't disconnect until all data is read */
+
+       /* Then let everyone know we're disconnected */
+       g_signal_emit (sock, signals[DISCONNECTED], 0);
+}
+
+gboolean
+soup_socket_is_connected (SoupSocket *sock)
+{
+       g_return_val_if_fail (SOUP_IS_SOCKET (sock), FALSE);
+
+       return sock->priv->iochannel != NULL;
+}
+
 
 SoupAddress *
 soup_socket_get_local_address (SoupSocket *sock)
@@ -514,3 +593,234 @@ soup_socket_get_remote_address (SoupSocket *sock)
 
        return sock->priv->remote_addr;
 }
+
+
+
+
+static gboolean
+socket_read_watch (GIOChannel *chan, GIOCondition cond, gpointer user_data)
+{
+       SoupSocket *sock = user_data;
+
+       sock->priv->read_tag = 0;
+       g_signal_emit (sock, signals[READABLE], 0);
+
+       return FALSE;
+}
+
+static SoupSocketIOStatus
+read_from_network (SoupSocket *sock, gpointer buffer, gsize len, gsize *nread)
+{
+       GIOStatus status;
+
+       if (!sock->priv->iochannel)
+               return SOUP_SOCKET_EOF;
+
+       status = g_io_channel_read_chars (sock->priv->iochannel,
+                                         buffer, len, nread, NULL);
+       switch (status) {
+       case G_IO_STATUS_NORMAL:
+       case G_IO_STATUS_AGAIN:
+               if (*nread > 0)
+                       return SOUP_SOCKET_OK;
+
+               if (!sock->priv->read_tag) {
+                       sock->priv->read_tag =
+                               g_io_add_watch (sock->priv->iochannel, G_IO_IN,
+                                               socket_read_watch, sock);
+               }
+               return SOUP_SOCKET_WOULD_BLOCK;
+
+       case G_IO_STATUS_EOF:
+               return SOUP_SOCKET_EOF;
+
+       default:
+               soup_socket_disconnect (sock);
+               return SOUP_SOCKET_ERROR;
+       }
+}
+
+static SoupSocketIOStatus
+read_from_buf (SoupSocket *sock, gpointer buffer, gsize len, gsize *nread)
+{
+       GByteArray *read_buf = sock->priv->read_buf;
+
+       *nread = MIN (read_buf->len, len);
+       memcpy (buffer, read_buf->data, *nread);
+
+       if (*nread == read_buf->len) {
+               g_byte_array_free (read_buf, TRUE);
+               sock->priv->read_buf = NULL;
+       } else {
+               memcpy (read_buf->data, read_buf->data + *nread, 
+                       read_buf->len - *nread);
+               g_byte_array_set_size (read_buf, read_buf->len - *nread);
+       }
+
+       return SOUP_SOCKET_OK;
+}
+
+/**
+ * soup_socket_read:
+ * @sock: the socket
+ * @buffer: buffer to read into
+ * @len: size of @buffer in bytes
+ * @nread: on return, the number of bytes read into @buffer
+ *
+ * Attempts to read up to @len bytes from @sock into @buffer. If some
+ * data is successfully read, soup_socket_read() will return
+ * %SOUP_SOCKET_OK, and *@nread will contain the number of bytes
+ * actually read.
+ *
+ * If @sock is non-blocking, and no data is available, the return
+ * value will be %SOUP_SOCKET_WOULD_BLOCK. In this case, the caller
+ * can connect to the %readable signal to know when there is more data
+ * to read. (NB: You MUST read all available data off the socket
+ * first. The %readable signal will only be emitted after
+ * soup_socket_read() has returned %SOUP_SOCKET_WOULD_BLOCK.)
+ *
+ * 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).
+ **/
+SoupSocketIOStatus
+soup_socket_read (SoupSocket *sock, gpointer buffer, gsize len, gsize *nread)
+{
+       g_return_val_if_fail (SOUP_IS_SOCKET (sock), SOUP_SOCKET_ERROR);
+
+       if (sock->priv->read_buf)
+               return read_from_buf (sock, buffer, len, nread);
+       else
+               return read_from_network (sock, buffer, len, nread);
+}
+
+/**
+ * soup_socket_read_until:
+ * @sock: the socket
+ * @buffer: buffer to read into
+ * @len: size of @buffer in bytes
+ * @boundary: boundary to read until
+ * @boundary_len: length of @boundary in bytes
+ * @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
+ *
+ * Like soup_socket_read(), but reads no further than the first
+ * occurrence of @boundary. (If the boundary is found, it will be
+ * included in the returned data, and *@got_boundary will be set to
+ * %TRUE.) Any data after the boundary will returned in future reads.
+ *
+ * Return value: as for soup_socket_read()
+ **/
+SoupSocketIOStatus
+soup_socket_read_until (SoupSocket *sock, gpointer buffer, gsize len,
+                       gconstpointer boundary, gsize boundary_len,
+                       gsize *nread, gboolean *got_boundary)
+{
+       SoupSocketIOStatus status;
+       GByteArray *read_buf;
+       guint match_len, prev_len;
+       guint8 *p, *end;
+
+       g_return_val_if_fail (SOUP_IS_SOCKET (sock), SOUP_SOCKET_ERROR);
+       g_return_val_if_fail (len >= boundary_len, SOUP_SOCKET_ERROR);
+
+       *got_boundary = FALSE;
+
+       if (!sock->priv->read_buf)
+               sock->priv->read_buf = g_byte_array_new ();
+       read_buf = sock->priv->read_buf;
+
+       if (read_buf->len < boundary_len) {
+               prev_len = read_buf->len;
+               g_byte_array_set_size (read_buf, len);
+               status = read_from_network (sock,
+                                           read_buf->data + prev_len,
+                                           len - prev_len, nread);
+               read_buf->len = prev_len + *nread;
+
+               if (status != SOUP_SOCKET_OK)
+                       return status;
+       }
+
+       /* Scan for the boundary */
+       end = read_buf->data + read_buf->len;
+       for (p = read_buf->data; p <= end - boundary_len; p++) {
+               if (!memcmp (p, boundary, boundary_len)) {
+                       p += boundary_len;
+                       *got_boundary = TRUE;
+                       break;
+               }
+       }
+
+       /* Return everything up to 'p' (which is either just after the
+        * boundary, or @boundary_len - 1 bytes before the end of the
+        * buffer).
+        */
+       match_len = p - read_buf->data;
+       return read_from_buf (sock, buffer, MIN (len, match_len), nread);
+}
+
+static gboolean
+socket_write_watch (GIOChannel *chan, GIOCondition condition, gpointer user_data)
+{
+       SoupSocket *sock = user_data;
+
+       sock->priv->write_tag = 0;
+       g_signal_emit (sock, signals[WRITABLE], 0);
+
+       return FALSE;
+}
+
+/**
+ * soup_socket_write:
+ * @sock: the socket
+ * @buffer: data to write
+ * @len: size of @buffer, in bytes
+ * @nwrite: on return, number of bytes written
+ *
+ * Attempts to write @len bytes from @buffer to @sock. If some data is
+ * successfully written, the resturn status will be
+ * %SOUP_SOCKET_SUCCESS, and *@nwrote will contain the number of bytes
+ * actually written.
+ *
+ * If @sock is non-blocking, and no data could be written right away,
+ * the return value will be %SOUP_SOCKET_WOULD_BLOCK. In this case,
+ * the caller can connect to the %writable signal to know when more
+ * data can be written. (NB: %writable is only emitted after a
+ * %SOUP_SOCKET_WOULD_BLOCK.)
+ *
+ * Return value: a #SoupSocketIOStatus, as described above (or
+ * %SOUP_SOCKET_EOF or %SOUP_SOCKET_ERROR).
+ **/
+SoupSocketIOStatus
+soup_socket_write (SoupSocket *sock, gconstpointer buffer,
+                  gsize len, gsize *nwrote)
+{
+       GIOStatus status;
+       gpointer pipe_handler;
+
+       g_return_val_if_fail (SOUP_IS_SOCKET (sock), SOUP_SOCKET_ERROR);
+
+       if (!sock->priv->iochannel)
+               return SOUP_SOCKET_EOF;
+       if (sock->priv->write_tag)
+               return SOUP_SOCKET_WOULD_BLOCK;
+
+       pipe_handler = signal (SIGPIPE, SIG_IGN);
+       status = g_io_channel_write_chars (sock->priv->iochannel,
+                                          buffer, len, nwrote, NULL);
+       signal (SIGPIPE, pipe_handler);
+       if (status != G_IO_STATUS_NORMAL && status != G_IO_STATUS_AGAIN) {
+               soup_socket_disconnect (sock);
+               return SOUP_SOCKET_ERROR;
+       }
+
+       if (*nwrote)
+               return SOUP_SOCKET_OK;
+
+       sock->priv->write_tag =
+               g_io_add_watch (sock->priv->iochannel, G_IO_OUT,
+                               socket_write_watch, sock);
+       return SOUP_SOCKET_WOULD_BLOCK;
+}
index 036d52f..45300a2 100644 (file)
@@ -30,13 +30,25 @@ typedef struct {
 
        /* signals */
        void (*connect_result) (SoupSocket *, SoupKnownErrorCode);
+       void (*readable)       (SoupSocket *);
+       void (*writable)       (SoupSocket *);
+       void (*disconnected)   (SoupSocket *);
 
        void (*new_connection) (SoupSocket *, SoupSocket *);
 } SoupSocketClass;
 
+typedef enum {
+       SOUP_SOCKET_FLAG_NONBLOCKING,
+       SOUP_SOCKET_FLAG_NODELAY,
+       SOUP_SOCKET_FLAG_REUSEADDR,
+} SoupSocketFlag;
+
 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_connect            (SoupSocket         *sock,
                                               SoupAddress        *rem_addr);
@@ -44,6 +56,8 @@ gboolean       soup_socket_listen             (SoupSocket         *sock,
                                               SoupAddress        *local_addr);
 void           soup_socket_start_ssl          (SoupSocket         *sock);
 
+void           soup_socket_disconnect         (SoupSocket         *sock);
+gboolean       soup_socket_is_connected       (SoupSocket         *sock);
 
 typedef void (*SoupSocketCallback)            (SoupSocket         *sock,
                                               SoupKnownErrorCode  status,
@@ -68,4 +82,30 @@ GIOChannel    *soup_socket_get_iochannel      (SoupSocket         *sock);
 SoupAddress   *soup_socket_get_local_address  (SoupSocket         *sock);
 SoupAddress   *soup_socket_get_remote_address (SoupSocket         *sock);
 
+
+typedef enum {
+       SOUP_SOCKET_OK,
+       SOUP_SOCKET_WOULD_BLOCK,
+       SOUP_SOCKET_EOF,
+       SOUP_SOCKET_ERROR
+} SoupSocketIOStatus;
+
+SoupSocketIOStatus  soup_socket_read       (SoupSocket         *sock,
+                                           gpointer            buffer,
+                                           guint               len,
+                                           guint              *nread);
+SoupSocketIOStatus  soup_socket_read_until (SoupSocket         *sock,
+                                           gpointer            buffer,
+                                           guint               len,
+                                           gconstpointer       boundary,
+                                           guint               boundary_len,
+                                           guint              *nread,
+                                           gboolean           *got_boundary);
+
+SoupSocketIOStatus  soup_socket_write      (SoupSocket         *sock,
+                                           gconstpointer       buffer,
+                                           guint               len,
+                                           guint              *nwrote);
+
+
 #endif /* SOUP_SOCKET_H */
index 19ec04d..bcd81c6 100644 (file)
 #include "soup-transfer.h"
 #include "soup-private.h"
 
-#undef DUMP
-
-#ifdef DUMP
-static void
-DUMP_READ (guchar *data, gint bytes_read) 
-{
-       gchar *buf = alloca (bytes_read + 1);
-       memcpy (buf, data, bytes_read);
-       buf[bytes_read] = '\0';
-       
-       g_warning ("READ %d\n----------\n%s\n----------\n", bytes_read, buf);
-}
-static void
-DUMP_WRITE (guchar *data, gint bytes_written) 
-{
-       gchar *buf = alloca (bytes_written + 1);
-       memcpy (buf, data, bytes_written);
-       buf[bytes_written] = '\0';
-
-       g_warning ("WRITE %d\n----------\n%s\n----------\n", bytes_written,buf);
-}
-#else
-#  define DUMP_READ(x,y)
-#  define DUMP_WRITE(x,y)
-#endif
-
-typedef struct {
-       /* 
-        * Length of the current chunk data. 
-        */
-       guint  len;
-
-       /* 
-        * Index into the recv buffer where this chunk's data begins.
-        * 0 if overwrite chunks is active.
-        */
-       guint  idx;
-} SoupTransferChunkState;
+typedef enum {
+       SOUP_READER_STATE_HEADERS,
+       SOUP_READER_STATE_READ_TO_EOF,
+       SOUP_READER_STATE_CONTENT_LENGTH,
+       SOUP_READER_STATE_CHUNK_SIZE,
+       SOUP_READER_STATE_CHUNK,
+       SOUP_READER_STATE_BETWEEN_CHUNKS,
+       SOUP_READER_STATE_TRAILERS
+} SoupReaderState;
 
 struct _SoupReader {
         int                    ref_count;
 
-       GIOChannel            *channel;
+       SoupSocket            *sock;
+       guint                  idle_tag;
        guint                  read_tag;
        guint                  err_tag;
 
-       /*
-        * If TRUE, a callback has been issued which references recv_buf.
-        * If the transfer is cancelled before a reference exists, the contents
-        * of recv_buf are free'd.
-        */
-       gboolean               callback_issued;
-
-       GByteArray            *recv_buf;
-       guint                  header_len;
-
-       gboolean               overwrite_chunks;
+       SoupReaderState        state;
+       GByteArray            *body_buf;
+       GByteArray            *meta_buf;
 
        SoupTransferEncoding   encoding;
-       guint                  content_length;
-       SoupTransferChunkState chunk_state;
+       guint                  read_length;
 
        SoupReadHeadersDoneFn  headers_done_cb;
        SoupReadChunkFn        read_chunk_cb;
@@ -91,26 +54,6 @@ struct _SoupReader {
        gpointer               user_data;
 };
 
-struct _SoupWriter {
-        int                    ref_count;
-
-       GIOChannel             *channel;
-       guint                   write_tag;
-       guint                   err_tag;
-
-       SoupTransferEncoding    encoding;
-       GByteArray             *write_buf;
-
-       gboolean                headers_done;
-       gint                    chunk_cnt;
-
-       SoupWriteGetHeaderFn    get_header_cb;
-       SoupWriteGetChunkFn     get_chunk_cb;
-       SoupWriteDoneFn         write_done_cb;
-       SoupWriteErrorFn        error_cb;
-       gpointer                user_data;
-};
-
 /* Stops reading and releases soup-transfer's ref. */
 static void
 soup_transfer_read_stop (SoupReader *r)
@@ -118,11 +61,16 @@ soup_transfer_read_stop (SoupReader *r)
        if (!r->err_tag)
                return;
 
-       g_source_remove (r->read_tag);
+       g_signal_handler_disconnect (r->sock, r->read_tag);
        r->read_tag = 0;
-       g_source_remove (r->err_tag);
+       g_signal_handler_disconnect (r->sock, r->err_tag);
        r->err_tag = 0;
 
+       if (r->idle_tag) {
+               g_source_remove (r->idle_tag);
+               r->idle_tag = 0;
+       }
+
        soup_transfer_read_unref (r);
 }
 
@@ -140,7 +88,11 @@ soup_transfer_read_unref (SoupReader *r)
                return TRUE;
 
        soup_transfer_read_stop (r);
-       g_byte_array_free (r->recv_buf, r->callback_issued ? FALSE : TRUE);
+       if (r->body_buf)
+               g_byte_array_free (r->body_buf, TRUE);
+       if (r->meta_buf)
+               g_byte_array_free (r->meta_buf, TRUE);
+       g_object_unref (r->sock);
        g_free (r);
        return FALSE;
 }
@@ -173,343 +125,242 @@ soup_transfer_read_set_callbacks (SoupReader             *r,
 static void
 issue_final_callback (SoupReader *r)
 {
-       SoupDataBuffer buf;
+       char *body;
+       guint len;
 
-       /* 
-        * Null terminate 
-        */
-       g_byte_array_append (r->recv_buf, "\0", 1);
-
-       buf.owner = SOUP_BUFFER_SYSTEM_OWNED;
-       buf.body = r->recv_buf->data;
-       buf.length = r->recv_buf->len - 1;
-
-       r->callback_issued = TRUE;
+       if (r->body_buf) {
+               /* 
+                * Null terminate. FIXME
+                */
+               g_byte_array_append (r->body_buf, "\0", 1);
+
+               body = r->body_buf->data;
+               len = r->body_buf->len - 1;
+               g_byte_array_free (r->body_buf, FALSE);
+               r->body_buf = NULL;
+       } else {
+               body = NULL;
+               len = 0;
+       }
 
        soup_transfer_read_ref (r);
        soup_transfer_read_stop (r);
 
-       (*r->read_done_cb) (&buf, r->user_data);
+       (*r->read_done_cb) (body, len, r->user_data);
        soup_transfer_read_unref (r);
 }
 
-static gboolean
-soup_transfer_read_error_cb (GIOChannel* iochannel,
-                            GIOCondition condition,
-                            SoupReader *r)
+static void
+reader_disconnected (SoupSocket *sock, SoupReader *r)
 {
-       gboolean body_started = r->recv_buf->len > r->header_len;
-
+       soup_transfer_read_ref (r);
        soup_transfer_read_stop (r);
 
        /*
         * Closing the connection to signify EOF is valid if content length is
         * unknown, but only if headers have been sent.
         */
-       if (r->header_len && r->encoding == SOUP_TRANSFER_UNKNOWN)
+       if (r->state == SOUP_READER_STATE_READ_TO_EOF)
                issue_final_callback (r);
-       else
-               (*r->error_cb) (body_started, r->user_data);
+       else {
+               (*r->error_cb) (r->state > SOUP_READER_STATE_HEADERS,
+                               r->user_data);
+       }
 
-       return FALSE;
+       soup_transfer_read_unref (r);
 }
 
-static void
-remove_block_at_index (GByteArray *arr, gint offset, gint length)
+static gboolean
+soup_reader_read_metadata (SoupReader *r, const char *boundary, int boundary_len)
 {
-       gchar *data;
+       SoupSocketIOStatus status;
+       char read_buf[RESPONSE_BLOCK_SIZE];
+       guint nread;
+       gboolean done;
 
-       g_return_if_fail (length != 0);
-       g_assert (arr->len >= (guint) offset + length);
+       do {
+               status = soup_socket_read_until (r->sock, read_buf,
+                                                sizeof (read_buf),
+                                                boundary, boundary_len,
+                                                &nread, &done);
+               switch (status) {
+               case SOUP_SOCKET_OK:
+                       g_byte_array_append (r->meta_buf, read_buf, nread);
+                       break;
 
-       data = &arr->data [offset];
+               case SOUP_SOCKET_ERROR:
+               case SOUP_SOCKET_EOF:
+                       reader_disconnected (r->sock, r);
+                       return FALSE;
 
-       g_memmove (data,
-                  data + length,
-                  arr->len - offset - length);
+               case SOUP_SOCKET_WOULD_BLOCK:
+                       return FALSE;
+               }
+       } while (!done);
 
-       g_byte_array_set_size (arr, arr->len - length);
+       return TRUE;
 }
 
-/* Parse chunk data in @arr. On return, *@datalen bytes of @arr contain
- * parsed data that can be returned to the caller. Return value is
- * TRUE if it parsed the last chunk, or FALSE if we need to read more
- * data.
- */
 static gboolean
-decode_chunk (SoupTransferChunkState *s,
-             GByteArray             *arr,
-             gint                   *datalen) 
+soup_reader_read_body_chunk (SoupReader *r, guint *size)
 {
-       gboolean ret = FALSE;
+       SoupSocketIOStatus status;
+       char read_buf[RESPONSE_BLOCK_SIZE];
+       guint nread, len = sizeof (read_buf);
 
-       *datalen = 0;
+       while (!size || *size > 0) {
+               if (size)
+                       len = MIN (len, *size);
 
-       while (TRUE) {
-               gint new_len = 0;
-               gint len = 0;
+               status = soup_socket_read (r->sock, read_buf, len, &nread);
 
-               if (s->len) {
-                       /* We're in the middle of a chunk. If we don't
-                        * have the entire chunk and the trailing CRLF
-                        * yet, read more.
-                        */
-                       if (s->idx + s->len + 2 > arr->len)
+               switch (status) {
+               case SOUP_SOCKET_OK:
+                       if (!nread)
                                break;
 
-                       /*
-                        * Increment datalen and s->idx, and remove
-                        * the trailing CRLF.
-                        */
-                       s->idx += s->len;
-                       *datalen += s->len;
-                       remove_block_at_index (arr, s->idx, 2);
-
-                       /*
-                        * Ready for the next chunk.
-                        */
-                       s->len = 0;
-               }
-
-               /*
-                * We're at the start of a new chunk. If we don't have
-                * the complete chunk header, wait for more.
-                */
-               len = soup_substring_index (&arr->data [s->idx],
-                                           arr->len - s->idx, 
-                                           "\r\n");
-               if (len < 0)
+                       if (r->read_chunk_cb) {
+                               r->read_chunk_cb (read_buf, nread,
+                                                 r->user_data);
+                       }
+                       if (r->body_buf)
+                               g_byte_array_append (r->body_buf, read_buf, nread);
+                       if (size)
+                               *size -= nread;
                        break;
-               len += 2;
-
-               new_len = strtol (&arr->data [s->idx], NULL, 16);
-               g_assert (new_len >= 0);
 
-               /*
-                * If this is the final (zero-length) chunk, we need
-                * to have all of the trailing entity headers as well.
-                */
-               if (new_len == 0) {
-                       len = soup_substring_index (&arr->data [s->idx],
-                                                   arr->len - s->idx, 
-                                                   "\r\n\r\n");
-                       if (len < 0)
-                               break;
+               case SOUP_SOCKET_EOF:
+                       if (!size)
+                               return TRUE;
+                       /* else fall through */
 
-                       /* 
-                        * FIXME: Add entity headers we find here to
-                        *        req->response_headers. 
-                        */
+               case SOUP_SOCKET_ERROR:
+                       reader_disconnected (r->sock, r);
+                       return FALSE;
 
-                       len += 4;
-                       ret = TRUE;
+               case SOUP_SOCKET_WOULD_BLOCK:
+                       return FALSE;
                }
-
-               /* 
-                * Remove chunk header and get ready for chunk data.
-                */
-               remove_block_at_index (arr, s->idx, len);
-               s->len = new_len;
        }
 
-       return ret;
-}
-
-static void
-issue_chunk_callback (SoupReader *r, gchar *data, gint len)
-{
-       /* 
-        * Call chunk callback. Pass len worth of data. 
-        */
-       if (r->read_chunk_cb && len) {
-               SoupDataBuffer buf = { 
-                       SOUP_BUFFER_SYSTEM_OWNED, 
-                       data,
-                       len
-               };
-
-               r->callback_issued = TRUE;
-               (*r->read_chunk_cb) (&buf, r->user_data);
-       }
+       return TRUE;
 }
 
-static gboolean
-read_chunk (SoupReader *r)
-{
-       SoupTransferChunkState *s = &r->chunk_state;
-       GByteArray *arr = r->recv_buf;
-       gboolean done;
-       gint datalen;
-
-       /* 
-        * Update datalen for any data read 
-        */
-       done = decode_chunk (&r->chunk_state, r->recv_buf, &datalen);
-       if (!datalen) 
-               return done;
-
-       issue_chunk_callback (r, arr->data, s->idx);
-
-       /* 
-        * If overwrite, remove already-processed data from start
-        * of buffer 
-        */
-       if (r->overwrite_chunks) {
-               remove_block_at_index (arr, 0, s->idx);
-
-               s->idx = 0;
-       }
+#define SOUP_TRANSFER_EOL     "\r\n"
+#define SOUP_TRANSFER_EOL_LEN 2
 
-       return done;
-}
+#define SOUP_TRANSFER_DOUBLE_EOL     "\r\n\r\n"
+#define SOUP_TRANSFER_DOUBLE_EOL_LEN 4
 
-static gboolean
-read_content_length (SoupReader *r)
+static void
+reader_read (SoupSocket *sock, SoupReader *r)
 {
-       GByteArray *arr = r->recv_buf;
+       soup_transfer_read_ref (r);
 
-       if (arr->len) {
-               issue_chunk_callback (r, arr->data, arr->len);
+       while (1) {
+               switch (r->state) {
+               case SOUP_READER_STATE_HEADERS:
+                       if (!soup_reader_read_metadata (
+                                   r, SOUP_TRANSFER_DOUBLE_EOL,
+                                   SOUP_TRANSFER_DOUBLE_EOL_LEN))
+                               goto out;
+
+                       r->meta_buf->len -= SOUP_TRANSFER_DOUBLE_EOL_LEN;
+                       if (r->headers_done_cb) {
+                               (*r->headers_done_cb) (r->meta_buf->data,
+                                                      r->meta_buf->len,
+                                                      &r->encoding, 
+                                                      &r->read_length, 
+                                                      r->user_data);
+                       }
+                       g_byte_array_set_size (r->meta_buf, 0);
+
+                       switch (r->encoding) {
+                       case SOUP_TRANSFER_UNKNOWN:
+                               r->state = SOUP_READER_STATE_READ_TO_EOF;
+                               break;
+                       case SOUP_TRANSFER_CONTENT_LENGTH:
+                               r->state = SOUP_READER_STATE_CONTENT_LENGTH;
+                               break;
+                       case SOUP_TRANSFER_CHUNKED:
+                               r->state = SOUP_READER_STATE_CHUNK_SIZE;
+                               break;
+                       }
+                       break;
 
-               /* 
-                * If overwrite, clear 
-                */
-               if (r->overwrite_chunks) {
-                       r->content_length -= r->recv_buf->len;
-                       g_byte_array_set_size (arr, 0);
-               }
-       }
+               case SOUP_READER_STATE_READ_TO_EOF:
+                       if (!soup_reader_read_body_chunk (r, NULL))
+                               goto out;
 
-       return r->content_length == arr->len;
-}
+                       goto done;
+                       break;
 
-static gboolean
-read_unknown (SoupReader *r)
-{
-       GByteArray *arr = r->recv_buf;
+               case SOUP_READER_STATE_CONTENT_LENGTH:
+                       if (!soup_reader_read_body_chunk (r, &r->read_length))
+                               goto out;
 
-       if (arr->len) {
-               issue_chunk_callback (r, arr->data, arr->len);
+                       goto done;
+                       break;
 
-               /* 
-                * If overwrite, clear 
-                */
-               if (r->overwrite_chunks)
-                       g_byte_array_set_size (arr, 0);
-       }
+               case SOUP_READER_STATE_CHUNK_SIZE:
+                       if (!soup_reader_read_metadata (r, SOUP_TRANSFER_EOL,
+                                                       SOUP_TRANSFER_EOL_LEN))
+                               goto out;
 
-       /* 
-        * Keep reading until we get a zero read or HUP.
-        */
-       return FALSE;
-}
+                       r->read_length = strtoul (r->meta_buf->data, NULL, 16);
+                       g_byte_array_set_size (r->meta_buf, 0);
 
-static gboolean
-soup_transfer_read_cb (GIOChannel   *iochannel,
-                      GIOCondition  condition,
-                      SoupReader   *r)
-{
-       gchar read_buf [RESPONSE_BLOCK_SIZE];
-       gsize bytes_read = 0, total_read = 0;
-       gboolean read_done = FALSE;
-       GIOError error;
-
- READ_AGAIN:
-       error = g_io_channel_read (iochannel,
-                                  read_buf,
-                                  sizeof (read_buf),
-                                  &bytes_read);
-
-       if (error == G_IO_ERROR_AGAIN) {
-               if (total_read) 
-                       goto PROCESS_READ;
-               else return TRUE;
-       }
+                       if (r->read_length > 0)
+                               r->state = SOUP_READER_STATE_CHUNK;
+                       else
+                               r->state = SOUP_READER_STATE_TRAILERS;
+                       break;
 
-       if (error != G_IO_ERROR_NONE) {
-               if (total_read) 
-                       goto PROCESS_READ;
-               else {
-                       soup_transfer_read_error_cb (iochannel, G_IO_HUP, r);
-                       return FALSE;
-               }
-       }
+               case SOUP_READER_STATE_CHUNK:
+                       if (!soup_reader_read_body_chunk (r, &r->read_length))
+                               goto out;
 
-       if (bytes_read) {
-               DUMP_READ (read_buf, bytes_read);
+                       r->state = SOUP_READER_STATE_BETWEEN_CHUNKS;
+                       break;
 
-               g_byte_array_append (r->recv_buf, read_buf, bytes_read);
-               total_read += bytes_read;
+               case SOUP_READER_STATE_BETWEEN_CHUNKS:
+                       if (!soup_reader_read_metadata (r, SOUP_TRANSFER_EOL,
+                                                       SOUP_TRANSFER_EOL_LEN))
+                               goto out;
 
-               goto READ_AGAIN;
-       }
+                       g_byte_array_set_size (r->meta_buf, 0);
+                       r->state = SOUP_READER_STATE_CHUNK_SIZE;
+                       break;
 
- PROCESS_READ:
+               case SOUP_READER_STATE_TRAILERS:
+                       if (!soup_reader_read_metadata (r, SOUP_TRANSFER_EOL,
+                                                       SOUP_TRANSFER_EOL_LEN))
+                               goto out;
 
-       if (r->header_len == 0 && total_read == 0) {
-               soup_transfer_read_error_cb (iochannel, G_IO_HUP, r);
-               return FALSE;
-       }
+                       if (r->meta_buf->len == SOUP_TRANSFER_EOL_LEN)
+                               goto done;
 
-       if (r->header_len == 0) {
-               gint index;
-
-               index = soup_substring_index (r->recv_buf->data,
-                                             r->recv_buf->len,
-                                             "\r\n\r\n");
-               if (index < 0) 
-                       return TRUE;
-               else
-                       index += 4;
-
-               if (r->headers_done_cb) {
-                       GString str;
-
-                       str.len = index;
-                       str.str = alloca (index + 1);
-                       strncpy (str.str, r->recv_buf->data, index);
-                       str.str [index] = '\0';
-
-                       soup_transfer_read_ref (r);
-                       (*r->headers_done_cb) (&str, 
-                                              &r->encoding, 
-                                              &r->content_length, 
-                                              r->user_data);
-                       if (!soup_transfer_read_unref (r))
-                               return FALSE;
-               }
+                       /* FIXME: process trailers */
+                       g_byte_array_set_size (r->meta_buf, 0);
+                       break;
 
-               remove_block_at_index (r->recv_buf, 0, index);
-               r->header_len = index;
+               }
        }
 
-       if (total_read == 0)
-               read_done = TRUE;
-       else {
-               soup_transfer_read_ref (r);
-
-               switch (r->encoding) {
-               case SOUP_TRANSFER_CHUNKED:
-                       read_done = read_chunk (r);
-                       break;
-               case SOUP_TRANSFER_CONTENT_LENGTH:
-                       read_done = read_content_length (r);
-                       break;
-               case SOUP_TRANSFER_UNKNOWN:
-                       read_done = read_unknown (r);
-                       break;
-               }
+ done:
+       issue_final_callback (r);
 
-               if (!soup_transfer_read_unref (r))
-                       return FALSE;
-       }
+ out:
+       soup_transfer_read_unref (r);
+}
 
-       if (!read_done) {
-               total_read = 0;
-               goto READ_AGAIN;
-       }
+static gboolean
+idle_read (gpointer user_data)
+{
+       SoupReader *r = user_data;
 
-       issue_final_callback (r);
+       r->idle_tag = 0;
+       reader_read (r->sock, r);
        return FALSE;
 }
 
@@ -536,7 +387,7 @@ soup_transfer_read_cb (GIOChannel   *iochannel,
  * soup_transfer_read_cancel().
  **/
 SoupReader *
-soup_transfer_read (GIOChannel            *chan,
+soup_transfer_read (SoupSocket            *sock,
                    gboolean               overwrite_chunks,
                    SoupReadHeadersDoneFn  headers_done_cb,
                    SoupReadChunkFn        read_chunk_cb,
@@ -549,27 +400,27 @@ soup_transfer_read (GIOChannel            *chan,
        g_assert (read_done_cb && error_cb);
 
        reader = g_new0 (SoupReader, 1);
-       reader->channel = chan;
-       reader->overwrite_chunks = overwrite_chunks;
+       reader->sock = g_object_ref (sock);
        reader->headers_done_cb = headers_done_cb;
        reader->read_chunk_cb = read_chunk_cb;
        reader->read_done_cb = read_done_cb;
        reader->error_cb = error_cb;
        reader->user_data = user_data;
-       reader->recv_buf = g_byte_array_new ();
        reader->encoding = SOUP_TRANSFER_UNKNOWN;
 
+       reader->meta_buf = g_byte_array_new ();
+       if (!overwrite_chunks)
+               reader->body_buf = g_byte_array_new ();
+
        reader->read_tag =
-               g_io_add_watch (chan,
-                               G_IO_IN,
-                               (GIOFunc) soup_transfer_read_cb,
-                               reader);
+               g_signal_connect (sock, "readable",
+                                 G_CALLBACK (reader_read), reader);
 
        reader->err_tag =
-               g_io_add_watch (chan,
-                               G_IO_HUP | G_IO_ERR | G_IO_NVAL,
-                               (GIOFunc) soup_transfer_read_error_cb,
-                               reader);
+               g_signal_connect (sock, "disconnected",
+                                 G_CALLBACK (reader_disconnected), reader);
+
+       reader->idle_tag = g_idle_add (idle_read, reader);
 
        /* Initial ref_count is 2: one reference owned by
         * soup-transfer and one by the caller.
@@ -579,18 +430,46 @@ soup_transfer_read (GIOChannel            *chan,
        return reader;
 }
 
+
+struct _SoupWriter {
+        int                     ref_count;
+
+       SoupSocket             *sock;
+       guint                   idle_tag;
+       guint                   write_tag;
+       guint                   err_tag;
+
+       SoupTransferEncoding    encoding;
+       GByteArray             *write_buf;
+
+       gboolean                headers_done;
+       int                     chunk_cnt;
+
+       SoupWriteGetHeaderFn    get_header_cb;
+       SoupWriteGetChunkFn     get_chunk_cb;
+       SoupWriteDoneFn         write_done_cb;
+       SoupWriteErrorFn        error_cb;
+       gpointer                user_data;
+};
+
 static void
 soup_transfer_write_stop (SoupWriter *w)
 {
        if (!w->err_tag)
                return;
 
+       g_signal_handler_disconnect (w->sock, w->err_tag);
+       w->err_tag = 0;
+
        if (w->write_tag) {
-               g_source_remove (w->write_tag);
+               g_signal_handler_disconnect (w->sock, w->write_tag);
                w->write_tag = 0;
        }
-       g_source_remove (w->err_tag);
-       w->err_tag = 0;
+
+       if (w->idle_tag) {
+               g_source_remove (w->idle_tag);
+               w->idle_tag = 0;
+       }
 
        /* Give up soup-transfer's ref */
        soup_transfer_write_unref (w);
@@ -611,6 +490,7 @@ soup_transfer_write_unref (SoupWriter *w)
 
        soup_transfer_write_stop (w);
        g_byte_array_free (w->write_buf, TRUE);
+       g_object_unref (w->sock);
        g_free (w);
        return FALSE;
 }
@@ -622,15 +502,11 @@ soup_transfer_write_cancel (SoupWriter *w)
        soup_transfer_write_unref (w);
 }
 
-static gboolean
-soup_transfer_write_error_cb (GIOChannel* iochannel,
-                             GIOCondition condition,
-                             SoupWriter *w)
+static void
+writer_disconnected (SoupSocket *sock, SoupWriter *w)
 {
        soup_transfer_write_stop (w);
        (*w->error_cb) (w->headers_done, w->user_data);
-
-       return FALSE;
 }
 
 static gboolean 
@@ -698,81 +574,66 @@ get_next_chunk (SoupWriter *w)
        }
 }
 
-#ifdef SIGPIPE
-#  define IGNORE_PIPE(pipe_handler) pipe_handler = signal (SIGPIPE, SIG_IGN)
-#  define RESTORE_PIPE(pipe_handler) signal (SIGPIPE, pipe_handler)
-#else
-#  define IGNORE_PIPE(x)
-#  define RESTORE_PIPE(x)
-#endif
-
-static gboolean
-soup_transfer_write_cb (GIOChannel* iochannel,
-                       GIOCondition condition,
-                       SoupWriter *w)
+static void
+writer_write (SoupSocket *sock, SoupWriter *w)
 {
-       GIOError error;
-       gpointer pipe_handler;
-       gsize bytes_written = 0;
+       SoupSocketIOStatus status;
+       guint bytes_written = 0;
 
-       /*
-        * Get the header and first data chunk (if available).
-        */
+       /* Get the header and first data chunk (if available). */
        if (w->get_header_cb) {
                soup_transfer_write_ref (w);
 
-               if (!get_header (w))
-                       return soup_transfer_write_unref (w);
+               if (!get_header (w)) {
+                       soup_transfer_write_unref (w);
+                       return;
+               }
 
                if (w->get_chunk_cb)
                        get_next_chunk (w);
 
                if (!soup_transfer_write_unref (w))
-                       return FALSE;
+                       return;
        }
 
  WRITE_AGAIN:
        while (w->write_buf->len) {
-               IGNORE_PIPE (pipe_handler);
-               error = g_io_channel_write (iochannel,
-                                           w->write_buf->data,
-                                           w->write_buf->len,
-                                           &bytes_written);
-               RESTORE_PIPE (pipe_handler);
-
-               if (error == G_IO_ERROR_AGAIN) 
-                       return TRUE;
-
-               if (error != G_IO_ERROR_NONE) {
-                       soup_transfer_write_error_cb (iochannel, G_IO_HUP, w);
-                       return FALSE;
+               status = soup_socket_write (sock, w->write_buf->data,
+                                           w->write_buf->len, &bytes_written);
+
+               switch (status) {
+               case SOUP_SOCKET_EOF:
+               case SOUP_SOCKET_ERROR:
+                       writer_disconnected (sock, w);
+                       return;
+
+               case SOUP_SOCKET_WOULD_BLOCK:
+                       return;
+
+               case SOUP_SOCKET_OK:
+                       memmove (w->write_buf->data,
+                                w->write_buf->data + bytes_written,
+                                w->write_buf->len - bytes_written);
+                       g_byte_array_set_size (w->write_buf,
+                                              w->write_buf->len - bytes_written);
+                       break;
                }
-
-               if (!bytes_written)
-                       return TRUE;
-
-               DUMP_WRITE (w->write_buf->data, bytes_written);
-
-               remove_block_at_index (w->write_buf, 0, bytes_written);
        }
 
-       /*
-        * When we exit the above block, we are certain that the headers have
+       /* When we exit the above block, we are certain that the headers have
         * been written.  
         */
        w->headers_done = TRUE;
 
-       /*
-        * Get the next data chunk and try again, or quit if paused.
-        */
+       /* Get the next data chunk and try again, or quit if paused. */
        if (w->get_chunk_cb) {
                soup_transfer_write_ref (w);
                get_next_chunk (w);
                if (!soup_transfer_write_unref (w))
-                       return TRUE;
+                       return;
 
                if (!w->write_tag)
-                       return TRUE;
+                       return;
 
                goto WRITE_AGAIN;
        }
@@ -781,11 +642,20 @@ soup_transfer_write_cb (GIOChannel* iochannel,
        soup_transfer_write_stop (w);
        (*w->write_done_cb) (w->user_data);
        soup_transfer_write_unref (w);
+}
+
+static gboolean
+idle_write (gpointer user_data)
+{
+       SoupWriter *w = user_data;
+
+       w->idle_tag = 0;
+       writer_write (w->sock, w);
        return FALSE;
 }
 
 static SoupWriter *
-create_writer (GIOChannel             *chan,
+create_writer (SoupSocket             *sock,
               SoupTransferEncoding    encoding,
               SoupWriteDoneFn         write_done_cb,
               SoupWriteErrorFn        error_cb,
@@ -796,7 +666,7 @@ create_writer (GIOChannel             *chan,
        g_assert (write_done_cb && error_cb);
 
        writer = g_new0 (SoupWriter, 1);
-       writer->channel       = chan;
+       writer->sock          = g_object_ref (sock);
        writer->encoding      = encoding;
        writer->write_buf     = g_byte_array_new ();
        writer->write_done_cb = write_done_cb;
@@ -804,16 +674,14 @@ create_writer (GIOChannel             *chan,
        writer->user_data     = user_data;
 
        writer->write_tag =
-               g_io_add_watch (chan,
-                               G_IO_OUT,
-                               (GIOFunc) soup_transfer_write_cb,
-                               writer);
+               g_signal_connect (sock, "writable",
+                                 G_CALLBACK (writer_write), writer);
 
        writer->err_tag =
-               g_io_add_watch (chan,
-                               G_IO_HUP | G_IO_ERR | G_IO_NVAL,
-                               (GIOFunc) soup_transfer_write_error_cb,
-                               writer);
+               g_signal_connect (sock, "disconnected",
+                                 G_CALLBACK (writer_disconnected), writer);
+
+       writer->idle_tag = g_idle_add (idle_write, writer);
 
        /* As with SoupReader, one reference is owned by soup-transfer
         * and one by the caller.
@@ -825,7 +693,7 @@ create_writer (GIOChannel             *chan,
 
 /**
  * soup_transfer_write_simple:
- * @chan: the iochannel to write to
+ * @sock: the socket to write to
  * @header: message headers (including trailing blank line)
  * @src: buffer to write
  * @write_done_cb: (mandatory) callback to call when the body has been
@@ -833,7 +701,7 @@ create_writer (GIOChannel             *chan,
  * @error_cb: (mandatory) callback to call when an error occurs
  * @user_data: data to pass to the callbacks.
  *
- * Attempts to write a single HTTP message to @chan using identity
+ * Attempts to write a single HTTP message to @sock using identity
  * encoding and Content-Length.
  *
  * Unless the caller calls soup_transfer_write_cancel(), either
@@ -844,7 +712,7 @@ create_writer (GIOChannel             *chan,
  * soup_transfer_write_cancel().
  **/
 SoupWriter *
-soup_transfer_write_simple (GIOChannel             *chan,
+soup_transfer_write_simple (SoupSocket             *sock,
                            GString                *header,
                            const SoupDataBuffer   *src,
                            SoupWriteDoneFn         write_done_cb,
@@ -853,7 +721,7 @@ soup_transfer_write_simple (GIOChannel             *chan,
 {
        SoupWriter *writer;
 
-       writer = create_writer (chan, 
+       writer = create_writer (sock,
                                SOUP_TRANSFER_CONTENT_LENGTH,
                                write_done_cb,
                                error_cb,
@@ -875,8 +743,8 @@ soup_transfer_write_simple (GIOChannel             *chan,
 }
 
 /**
- * soup_transfer_write_simple:
- * @chan: the iochannel to write to
+ * soup_transfer_write:
+ * @sock: the socket to write to
  * @encoding: HTTP encoding mechanism to use.
  * @get_header_cb: (mandatory) callback to call to get message headers
  * @get_chunk_cb: (optional) callback to call to get body chunks
@@ -885,7 +753,7 @@ soup_transfer_write_simple (GIOChannel             *chan,
  * @error_cb: (mandatory) callback to call when an error occurs
  * @user_data: data to pass to the callbacks.
  *
- * Attempts to write a single HTTP message to @chan using @encoding.
+ * Attempts to write a single HTTP message to @sock using @encoding.
  *
  * Unless the caller calls soup_transfer_write_cancel(), either
  * @write_done_cb or @write_error_cb will eventually be called.
@@ -895,7 +763,7 @@ soup_transfer_write_simple (GIOChannel             *chan,
  * soup_transfer_write_cancel().
  **/
 SoupWriter *
-soup_transfer_write (GIOChannel             *chan,
+soup_transfer_write (SoupSocket             *sock,
                     SoupTransferEncoding    encoding,
                     SoupWriteGetHeaderFn    get_header_cb,
                     SoupWriteGetChunkFn     get_chunk_cb,
@@ -905,7 +773,7 @@ soup_transfer_write (GIOChannel             *chan,
 {
        SoupWriter *writer;
 
-       writer = create_writer (chan, 
+       writer = create_writer (sock,
                                encoding,
                                write_done_cb,
                                error_cb,
@@ -923,9 +791,13 @@ soup_transfer_write_pause (SoupWriter *w)
        g_return_if_fail (w != NULL);
 
        if (w->write_tag) {
-               g_source_remove (w->write_tag);
+               g_signal_handler_disconnect (w->sock, w->write_tag);
                w->write_tag = 0;
        }
+       if (w->idle_tag) {
+               g_source_remove (w->idle_tag);
+               w->idle_tag = 0;
+       }
 }
 
 void  
@@ -935,9 +807,9 @@ soup_transfer_write_unpause (SoupWriter *w)
 
        if (!w->write_tag) {
                w->write_tag =
-                       g_io_add_watch (w->channel,
-                                       G_IO_OUT,
-                                       (GIOFunc) soup_transfer_write_cb,
-                                       w);
+                       g_signal_connect (w->sock, "writable",
+                                         G_CALLBACK (writer_write), w);
        }
+       if (!w->idle_tag)
+               w->idle_tag = g_idle_add (idle_write, w);
 }
index d09749b..068529a 100644 (file)
@@ -29,20 +29,23 @@ typedef enum {
 typedef struct _SoupReader SoupReader;
 typedef struct _SoupWriter SoupWriter;
 
-typedef void (*SoupReadHeadersDoneFn) (const GString        *headers,
+typedef void (*SoupReadHeadersDoneFn) (char                 *headers,
+                                      guint                 header_len,
                                       SoupTransferEncoding *encoding,
-                                      gint                 *content_len,
+                                      int                  *content_len,
                                       gpointer              user_data);
 
-typedef void (*SoupReadChunkFn) (const SoupDataBuffer *data,
-                                gpointer              user_data);
+typedef void (*SoupReadChunkFn) (const char *chunk,
+                                guint       len,
+                                gpointer    user_data);
 
-typedef void (*SoupReadDoneFn) (const SoupDataBuffer *data,
-                               gpointer              user_data);
+typedef void (*SoupReadDoneFn) (char     *body,
+                               guint     len,
+                               gpointer  user_data);
 
 typedef void (*SoupReadErrorFn) (gboolean headers_done, gpointer user_data);
 
-SoupReader *soup_transfer_read  (GIOChannel             *chan,
+SoupReader *soup_transfer_read  (SoupSocket             *sock,
                                 gboolean                overwrite_chunks,
                                 SoupReadHeadersDoneFn   headers_done_cb,
                                 SoupReadChunkFn         read_chunk_cb,
@@ -66,7 +69,7 @@ typedef void (*SoupWriteDoneFn) (gpointer user_data);
 
 typedef void (*SoupWriteErrorFn) (gboolean headers_done, gpointer user_data);
 
-SoupWriter *soup_transfer_write_simple (GIOChannel             *chan,
+SoupWriter *soup_transfer_write_simple (SoupSocket             *sock,
                                        GString                *header,
                                        const SoupDataBuffer   *src,
                                        SoupWriteDoneFn         write_done_cb,
@@ -79,7 +82,7 @@ typedef void (*SoupWriteGetHeaderFn) (GString  **out_hdr,
 typedef SoupTransferDone (*SoupWriteGetChunkFn) (SoupDataBuffer *out_next,
                                                 gpointer        user_data);
 
-SoupWriter *soup_transfer_write (GIOChannel             *chan,
+SoupWriter *soup_transfer_write (SoupSocket             *sock,
                                 SoupTransferEncoding    encoding,
                                 SoupWriteGetHeaderFn    get_header_cb,
                                 SoupWriteGetChunkFn     get_chunk_cb,
index 4282ea1..c934942 100644 (file)
@@ -298,7 +298,7 @@ soup_uri_to_string (const SoupUri *uri, gboolean just_path)
                        g_string_append_c (str, '/');
        }
 
-       if (uri->path)
+       if (uri->path && *uri->path)
                append_uri_encoded (str, uri->path, "?");
        else if (just_path)
                g_string_append_c (str, '/');
index 14ba20c..be4f123 100644 (file)
@@ -2,7 +2,7 @@ Makefile
 Makefile.in
 auth-test
 get
+revserver
 simple-httpd
 simple-proxy
-timeserver
 uri-parsing
index 7419ee0..aa8dcc5 100644 (file)
@@ -4,10 +4,10 @@ INCLUDES =            \
 
 LIBS = $(top_builddir)/libsoup/libsoup-$(SOUP_API_VERSION).la
 
-noinst_PROGRAMS = get timeserver simple-httpd simple-proxy
+noinst_PROGRAMS = get revserver simple-httpd simple-proxy
 
 get_SOURCES = get.c
-timeserver_SOURCES = timeserver.c
+revserver_SOURCES = revserver.c
 simple_httpd_SOURCES = simple-httpd.c
 simple_proxy_SOURCES = simple-proxy.c
 
diff --git a/tests/revserver.c b/tests/revserver.c
new file mode 100644 (file)
index 0000000..839603a
--- /dev/null
@@ -0,0 +1,182 @@
+#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.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)
+{
+       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_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);
+}
+
+static void
+rev_write (SoupSocket *sock, GString *buf)
+{
+       SoupSocketIOStatus status;
+       guint nwrote;
+
+       do {
+               status = soup_socket_write (sock, buf->str, buf->len, &nwrote);
+               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_assert (nonblocking == TRUE);
+               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];
+       guint nread;
+       gboolean eol;
+
+       do {
+               status = soup_socket_read_until (sock, tmp, sizeof (tmp),
+                                                "\n", 1, &nread, &eol);
+               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_assert (nonblocking == TRUE);
+               break;
+
+       default:
+               g_warning ("Socket error");
+               /* fall through */
+
+       case SOUP_SOCKET_EOF:
+               rev_done (sock, buf);
+               break;
+       }
+}
+
+static void
+new_connection (SoupSocket *listener, SoupSocket *client, gpointer user_data)
+{
+       GString *buf;
+
+       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);
+
+       rev_read (client, buf);
+}
+
+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 ();
+
+       while ((opt = getopt (argc, argv, "6bp:")) != -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",
+                                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_server_new (addr, port,
+                                          new_connection, NULL);
+       g_object_unref (addr);
+       if (!listener) {
+               fprintf (stderr, "Could not create listening socket\n");
+               exit (1);
+       }
+       printf ("Listening on port %d\n",
+               soup_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/timeserver.c b/tests/timeserver.c
deleted file mode 100644 (file)
index 766c322..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <libsoup/soup.h>
-
-static void
-got_address (SoupAddress *addr, SoupKnownErrorCode status, gpointer user_data)
-{
-       SoupSocket *client = user_data;
-       const char *name, *phys;
-       time_t now;
-       char *timebuf;
-       GIOChannel *chan;
-       gsize wrote;
-
-       name = soup_address_get_name (addr);
-       phys = soup_address_get_physical (addr);
-
-       printf ("got connection from %s (%s) port %d\n",
-               name ? name : "?", phys,
-               soup_address_get_port (addr));
-
-       now = time (NULL);
-       timebuf = ctime (&now);
-
-       chan = soup_socket_get_iochannel (client);
-       g_io_channel_write (chan, timebuf, strlen (timebuf), &wrote);
-       g_io_channel_unref (chan);
-
-       g_object_unref (client);
-}
-
-static void
-new_connection (SoupSocket *listener, SoupSocket *client, gpointer user_data)
-{
-       SoupAddress *addr;
-
-       g_object_ref (client);
-       addr = soup_socket_get_remote_address (client);
-       soup_address_resolve (addr, got_address, client);
-}
-
-int
-main (int argc, char **argv)
-{
-       SoupSocket *listener;
-       SoupAddressFamily family;
-       SoupAddress *addr;
-       guint port;
-       GMainLoop *loop;
-
-       g_type_init ();
-
-       if (argc >=2 && !strcmp (argv[1], "-6")) {
-               family = SOUP_ADDRESS_FAMILY_IPV6;
-               argc--;
-               argv++;
-       } else
-               family = SOUP_ADDRESS_FAMILY_IPV4;
-
-       if (argc > 2) {
-               fprintf (stderr, "Usage: %s [-6] [port]\n", argv[0]);
-               exit (1);
-       }
-
-       if (argc == 2)
-               port = atoi (argv[1]);
-       else
-               port = SOUP_ADDRESS_ANY_PORT;
-
-       addr = soup_address_new_any (SOUP_ADDRESS_FAMILY_IPV4, port);
-       if (!addr) {
-               fprintf (stderr, "Could not create listener address\n");
-               exit (1);
-       }
-
-       listener = soup_socket_server_new (addr, port,
-                                          new_connection, NULL);
-       g_object_unref (addr);
-       if (!listener) {
-               fprintf (stderr, "Could not create listening socket\n");
-               exit (1);
-       }
-       printf ("Listening on port %d\n",
-               soup_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;
-}