+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
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
soup-socks.c \
soup-ssl.h \
soup-ssl.c \
+ soup-transfer.h \
+ soup-transfer.c \
soup-uri.c
libsoup_apache_la_LDFLAGS = \
#include "soup-message.h"
#include "soup-context.h"
#include "soup-private.h"
+#include "soup-transfer.h"
/**
* soup_message_new:
return ret;
}
-#define source_remove(_src) \
- ({ if ((_src)) { g_source_remove ((_src)); (_src) = 0; }})
-
/**
* soup_message_cleanup:
* @req: a %SoupMessage.
{
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);
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);
}
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),
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;
#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);
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))
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");
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;
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
}
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);
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,
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;
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"))
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
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:
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);
+}
--- /dev/null
+/* -*- 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);
+}
--- /dev/null
+/* -*- 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*/