Remove _SoupMessagePrivate bits covered by soup-transfer.
authorAlex Graveley <alex@ximian.com>
Mon, 2 Jul 2001 16:46:27 +0000 (16:46 +0000)
committerAlex Graveley <orph@src.gnome.org>
Mon, 2 Jul 2001 16:46:27 +0000 (16:46 +0000)
2001-07-02  Alex Graveley  <alex@ximian.com>

* src/soup-core/soup-private.h: Remove _SoupMessagePrivate bits
covered by soup-transfer.

* src/soup-core/soup-queue.c: Use soup-transfer.
(soup_queue_error_cb): Remove handling of buggy MS IIS server
tranferring partial content then closing connection. Report this
as SOUP_ERROR_IO instead.

* src/soup-core/soup-httpd.c: Use soup-transfer.

* src/soup-core/soup-transfer.[ch]: Added. HTTP Transport
abstraction used to clean up client and standalone/cgi server code
duplication.

* configure.in: Bump version to 0.3.

* src/soup-core/soup-queue.c (soup_check_used_headers): Use
toupper in switch instead of upper/lower cases for each.

* src/soup-core/soup-httpd.c: Declare apache dummy method
implementations to avoid warnings.

ChangeLog
configure.in
libsoup/Makefile.am
libsoup/soup-message.c
libsoup/soup-message.h
libsoup/soup-private.h
libsoup/soup-queue.c
libsoup/soup-transfer.c [new file with mode: 0644]
libsoup/soup-transfer.h [new file with mode: 0644]

index 42f5430..002e9e3 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,27 @@
+2001-07-02  Alex Graveley  <alex@ximian.com>
+
+       * src/soup-core/soup-private.h: Remove _SoupMessagePrivate bits
+       covered by soup-transfer.
+
+       * src/soup-core/soup-queue.c: Use soup-transfer.
+       (soup_queue_error_cb): Remove handling of buggy MS IIS server
+       tranferring partial content then closing connection. Report this
+       as SOUP_ERROR_IO instead.
+
+       * src/soup-core/soup-httpd.c: Use soup-transfer.
+
+       * src/soup-core/soup-transfer.[ch]: Added. HTTP Transport
+       abstraction used to clean up client and standalone/cgi server code
+       duplication.
+
+       * configure.in: Bump version to 0.3.
+
+       * src/soup-core/soup-queue.c (soup_check_used_headers): Use
+       toupper in switch instead of upper/lower cases for each.
+
+       * src/soup-core/soup-httpd.c: Declare apache dummy method
+       implementations to avoid warnings.
+
 2001-06-27  Joe Shaw  <joe@ximian.com>
 
        * src/soup-core/soup-digest.c: Largely gutted. Made RFC 2617
index 4f6e972..93eb2aa 100644 (file)
@@ -13,7 +13,7 @@ AC_SUBST(SOUP_REVISION)
 AC_SUBST(SOUP_AGE)
 
 AM_CONFIG_HEADER(config.h)
-AM_INIT_AUTOMAKE(soup, 0.2.1)
+AM_INIT_AUTOMAKE(soup, 0.3)
 AM_MAINTAINER_MODE
 AC_PROG_MAKE_SET
 
index 443be88..5158382 100644 (file)
@@ -58,6 +58,8 @@ libsoup_la_SOURCES =          \
        soup-socks.c            \
        soup-ssl.h              \
        soup-ssl.c              \
+       soup-transfer.h         \
+       soup-transfer.c         \
        soup-uri.c
 
 libsoup_apache_la_LDFLAGS =    \
index 3ba535d..8827e22 100644 (file)
@@ -11,6 +11,7 @@
 #include "soup-message.h"
 #include "soup-context.h"
 #include "soup-private.h"
+#include "soup-transfer.h"
 
 /**
  * soup_message_new:
@@ -74,9 +75,6 @@ soup_message_new_full (SoupContext   *context,
        return ret;
 }
 
-#define source_remove(_src) \
-        ({ if ((_src)) { g_source_remove ((_src)); (_src) = 0; }})
-
 /**
  * soup_message_cleanup:
  * @req: a %SoupMessage.
@@ -91,10 +89,15 @@ soup_message_cleanup (SoupMessage *req)
 {
        g_return_if_fail (req != NULL);
 
-       source_remove (req->priv->read_tag);
-       source_remove (req->priv->write_tag);
-       source_remove (req->priv->error_tag);
-       source_remove (req->priv->timeout_tag);
+       if (req->priv->read_tag) {
+               soup_transfer_read_cancel (req->priv->read_tag);
+               req->priv->read_tag = 0;
+       }
+
+       if (req->priv->write_tag) {
+               soup_transfer_write_cancel (req->priv->write_tag);
+               req->priv->write_tag = 0;
+       }
 
        if (req->priv->connect_tag) {
                soup_context_cancel_connect (req->priv->connect_tag);
@@ -104,17 +107,6 @@ soup_message_cleanup (SoupMessage *req)
                soup_connection_release (req->priv->conn);
                req->priv->conn = NULL;
        }
-       if (req->priv->recv_buf) {
-               g_byte_array_free (req->priv->recv_buf, FALSE);
-               req->priv->recv_buf = NULL;
-       }
-
-       req->priv->write_len = 0;
-       req->priv->headers_done = FALSE;
-       req->priv->content_length = 0;
-       req->priv->is_chunked = FALSE;
-       req->priv->cur_chunk_len = 0;
-       req->priv->cur_chunk_idx = 0;
 
        soup_active_requests = g_slist_remove (soup_active_requests, req);
 }
index 462776e..de377e8 100644 (file)
@@ -143,7 +143,6 @@ void           soup_message_add_body_handler    (SoupMessage      *msg,
                                                 SoupHandlerFn     handler_cb,
                                                 gpointer          user_data);
 
-/* FIXME: None of these are implemented yet, oh well... */
 typedef enum {
        SOUP_MESSAGE_FOLLOW_REDIRECT  = (1 << 1),
        SOUP_MESSAGE_NO_COOKIE        = (1 << 2),
index 357dac1..da67141 100644 (file)
@@ -89,19 +89,9 @@ struct _SoupMessagePrivate {
        SoupConnectId   connect_tag;
        guint           read_tag;
        guint           write_tag;
-       guint           error_tag;
        guint           timeout_tag;
 
-       guint           write_len;
-       gboolean        headers_done;
-
-       guint           content_length;
-       gboolean        is_chunked;
-       guint           cur_chunk_len;
-       guint           cur_chunk_idx;
-
        GString        *req_header;
-       GByteArray     *recv_buf;
 
        SoupCallbackFn  callback;
        gpointer        user_data;
index 367e029..e0f3536 100644 (file)
 #include "soup-misc.h"
 #include "soup-private.h"
 #include "soup-socks.h"
+#include "soup-transfer.h"
 
 GSList *soup_active_requests = NULL;
 
 static guint soup_queue_idle_tag = 0;
 
-/**
- * soup_queue_shutdown:
- * 
- * Shut down the message queue by calling %soup_message_cancel on all active
- * requests.
- */
-void 
-soup_queue_shutdown (void)
+static void
+soup_debug_print_a_header (gchar *key, gchar *val, gpointer not_used)
 {
-        GSList *iter;
-
-       g_source_remove (soup_queue_idle_tag);
-       soup_queue_idle_tag = 0;
+       g_print ("\tKEY: \"%s\", VALUE: \"%s\"\n", key, val);
+}
 
-       for (iter = soup_active_requests; iter; iter = iter->next)
-               soup_message_cancel (iter->data);
+void 
+soup_debug_print_headers (SoupMessage *req)
+{
+       g_hash_table_foreach (req->response_headers,
+                             (GHFunc) soup_debug_print_a_header,
+                             NULL); 
 }
 
 static gboolean
-soup_parse_headers (SoupMessage *req)
+soup_parse_headers (const GString *headers, SoupMessage *req)
 {
        if (req->response_headers) 
                g_hash_table_destroy (req->response_headers);
@@ -57,8 +54,8 @@ soup_parse_headers (SoupMessage *req)
        req->response_headers = g_hash_table_new (soup_str_case_hash, 
                                                  soup_str_case_equal);
 
-       if (!soup_headers_parse_response (req->priv->recv_buf->data
-                                         req->priv->recv_buf->len, 
+       if (!soup_headers_parse_response (headers->str
+                                         headers->len, 
                                          req->response_headers,
                                          &req->response_code,
                                          &req->response_phrase))
@@ -71,13 +68,17 @@ soup_parse_headers (SoupMessage *req)
        return FALSE;
 }
 
-/* returns TRUE to continue processing, FALSE if a callback was issued */
-static gboolean 
-soup_process_headers (SoupMessage *req)
+static SoupTransferDone
+soup_queue_read_headers_cb (const GString *headers,
+                           guint         *content_len,
+                           SoupMessage   *req)
 {
        gchar *connection, *length, *enc;
        SoupErrorCode err = SOUP_ERROR_MALFORMED_HEADER;
 
+       if (!soup_parse_headers (headers, req)) 
+               return SOUP_TRANSFER_END;
+
        /* Handle connection persistence */
        connection = g_hash_table_lookup (req->response_headers, "Connection");
 
@@ -88,11 +89,13 @@ soup_process_headers (SoupMessage *req)
        length = g_hash_table_lookup (req->response_headers, "Content-Length");
        enc = g_hash_table_lookup (req->response_headers, "Transfer-Encoding");
        
-       if (length)
-               req->priv->content_length = atoi (length);
-       else if (enc) {
+       if (length) {
+               *content_len = atoi (length);
+               if (*content_len < 0) 
+                       goto THROW_MALFORMED_HEADER;
+       } else if (enc) {
                if (g_strcasecmp (enc, "chunked") == 0)
-                       req->priv->is_chunked = TRUE;
+                       *content_len = SOUP_TRANSFER_CHUNKED;
                else {
                        g_warning ("Unknown encoding type in HTTP response.");
                        goto THROW_MALFORMED_HEADER;
@@ -103,99 +106,49 @@ soup_process_headers (SoupMessage *req)
        if (err) goto THROW_MALFORMED_HEADER;
        if (req->status == SOUP_STATUS_QUEUED) return FALSE;
 
-       return TRUE;
+       return SOUP_TRANSFER_CONTINUE;
 
  THROW_MALFORMED_HEADER:
        soup_message_issue_callback (req, err);
-       return FALSE;
+       return SOUP_TRANSFER_END;
 }
 
-static void
-soup_debug_print_a_header (gchar *key, gchar *val, gpointer not_used)
+static SoupTransferDone
+soup_queue_read_chunk_cb (const SoupDataBuffer *data,
+                         SoupMessage          *req)
 {
-       g_print ("\tKEY: \"%s\", VALUE: \"%s\"\n", key, val);
-}
-
-void 
-soup_debug_print_headers (SoupMessage *req)
-{
-       g_hash_table_foreach (req->response_headers,
-                             (GHFunc) soup_debug_print_a_header,
-                             NULL); 
-}
-
-static gboolean 
-soup_read_chunk (SoupMessage *req) 
-{
-       guint chunk_idx = req->priv->cur_chunk_idx;
-       gint chunk_len = req->priv->cur_chunk_len;
-       GByteArray *arr = req->priv->recv_buf;
-       
-       while (chunk_idx + chunk_len + 5 <= arr->len) {
-               gint new_len = 0;
-               gint len = 0, j;
-               gchar *i = &arr->data [chunk_idx + chunk_len];
-
-               /* remove \r\n after previous chunk body */
-               if (chunk_len) {
-                       g_memmove (i, 
-                                  i + 2, 
-                                  arr->len - chunk_idx - chunk_len - 2);
-                       g_byte_array_set_size (arr, arr->len - 2);
-               }
-
-               /* Convert the size of the next chunk from hex */
-               while ((tolower (*i) >= 'a' && tolower (*i) <= 'f') ||
-                      (*i >= '0' && *i <= '9'))
-                       len++, i++;
-               
-               for (i -= len, j = len - 1; j + 1; i++, j--)
-                       new_len += (*i > '9') ? 
-                               (tolower (*i) - 0x57) << (4*j) :
-                               (tolower (*i) - 0x30) << (4*j);
-
-               chunk_idx = chunk_idx + chunk_len;
-               chunk_len = new_len;
-
-               if (chunk_len == 0) {
-                       /* FIXME: Add entity headers we find here to
-                                 req->response_headers. */
-                       len += soup_substring_index (&arr->data [chunk_idx + 3],
-                                                    arr->len - chunk_idx - 3,
-                                                    "\r\n");
-                       len += 2;
-               }
-
-               /* trailing \r\n after chunk length */
-               g_memmove (&arr->data [chunk_idx], 
-                          &arr->data [chunk_idx + len + 2],
-                          arr->len - chunk_idx - len - 2);
-               g_byte_array_set_size (arr, arr->len - len - 2);
+       SoupErrorCode err;
 
-               /* zero-length chunk closes transfer */
-               if (chunk_len == 0) return TRUE;
-       }
+       req->response.owner = data->owner;
+       req->response.length = data->length;
+       req->response.body = data->body;
 
-       req->priv->cur_chunk_len = chunk_len;
-       req->priv->cur_chunk_idx = chunk_idx;
+       err = soup_message_run_handlers (req, SOUP_HANDLER_BODY_CHUNK);
+       if (err) { 
+               soup_message_issue_callback (req, err); 
+               return FALSE;
+       } else if (req->status == SOUP_STATUS_QUEUED) 
+               return FALSE;
 
-       return FALSE;
+       return TRUE;
 }
 
 static void
-soup_finish_read (SoupMessage *req)
+soup_queue_read_done_cb (const SoupDataBuffer *data,
+                        SoupMessage          *req)
 {
        SoupErrorCode err;
 
-       req->response.owner = SOUP_BUFFER_SYSTEM_OWNED;
-       req->response.length = req->priv->recv_buf->len;
-       req->response.body = req->priv->recv_buf->data;
+       req->response.owner = data->owner;
+       req->response.length = data->length;
+       req->response.body = data->body;
 
        req->status = SOUP_STATUS_FINISHED;
 
+       req->priv->read_tag = 0;
+
        err = soup_message_run_handlers (req, SOUP_HANDLER_POST_BODY);
        if (req->status == SOUP_STATUS_QUEUED) return;
-
        if (err)
                soup_message_issue_callback (req, err); 
        else 
@@ -203,100 +156,12 @@ soup_finish_read (SoupMessage *req)
 }
 
 static gboolean 
-soup_queue_read_cb (GIOChannel* iochannel, 
-                   GIOCondition condition, 
-                   SoupMessage *req)
-{
-       gchar read_buf [RESPONSE_BLOCK_SIZE];
-       gint bytes_read = 0;
-       gboolean read_done = FALSE;
-       GByteArray *arr;
-       GIOError error;
-       SoupErrorCode err;
-
-       error = g_io_channel_read (iochannel,
-                                  read_buf,
-                                  sizeof (read_buf),
-                                  &bytes_read);
-
-       if (error == G_IO_ERROR_AGAIN)
-               return TRUE;
-
-       if (error != G_IO_ERROR_NONE) {
-               soup_message_issue_callback (req, SOUP_ERROR_IO);
-               return FALSE;
-       }
-
-       arr = req->priv->recv_buf;
-
-       if (!arr) arr = req->priv->recv_buf = g_byte_array_new ();
-
-       if (req->priv->headers_done && 
-           req->priv->msg_flags & SOUP_MESSAGE_OVERWRITE_CHUNKS) {
-               req->priv->cur_chunk_len -= arr->len - req->priv->cur_chunk_idx;
-               req->priv->cur_chunk_idx = 0;
-               req->priv->content_length -= arr->len;
-               g_byte_array_set_size (arr, 0);
-       }
-
-       if (bytes_read) 
-               g_byte_array_append (arr, read_buf, bytes_read);
-
-       if (!req->priv->headers_done) {
-               gint index = soup_substring_index (arr->data, 
-                                                  arr->len, 
-                                                  "\r\n\r\n");
-               if (index < 0) return TRUE;
-
-               /* Terminate Headers */
-               arr->data [index + 3] = '\0';
-               index += 4;
-
-               if (!soup_parse_headers (req) || !soup_process_headers (req)) 
-                       return FALSE;
-
-               g_memmove (arr->data, &arr->data [index], arr->len - index);
-               g_byte_array_set_size (arr, arr->len - index);
-
-               req->priv->headers_done = TRUE;
-       }
-
-       /* Allow the chunk parser to strip the data stream */
-       if (bytes_read == 0) 
-               read_done = TRUE;
-       else if (req->priv->is_chunked) 
-               read_done = soup_read_chunk (req);
-       else if (req->priv->content_length == arr->len) 
-               read_done = TRUE;
-
-       /* Don't call chunk handlers if we didn't actually read anything */
-       if (bytes_read != 0) {
-               req->response.owner = SOUP_BUFFER_SYSTEM_OWNED;
-               req->response.length = arr->len;
-               req->response.body = arr->data;
-
-               err = soup_message_run_handlers (req, SOUP_HANDLER_BODY_CHUNK);
-               if (err) { 
-                       soup_message_issue_callback (req, err); 
-                       return FALSE;
-               } else if (req->status == SOUP_STATUS_QUEUED) 
-                       return FALSE;
-       }
-
-       if (read_done) {
-               soup_finish_read (req);
-               return FALSE;
-       }
-
-       return TRUE;
-}
-
-static gboolean 
-soup_queue_error_cb (GIOChannel* iochannel, 
-                    GIOCondition condition, 
+soup_queue_error_cb (gboolean     body_started, 
                     SoupMessage *req)
 {
+       /*
        gboolean conn_closed = soup_connection_is_keep_alive (req->priv->conn);
+       */
 
        soup_connection_set_keep_alive (req->priv->conn, FALSE);
 
@@ -309,8 +174,7 @@ soup_queue_error_cb (GIOChannel* iochannel,
                soup_message_issue_callback (req, SOUP_ERROR_CANT_CONNECT);
                break;
        case SOUP_STATUS_SENDING_REQUEST:
-               if (req->priv->req_header && 
-                   req->priv->req_header->len >= req->priv->write_len) {
+               if (!body_started) {
                        g_warning ("Requeueing request which failed in "
                                   "the sending headers phase");
                        soup_message_queue (req, 
@@ -322,10 +186,13 @@ soup_queue_error_cb (GIOChannel* iochannel,
                soup_message_issue_callback (req, SOUP_ERROR_IO);
                break;
        case SOUP_STATUS_READING_RESPONSE:
-               if (req->priv->headers_done && !conn_closed) {
+               /* FIXME: Remove this ?? */
+               /*
+               if (body_started && !conn_closed) {
                        soup_finish_read (req);
                        break;
                }
+               */
 
                soup_message_issue_callback (req, SOUP_ERROR_IO);
                break;
@@ -372,30 +239,28 @@ soup_check_used_headers (gchar *key,
                         gchar *value, 
                         struct SoupUsedHeaders *hdrs)
 {
-       switch (key [0]) {
+       switch (toupper (key [0])) {
        case 'H':
-       case 'h':
-               if (!g_strcasecmp (key+1, "ost")) hdrs->host = TRUE;
+               if (!g_strcasecmp (key+1, "ost")) 
+                       hdrs->host = TRUE;
                break;
        case 'U':
-       case 'u':
-               if (!g_strcasecmp (key+1, "ser-Agent")) hdrs->user_agent = TRUE;
+               if (!g_strcasecmp (key+1, "ser-Agent")) 
+                       hdrs->user_agent = TRUE;
                break;
        case 'S':
-       case 's':
-               if (!g_strcasecmp (key+1, "OAPAction")) hdrs->soapaction = TRUE;
+               if (!g_strcasecmp (key+1, "OAPAction")) 
+                       hdrs->soapaction = TRUE;
                break;
        case 'A':
-       case 'a':
-               if (!g_strcasecmp (key+1, "uthorization")) hdrs->auth = TRUE;
+               if (!g_strcasecmp (key+1, "uthorization")) 
+                       hdrs->auth = TRUE;
                break;
        case 'P':
-       case 'p':
                if (!g_strcasecmp (key+1, "roxy-Authorization")) 
                        hdrs->proxy_auth = TRUE;
                break;
        case 'C':
-       case 'c':
                if (!g_strcasecmp (key+1, "onnection")) 
                        hdrs->connection = TRUE;
                else if (!g_strcasecmp (key+1, "ontent-Type"))
@@ -483,72 +348,28 @@ soup_get_request_header (SoupMessage *req)
        return header;
 }
 
-static gboolean 
-soup_queue_write_cb (GIOChannel* iochannel, 
-                    GIOCondition condition, 
-                    SoupMessage *req)
+static void 
+soup_queue_write_done_cb (SoupMessage *req)
 {
-       guint head_len, body_len, total_len, total_written, bytes_written;
-       GIOError error;
-       gchar *write_buf;
-       guint  write_len;
-       void *pipe_handler;
-
-       if (!req->priv->req_header)
-               req->priv->req_header = soup_get_request_header (req);
-
-       head_len = req->priv->req_header->len;
-       body_len = req->request.length;
-       total_len = head_len + body_len;
-       total_written = req->priv->write_len;
-
-       pipe_handler = signal (SIGPIPE, SIG_IGN);
-       errno = 0;
-
- WRITE_SOME_MORE:
-       if (total_written < head_len) {
-               /* send rest of headers */
-               write_buf = &req->priv->req_header->str [total_written];
-               write_len = head_len - total_written;
-       } else {
-               /* send rest of body */
-               guint offset = total_written - head_len;
-               write_buf = &req->request.body [offset];
-               write_len = body_len - offset;
-       }
-
-       error = g_io_channel_write (iochannel, 
-                                   write_buf, 
-                                   write_len, 
-                                   &bytes_written);
-
-       if (error == G_IO_ERROR_AGAIN) {
-               signal (SIGPIPE, pipe_handler);
-               return TRUE;
-       }
+       GIOChannel *channel;
 
-       if (errno != 0 || error != G_IO_ERROR_NONE) {
-               soup_queue_error_cb (iochannel, G_IO_HUP, req);
-               goto DONE_WRITING;
-       }
+       channel = soup_connection_get_iochannel (req->priv->conn);
 
-       total_written = (req->priv->write_len += bytes_written);
+       req->priv->write_tag = 0;
 
-       if (total_written == total_len) {
-               req->status = SOUP_STATUS_READING_RESPONSE;
-               req->priv->read_tag = 
-                       g_io_add_watch (iochannel, 
-                                       G_IO_IN, 
-                                       (GIOFunc) soup_queue_read_cb, 
-                                       req);
-               goto DONE_WRITING;
-       }
+       req->priv->read_tag = 
+               soup_transfer_read (
+                       channel,
+                       req->priv->msg_flags & SOUP_MESSAGE_OVERWRITE_CHUNKS,
+                       (SoupReadHeadersDoneFn) soup_queue_read_headers_cb,
+                       (SoupReadChunkFn) soup_queue_read_chunk_cb,
+                       (SoupReadDoneFn) soup_queue_read_done_cb,
+                       (SoupReadErrorFn) soup_queue_error_cb,
+                       req);
 
-       goto WRITE_SOME_MORE;
+       g_io_channel_unref (channel);
 
- DONE_WRITING:
-       signal (SIGPIPE, pipe_handler);
-       return FALSE;
+       req->status = SOUP_STATUS_READING_RESPONSE;
 }
 
 static void
@@ -577,22 +398,26 @@ soup_queue_connect_cb (SoupContext          *ctx,
                        return;
                }
 
+               if (!req->priv->req_header)
+                       req->priv->req_header = soup_get_request_header (req);
+
                channel = soup_connection_get_iochannel (conn);
 
-               req->status = SOUP_STATUS_SENDING_REQUEST;
-               req->priv->conn = conn;
                req->priv->write_tag = 
-                       g_io_add_watch (channel, 
-                                       G_IO_OUT, 
-                                       (GIOFunc) soup_queue_write_cb, 
-                                       req);
-               req->priv->error_tag = 
-                       g_io_add_watch (channel, 
-                                       G_IO_HUP | G_IO_ERR | G_IO_NVAL, 
-                                       (GIOFunc) soup_queue_error_cb, 
-                                       req);
+                       soup_transfer_write (
+                               channel,
+                               req->priv->req_header,
+                               &req->request,
+                               NULL,
+                               (SoupWriteDoneFn) soup_queue_write_done_cb,
+                               (SoupWriteErrorFn) soup_queue_error_cb,
+                               req);
 
                g_io_channel_unref (channel);
+
+               req->status = SOUP_STATUS_SENDING_REQUEST;
+               req->priv->conn = conn;
+
                break;
        case SOUP_CONNECT_ERROR_ADDR_RESOLVE:
        case SOUP_CONNECT_ERROR_NETWORK:
@@ -697,8 +522,25 @@ soup_message_queue (SoupMessage    *req,
 
        req->response_code = 0;
        req->response_phrase = NULL;
-       req->priv->recv_buf = NULL;
        req->status = SOUP_STATUS_QUEUED;
 
        soup_active_requests = g_slist_prepend (soup_active_requests, req);
 }
+
+/**
+ * soup_queue_shutdown:
+ * 
+ * Shut down the message queue by calling %soup_message_cancel on all active
+ * requests.
+ */
+void 
+soup_queue_shutdown (void)
+{
+        GSList *iter;
+
+       g_source_remove (soup_queue_idle_tag);
+       soup_queue_idle_tag = 0;
+
+       for (iter = soup_active_requests; iter; iter = iter->next)
+               soup_message_cancel (iter->data);
+}
diff --git a/libsoup/soup-transfer.c b/libsoup/soup-transfer.c
new file mode 100644 (file)
index 0000000..62f5a9d
--- /dev/null
@@ -0,0 +1,422 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-queue.c: Asyncronous Callback-based SOAP Request Queue.
+ *
+ * Authors:
+ *      Alex Graveley (alex@helixcode.com)
+ *
+ * Copyright (C) 2000, Helix Code, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <ctype.h>
+#include <glib.h>
+#include <string.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <errno.h>
+
+#include "soup-transfer.h"
+#include "soup-private.h"
+
+typedef struct {
+       GIOChannel            *channel;
+       guint                  read_tag;
+       guint                  err_tag;
+
+       GByteArray            *recv_buf;
+       guint                  header_len;
+
+       gboolean               overwrite_chunks;
+       guint                  content_length;
+       gboolean               is_chunked;
+       guint                  cur_chunk_len;
+       guint                  cur_chunk_idx;
+
+       SoupReadHeadersDoneFn  headers_done_cb;
+       SoupReadChunkFn        read_chunk_cb;
+       SoupReadDoneFn         read_done_cb;
+       SoupReadErrorFn        error_cb;
+       gpointer               user_data;
+} SoupReader;
+
+typedef struct {
+       GIOChannel             *channel;
+       guint                   write_tag;
+       guint                   err_tag;
+
+       const GString          *header;
+       const SoupDataBuffer   *src;
+
+       guint                   write_len;
+       gboolean                headers_done;
+
+       SoupWriteHeadersDoneFn  headers_done_cb;
+       SoupWriteDoneFn         write_done_cb;
+       SoupWriteErrorFn        error_cb;
+       gpointer                user_data;
+} SoupWriter;
+
+#define source_remove(_src) \
+        ({ if ((_src)) { g_source_remove ((_src)); (_src) = 0; }})
+
+void
+soup_transfer_read_cancel (guint tag)
+{
+       SoupReader *r = GINT_TO_POINTER (tag);
+
+       source_remove (r->read_tag);
+       source_remove (r->err_tag);
+
+       g_byte_array_free (r->recv_buf, FALSE);
+
+       g_free (r);
+}
+
+static gboolean 
+soup_transfer_read_error_cb (GIOChannel* iochannel, 
+                            GIOCondition condition, 
+                            SoupReader *r)
+{
+       gboolean body_started = r->recv_buf->len > r->header_len;
+
+       if (r->error_cb) (*r->error_cb) (body_started, r->user_data);
+
+       soup_transfer_read_cancel (GPOINTER_TO_INT (r));
+
+       return FALSE;
+}
+
+static gboolean 
+soup_transfer_read_chunk (SoupReader *r) 
+{
+       guint chunk_idx = r->cur_chunk_idx;
+       gint chunk_len = r->cur_chunk_len;
+       GByteArray *arr = r->recv_buf;
+
+       while (chunk_idx + chunk_len + 5 <= arr->len) {
+               gint new_len = 0;
+               gint len = 0, j;
+               gchar *i = &arr->data [chunk_idx + chunk_len];
+
+               /* remove \r\n after previous chunk body */
+               if (chunk_len) {
+                       g_memmove (i, 
+                                  i + 2, 
+                                  arr->len - chunk_idx - chunk_len - 2);
+                       g_byte_array_set_size (arr, arr->len - 2);
+               }
+
+               /* Convert the size of the next chunk from hex */
+               while ((tolower (*i) >= 'a' && tolower (*i) <= 'f') ||
+                      (*i >= '0' && *i <= '9'))
+                       len++, i++;
+               
+               for (i -= len, j = len - 1; j + 1; i++, j--)
+                       new_len += (*i > '9') ? 
+                               (tolower (*i) - 0x57) << (4*j) :
+                               (tolower (*i) - 0x30) << (4*j);
+
+               chunk_idx = chunk_idx + chunk_len;
+               chunk_len = new_len;
+
+               if (chunk_len == 0) {
+                       /* FIXME: Add entity headers we find here to
+                                 req->response_headers. */
+                       len += soup_substring_index (&arr->data [chunk_idx + 3],
+                                                    arr->len - chunk_idx - 3,
+                                                    "\r\n");
+                       len += 2;
+               }
+
+               /* trailing \r\n after chunk length */
+               g_memmove (&arr->data [chunk_idx], 
+                          &arr->data [chunk_idx + len + 2],
+                          arr->len - chunk_idx - len - 2);
+               g_byte_array_set_size (arr, arr->len - len - 2);
+
+               /* zero-length chunk closes transfer */
+               if (chunk_len == 0) return TRUE;
+       }
+
+       r->cur_chunk_len = chunk_len;
+       r->cur_chunk_idx = chunk_idx;
+
+       return FALSE;
+}
+
+static gboolean 
+soup_transfer_read_cb (GIOChannel   *iochannel, 
+                      GIOCondition  condition, 
+                      SoupReader   *r)
+{
+       gchar read_buf [RESPONSE_BLOCK_SIZE];
+       gint bytes_read = 0;
+       gboolean read_done = FALSE;
+       GIOError error;
+       SoupDataBuffer buf;
+
+       error = g_io_channel_read (iochannel,
+                                  read_buf,
+                                  sizeof (read_buf),
+                                  &bytes_read);
+
+       if (error == G_IO_ERROR_AGAIN)
+               return TRUE;
+
+       if (error != G_IO_ERROR_NONE) {
+               soup_transfer_read_error_cb (iochannel, G_IO_HUP, r);
+               return FALSE;
+       }
+
+       if (r->header_len && r->overwrite_chunks) {
+               r->cur_chunk_len -= r->recv_buf->len - r->cur_chunk_idx;
+               r->cur_chunk_idx = 0;
+               r->content_length -= r->recv_buf->len;
+               g_byte_array_set_size (r->recv_buf, 0);
+       }
+
+       if (bytes_read) 
+               g_byte_array_append (r->recv_buf, read_buf, bytes_read);
+
+       if (!r->header_len) {
+               gint index = soup_substring_index (r->recv_buf->data, 
+                                                  r->recv_buf->len, 
+                                                  "\r\n\r\n");
+               if (index < 0) return TRUE;
+
+               index += 4;
+
+               if (r->headers_done_cb) {
+                       GString str;
+                       gint len;
+                       SoupTransferDone ret;
+
+                       str.str = g_strndup (r->recv_buf->data, index);;
+                       str.len = index;
+
+                       ret = (*r->headers_done_cb) (&str, &len, r->user_data);
+
+                       g_free (str.str);
+
+                       if (!ret) goto FINISH_READ;
+
+                       if (len == -1) r->is_chunked = TRUE;
+                       else r->content_length = len;
+               }
+
+               g_memmove (r->recv_buf->data, 
+                          &r->recv_buf->data [index], 
+                          r->recv_buf->len - index);
+               g_byte_array_set_size (r->recv_buf, r->recv_buf->len - index);
+
+               r->header_len = index;
+       }
+
+       /* Allow the chunk parser to strip the data stream */
+       if (bytes_read == 0) 
+               read_done = TRUE;
+       else if (r->is_chunked) 
+               read_done = soup_transfer_read_chunk (r);
+       else if (r->content_length == r->recv_buf->len) 
+               read_done = TRUE;
+
+       /* Don't call chunk handlers if we didn't actually read anything */
+       if (r->read_chunk_cb && bytes_read != 0) {
+               gboolean cont;
+
+               g_byte_array_append (r->recv_buf, "\0", 1);
+
+               buf.owner = SOUP_BUFFER_SYSTEM_OWNED;
+               buf.length = r->recv_buf->len - 1;
+               buf.body = r->recv_buf->data;
+
+               cont = (*r->read_chunk_cb) (&buf, r->user_data);
+
+               g_byte_array_remove_index (r->recv_buf, r->recv_buf->len - 1);
+
+               if (!cont) goto FINISH_READ;
+       }
+
+       if (!read_done) return TRUE;
+
+       if (r->read_done_cb) {
+               g_byte_array_append (r->recv_buf, "\0", 1);
+
+               buf.owner = SOUP_BUFFER_SYSTEM_OWNED;
+               buf.length = r->recv_buf->len - 1;
+               buf.body = r->recv_buf->data;
+
+               (*r->read_done_cb) (&buf, r->user_data);
+       }
+
+ FINISH_READ:
+       soup_transfer_read_cancel (GPOINTER_TO_INT (r));
+
+       return FALSE;
+}
+
+guint
+soup_transfer_read (GIOChannel            *chan,
+                   gboolean               overwrite_chunks,
+                   SoupReadHeadersDoneFn  headers_done_cb,
+                   SoupReadChunkFn        read_chunk_cb,
+                   SoupReadDoneFn         read_done_cb,
+                   SoupReadErrorFn        error_cb,
+                   gpointer               user_data)
+{
+       SoupReader *reader;
+
+       reader = g_new0 (SoupReader, 1);
+       reader->channel = chan;
+       reader->overwrite_chunks = overwrite_chunks;
+       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->read_tag = 
+               g_io_add_watch (chan, 
+                               G_IO_IN, 
+                               (GIOFunc) soup_transfer_read_cb, 
+                               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);
+
+       return GPOINTER_TO_INT (reader);
+}
+
+void
+soup_transfer_write_cancel (guint tag)
+{
+       SoupWriter *w = GINT_TO_POINTER (tag);
+
+       source_remove (w->write_tag);
+       source_remove (w->err_tag);
+
+       //g_io_channel_unref (w->channel);
+       g_free (w);
+}
+
+static gboolean 
+soup_transfer_write_error_cb (GIOChannel* iochannel, 
+                             GIOCondition condition, 
+                             SoupWriter *w)
+{
+       gboolean body_started = w->write_len > w->header->len;
+
+       if (w->error_cb) (*w->error_cb) (body_started, w->user_data);
+
+       soup_transfer_write_cancel (GPOINTER_TO_INT (w));
+
+       return FALSE;
+}
+
+static gboolean 
+soup_transfer_write_cb (GIOChannel* iochannel, 
+                       GIOCondition condition, 
+                       SoupWriter *w)
+{
+       guint head_len, body_len, total_len, total_written, bytes_written;
+       GIOError error;
+       gchar *write_buf;
+       guint  write_len;
+       void *pipe_handler;
+
+       head_len = w->header->len;
+       body_len = w->src->length;
+       total_len = head_len + body_len;
+       total_written = w->write_len;
+
+       pipe_handler = signal (SIGPIPE, SIG_IGN);
+       errno = 0;
+
+ WRITE_SOME_MORE:
+       if (total_written < head_len) {
+               /* send rest of headers */
+               write_buf = &w->header->str [total_written];
+               write_len = head_len - total_written;
+       } else {
+               /* send rest of body */
+               guint offset = total_written - head_len;
+               write_buf = &w->src->body [offset];
+               write_len = body_len - offset;
+
+               if (!w->headers_done) {
+                       if (w->headers_done_cb) 
+                               (*w->headers_done_cb) (w->user_data);
+                       w->headers_done = TRUE;
+               }
+       }
+
+       error = g_io_channel_write (iochannel, 
+                                   write_buf, 
+                                   write_len, 
+                                   &bytes_written);
+
+       if (error == G_IO_ERROR_AGAIN) {
+               signal (SIGPIPE, pipe_handler);
+               return TRUE;
+       }
+
+       if (errno != 0 || error != G_IO_ERROR_NONE) {
+               soup_transfer_write_error_cb (iochannel, G_IO_HUP, w);
+               goto DONE_WRITING;
+       }
+
+       total_written = (w->write_len += bytes_written);
+
+       if (total_written != total_len) 
+               goto WRITE_SOME_MORE;
+
+       if (w->write_done_cb) (*w->write_done_cb) (w->user_data);
+       soup_transfer_write_cancel (GPOINTER_TO_INT (w));
+
+ DONE_WRITING:
+       signal (SIGPIPE, pipe_handler);
+       return FALSE;
+}
+
+guint
+soup_transfer_write (GIOChannel             *chan,
+                    const GString          *header,
+                    const SoupDataBuffer   *src,
+                    SoupWriteHeadersDoneFn  headers_done_cb,
+                    SoupWriteDoneFn         write_done_cb,
+                    SoupWriteErrorFn        error_cb,
+                    gpointer                user_data)
+{
+       SoupWriter *writer;
+
+       writer = g_new0 (SoupWriter, 1);
+       writer->channel = chan;
+       writer->header = header;
+       writer->src = src;
+       writer->headers_done_cb = headers_done_cb;
+       writer->write_done_cb = write_done_cb;
+       writer->error_cb = error_cb;
+       writer->user_data = user_data;
+
+       writer->write_tag = 
+               g_io_add_watch (chan, 
+                               G_IO_OUT, 
+                               (GIOFunc) soup_transfer_write_cb, 
+                               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);
+
+       return GPOINTER_TO_INT (writer);
+}
diff --git a/libsoup/soup-transfer.h b/libsoup/soup-transfer.h
new file mode 100644 (file)
index 0000000..2c73de8
--- /dev/null
@@ -0,0 +1,63 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-queue.c: Asyncronous Callback-based SOAP Request Queue.
+ *
+ * Authors:
+ *      Alex Graveley (alex@helixcode.com)
+ *
+ * Copyright (C) 2000, Helix Code, Inc.
+ */
+
+#ifndef SOUP_TRANSFER_H
+#define SOUP_TRANSFER_H 1
+
+#include <glib.h>
+
+#include "soup-message.h"
+
+typedef enum {
+       SOUP_TRANSFER_END = 0,
+       SOUP_TRANSFER_CONTINUE,
+} SoupTransferDone;
+
+#define SOUP_TRANSFER_CHUNKED -1
+
+typedef SoupTransferDone (*SoupReadHeadersDoneFn) (const GString *headers,
+                                                  guint         *content_len,
+                                                  gpointer       user_data);
+
+typedef SoupTransferDone (*SoupReadChunkFn) (const SoupDataBuffer *data,
+                                            gpointer              user_data);
+
+typedef void (*SoupReadDoneFn) (const SoupDataBuffer *data,
+                               gpointer              user_data);
+
+typedef void (*SoupReadErrorFn) (gboolean headers_done, gpointer user_data);
+
+guint soup_transfer_read  (GIOChannel             *chan,
+                          gboolean                overwrite_chunks,
+                          SoupReadHeadersDoneFn   headers_done_cb,
+                          SoupReadChunkFn         read_chunk_cb,
+                          SoupReadDoneFn          read_done_cb,
+                          SoupReadErrorFn         error_cb,
+                          gpointer                user_data);
+
+void  soup_transfer_read_cancel (guint tag);
+
+typedef void (*SoupWriteHeadersDoneFn) (gpointer user_data);
+
+typedef void (*SoupWriteDoneFn) (gpointer user_data);
+
+typedef void (*SoupWriteErrorFn) (gboolean headers_done, gpointer user_data);
+
+guint soup_transfer_write (GIOChannel             *chan,
+                          const GString          *header,
+                          const SoupDataBuffer   *src,
+                          SoupWriteHeadersDoneFn  headers_done_cb,
+                          SoupWriteDoneFn         write_done_cb,
+                          SoupWriteErrorFn        error_cb,
+                          gpointer                user_data);
+
+void  soup_transfer_write_cancel (guint tag);
+
+#endif /*SOUP_TRANSFER_H*/