+2003-08-22 Dan Winship <danw@ximian.com>
+
+ * libsoup/soup-message-private.h: New file containing
+ SoupMessagePrivate and some other soup-message-internal
+ types/functions. Also includes the new, expanded SoupMessageStatus
+ enum.
+
+ * libsoup/soup-message-io.c: Replaces what used to be in
+ soup-transfer, but now all the interfaces take SoupMessages
+ instead of SoupReader/SoupWriter and deal with maintaining
+ msg->priv->{read,write}_state themselves. Fixes up all the
+ refcounting madness.
+
+ * libsoup/soup-message-handlers.c: Move the handler code here,
+ mostly unchanged. (But rename SoupHandlerType to SoupHandlerPhase
+ to make the distinction from SoupHandlerKind clearer.)
+
+ * libsoup/soup-message.c: Update for soup-message-io and new
+ SoupMessageStatus values. Remove handler code.
+ (soup_message_cleanup): Remove the hack to try to preserve the
+ connection if the message gets cleaned up before it finishes
+ reading. soup_message_requeue handles this in the requeuing case,
+ and there's no especially compelling reason to bother doing it in
+ any other case. (And the soup-message-io api doesn't support
+ having a read operation that's not connected to any message.)
+
+ * libsoup/soup-private.h: remove SoupMessagePrivate
+
+ * libsoup/soup-queue.c: Update for soup-message-io and new
+ SoupMessageStatus values.
+
+ * libsoup/soup-server-message.c: Likewise
+
+ * libsoup/soup-server.c: Likewise
+
+ * libsoup/soup-transfer.c: Gone (yay)
+
+ * libsoup/Makefile.am (libsoup_2_2_la_SOURCES): update
+
2003-08-20 Dan Winship <danw@ximian.com>
* libsoup/soup-message.c: Make this a GObject. (Note that since
soup-gnutls.h \
soup-gnutls.c \
soup-headers.c \
+ soup-message-private.h \
soup-message.c \
+ soup-message-handlers.c \
+ soup-message-io.c \
soup-method.c \
soup-misc.c \
soup-private.h \
soup-socket.c \
soup-ssl.h \
soup-ssl.c \
- soup-transfer.h \
- soup-transfer.c \
soup-uri.c
EXTRA_DIST= soup-marshal.list
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-message-handlers.c: HTTP response handlers
+ *
+ * Copyright (C) 2000-2003, Ximian, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "soup-message.h"
+#include "soup-message-private.h"
+#include "soup-misc.h"
+#include "soup-private.h"
+
+typedef enum {
+ SOUP_HANDLER_HEADER = 1,
+ SOUP_HANDLER_ERROR_CODE,
+ SOUP_HANDLER_ERROR_CLASS
+} SoupHandlerKind;
+
+typedef struct {
+ SoupHandlerPhase phase;
+ SoupCallbackFn handler_cb;
+ gpointer user_data;
+
+ SoupHandlerKind kind;
+ union {
+ guint errorcode;
+ SoupErrorClass errorclass;
+ const char *header;
+ } data;
+} SoupHandlerData;
+
+static void redirect_handler (SoupMessage *msg, gpointer user_data);
+static void authorize_handler (SoupMessage *msg, gpointer proxy);
+
+static SoupHandlerData global_handlers [] = {
+ /* Handle redirect response codes. */
+ {
+ SOUP_HANDLER_PRE_BODY,
+ redirect_handler,
+ NULL,
+ SOUP_HANDLER_ERROR_CLASS,
+ { SOUP_ERROR_CLASS_REDIRECT }
+ },
+
+ /* Handle authorization. */
+ {
+ SOUP_HANDLER_PRE_BODY,
+ authorize_handler,
+ GINT_TO_POINTER (FALSE),
+ SOUP_HANDLER_ERROR_CODE,
+ { 401 }
+ },
+
+ /* Handle proxy authorization. */
+ {
+ SOUP_HANDLER_PRE_BODY,
+ authorize_handler,
+ GINT_TO_POINTER (TRUE),
+ SOUP_HANDLER_ERROR_CODE,
+ { 407 }
+ },
+
+ { 0 }
+};
+
+static inline void
+run_handler (SoupMessage *msg,
+ SoupHandlerPhase invoke_phase,
+ SoupHandlerData *data)
+{
+ if (data->phase != invoke_phase)
+ return;
+
+ switch (data->kind) {
+ case SOUP_HANDLER_HEADER:
+ if (!soup_message_get_header (msg->response_headers,
+ data->data.header))
+ return;
+ break;
+ case SOUP_HANDLER_ERROR_CODE:
+ if (msg->errorcode != data->data.errorcode)
+ return;
+ break;
+ case SOUP_HANDLER_ERROR_CLASS:
+ if (msg->errorclass != data->data.errorclass)
+ return;
+ break;
+ default:
+ break;
+ }
+
+ (*data->handler_cb) (msg, data->user_data);
+}
+
+/*
+ * Run each handler with matching criteria (first per-message then
+ * global handlers). If a handler requeues a message, we stop
+ * processing and terminate the current request.
+ *
+ * After running all handlers, if there is an error set or the invoke
+ * phase was post_body, issue the final callback.
+ *
+ * FIXME: If the errorcode is changed by a handler, we should restart
+ * the processing.
+ */
+void
+soup_message_run_handlers (SoupMessage *msg, SoupHandlerPhase invoke_phase)
+{
+ GSList *list;
+ SoupHandlerData *data;
+
+ g_return_if_fail (SOUP_IS_MESSAGE (msg));
+
+ for (list = msg->priv->content_handlers; list; list = list->next) {
+ run_handler (msg, invoke_phase, list->data);
+
+ if (SOUP_MESSAGE_IS_STARTING (msg))
+ return;
+ }
+
+ for (data = global_handlers; data->phase; data++) {
+ run_handler (msg, invoke_phase, data);
+
+ if (SOUP_MESSAGE_IS_STARTING (msg))
+ return;
+ }
+
+ /* Issue final callback if the invoke_phase is POST_BODY and
+ * the error class is not INFORMATIONAL.
+ */
+ if (invoke_phase == SOUP_HANDLER_POST_BODY &&
+ msg->errorclass != SOUP_ERROR_CLASS_INFORMATIONAL)
+ soup_message_issue_callback (msg);
+}
+
+static void
+add_handler (SoupMessage *msg,
+ SoupHandlerPhase phase,
+ SoupCallbackFn handler_cb,
+ gpointer user_data,
+ SoupHandlerKind kind,
+ const char *header,
+ guint errorcode,
+ guint errorclass)
+{
+ SoupHandlerData *data;
+
+ data = g_new0 (SoupHandlerData, 1);
+ data->phase = phase;
+ data->handler_cb = handler_cb;
+ data->user_data = user_data;
+ data->kind = kind;
+
+ switch (kind) {
+ case SOUP_HANDLER_HEADER:
+ data->data.header = header;
+ break;
+ case SOUP_HANDLER_ERROR_CODE:
+ data->data.errorcode = errorcode;
+ break;
+ case SOUP_HANDLER_ERROR_CLASS:
+ data->data.errorclass = errorclass;
+ break;
+ default:
+ break;
+ }
+
+ msg->priv->content_handlers =
+ g_slist_append (msg->priv->content_handlers, data);
+}
+
+void
+soup_message_add_header_handler (SoupMessage *msg,
+ const char *header,
+ SoupHandlerPhase phase,
+ SoupCallbackFn handler_cb,
+ gpointer user_data)
+{
+ g_return_if_fail (SOUP_IS_MESSAGE (msg));
+ g_return_if_fail (header != NULL);
+ g_return_if_fail (handler_cb != NULL);
+
+ add_handler (msg, phase, handler_cb, user_data,
+ SOUP_HANDLER_HEADER,
+ header, 0, 0);
+}
+
+void
+soup_message_add_error_code_handler (SoupMessage *msg,
+ guint errorcode,
+ SoupHandlerPhase phase,
+ SoupCallbackFn handler_cb,
+ gpointer user_data)
+{
+ g_return_if_fail (SOUP_IS_MESSAGE (msg));
+ g_return_if_fail (errorcode != 0);
+ g_return_if_fail (handler_cb != NULL);
+
+ add_handler (msg, phase, handler_cb, user_data,
+ SOUP_HANDLER_ERROR_CODE,
+ NULL, errorcode, 0);
+}
+
+void
+soup_message_add_error_class_handler (SoupMessage *msg,
+ SoupErrorClass errorclass,
+ SoupHandlerPhase phase,
+ SoupCallbackFn handler_cb,
+ gpointer user_data)
+{
+ g_return_if_fail (SOUP_IS_MESSAGE (msg));
+ g_return_if_fail (errorclass != 0);
+ g_return_if_fail (handler_cb != NULL);
+
+ add_handler (msg, phase, handler_cb, user_data,
+ SOUP_HANDLER_ERROR_CLASS,
+ NULL, 0, errorclass);
+}
+
+void
+soup_message_add_handler (SoupMessage *msg,
+ SoupHandlerPhase phase,
+ SoupCallbackFn handler_cb,
+ gpointer user_data)
+{
+ g_return_if_fail (SOUP_IS_MESSAGE (msg));
+ g_return_if_fail (handler_cb != NULL);
+
+ add_handler (msg, phase, handler_cb, user_data, 0, NULL, 0, 0);
+}
+
+void
+soup_message_remove_handler (SoupMessage *msg,
+ SoupHandlerPhase phase,
+ SoupCallbackFn handler_cb,
+ gpointer user_data)
+{
+ GSList *iter = msg->priv->content_handlers;
+
+ while (iter) {
+ SoupHandlerData *data = iter->data;
+
+ if (data->handler_cb == handler_cb &&
+ data->user_data == user_data &&
+ data->phase == phase) {
+ msg->priv->content_handlers =
+ g_slist_remove (msg->priv->content_handlers,
+ data);
+ g_free (data);
+ break;
+ }
+
+ iter = iter->next;
+ }
+}
+
+
+/* FIXME: these don't belong here */
+
+static void
+authorize_handler (SoupMessage *msg, gpointer proxy)
+{
+ SoupContext *ctx;
+
+ ctx = proxy ? soup_get_proxy () : msg->context;
+ if (soup_context_update_auth (ctx, msg))
+ soup_message_requeue (msg);
+ else {
+ soup_message_set_error (msg,
+ proxy ?
+ SOUP_ERROR_CANT_AUTHENTICATE_PROXY :
+ SOUP_ERROR_CANT_AUTHENTICATE);
+ }
+}
+
+static void
+redirect_handler (SoupMessage *msg, gpointer user_data)
+{
+ const char *new_loc;
+ const SoupUri *old_uri;
+ SoupUri *new_uri;
+ SoupContext *new_ctx;
+
+ if (msg->priv->msg_flags & SOUP_MESSAGE_NO_REDIRECT)
+ return;
+
+ old_uri = soup_context_get_uri (msg->context);
+
+ new_loc = soup_message_get_header (msg->response_headers, "Location");
+ if (!new_loc)
+ return;
+ new_uri = soup_uri_new (new_loc);
+ if (!new_uri)
+ goto INVALID_REDIRECT;
+
+ /* Copy auth info from original URI. */
+ if (old_uri->user && !new_uri->user)
+ soup_uri_set_auth (new_uri,
+ old_uri->user,
+ old_uri->passwd,
+ old_uri->authmech);
+
+ new_ctx = soup_context_from_uri (new_uri);
+
+ soup_uri_free (new_uri);
+
+ if (!new_ctx)
+ goto INVALID_REDIRECT;
+
+ soup_message_set_context (msg, new_ctx);
+ g_object_unref (new_ctx);
+
+ soup_message_requeue (msg);
+ return;
+
+ INVALID_REDIRECT:
+ soup_message_set_error_full (msg,
+ SOUP_ERROR_MALFORMED,
+ "Invalid Redirect URL");
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-message-io.c: HTTP message I/O
+ *
+ * Copyright (C) 2000-2003, Ximian, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "soup-message.h"
+#include "soup-message-private.h"
+#include "soup-misc.h"
+#include "soup-private.h"
+
+typedef struct {
+ guint idle_tag;
+ guint read_tag;
+ guint err_tag;
+
+ GByteArray *body_buf;
+ GByteArray *meta_buf;
+
+ SoupTransferEncoding encoding;
+ guint read_length;
+
+ SoupMessageReadHeadersFn read_headers_cb;
+ SoupMessageReadChunkFn read_chunk_cb;
+ SoupMessageReadBodyFn read_body_cb;
+ SoupMessageReadErrorFn error_cb;
+} SoupMessageReadState;
+
+/* Put these around callback invocation if there is code afterward
+ * that depends on the read not having been cancelled.
+ */
+#define SOUP_MESSAGE_READ_PREPARE_FOR_CALLBACK { gboolean cancelled; g_object_ref (msg);
+#define SOUP_MESSAGE_READ_RETURN_IF_CANCELLED cancelled = (msg->priv->read_state != r); g_object_unref (msg); if (cancelled) return; }
+#define SOUP_MESSAGE_READ_RETURN_VAL_IF_CANCELLED(val) cancelled = (msg->priv->read_state != r); g_object_unref (msg); if (cancelled) return val; }
+
+void
+soup_message_read_cancel (SoupMessage *msg)
+{
+ SoupMessageReadState *r = msg->priv->read_state;
+
+ if (!r)
+ return;
+
+ if (r->idle_tag)
+ g_source_remove (r->idle_tag);
+ if (r->read_tag)
+ g_signal_handler_disconnect (msg->priv->socket, r->read_tag);
+ if (r->err_tag)
+ g_signal_handler_disconnect (msg->priv->socket, r->err_tag);
+
+ 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_free (r);
+
+ msg->priv->read_state = NULL;
+}
+
+void
+soup_message_read_set_callbacks (SoupMessage *msg,
+ SoupMessageReadHeadersFn read_headers_cb,
+ SoupMessageReadChunkFn read_chunk_cb,
+ SoupMessageReadBodyFn read_body_cb,
+ SoupMessageReadErrorFn error_cb)
+{
+ SoupMessageReadState *r = msg->priv->read_state;
+
+ r->read_headers_cb = read_headers_cb;
+ r->read_chunk_cb = read_chunk_cb;
+ r->read_body_cb = read_body_cb;
+ r->error_cb = error_cb;
+}
+
+static void
+issue_final_callback (SoupMessage *msg)
+{
+ SoupMessageReadState *r = msg->priv->read_state;
+ SoupMessageReadBodyFn read_body_cb = r->read_body_cb;
+ char *body;
+ guint len;
+
+ if (r->body_buf) {
+ body = r->body_buf->data;
+ len = r->body_buf->len;
+
+ g_byte_array_free (r->body_buf, FALSE);
+ r->body_buf = NULL;
+ } else {
+ body = NULL;
+ len = 0;
+ }
+
+ soup_message_read_cancel (msg);
+ read_body_cb (msg, body, len);
+}
+
+static void
+failed_read (SoupSocket *sock, SoupMessage *msg)
+{
+ SoupMessageReadState *r = msg->priv->read_state;
+ SoupMessageReadErrorFn error_cb = r->error_cb;
+
+ /* Closing the connection to signify EOF is valid if content
+ * length is unknown, but only if headers have been sent.
+ */
+ if (msg->priv->status > SOUP_MESSAGE_STATUS_READING_HEADERS &&
+ r->encoding == SOUP_TRANSFER_UNKNOWN) {
+ issue_final_callback (msg);
+ return;
+ }
+
+ soup_message_read_cancel (msg);
+ error_cb (msg);
+}
+
+static gboolean
+read_metadata (SoupMessage *msg, const char *boundary, int boundary_len)
+{
+ SoupMessageReadState *r = msg->priv->read_state;
+ SoupSocketIOStatus status;
+ char read_buf[RESPONSE_BLOCK_SIZE];
+ guint nread;
+ gboolean done;
+
+ do {
+ status = soup_socket_read_until (msg->priv->socket, 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;
+
+ case SOUP_SOCKET_ERROR:
+ case SOUP_SOCKET_EOF:
+ failed_read (msg->priv->socket, msg);
+ return FALSE;
+
+ case SOUP_SOCKET_WOULD_BLOCK:
+ return FALSE;
+ }
+ } while (!done);
+
+ return TRUE;
+}
+
+static gboolean
+read_body_chunk (SoupMessage *msg, guint *size)
+{
+ SoupMessageReadState *r = msg->priv->read_state;
+ SoupSocketIOStatus status;
+ char read_buf[RESPONSE_BLOCK_SIZE];
+ guint nread, len = sizeof (read_buf);
+ gboolean read_to_eof = (r->encoding == SOUP_TRANSFER_UNKNOWN);
+
+ while (read_to_eof || *size > 0) {
+ if (!read_to_eof)
+ len = MIN (len, *size);
+
+ status = soup_socket_read (msg->priv->socket, read_buf,
+ len, &nread);
+
+ switch (status) {
+ case SOUP_SOCKET_OK:
+ if (!nread)
+ break;
+
+ if (r->read_chunk_cb) {
+ SOUP_MESSAGE_READ_PREPARE_FOR_CALLBACK;
+ r->read_chunk_cb (msg, read_buf, nread);
+ SOUP_MESSAGE_READ_RETURN_VAL_IF_CANCELLED (FALSE);
+ }
+
+ if (r->body_buf)
+ g_byte_array_append (r->body_buf, read_buf, nread);
+ *size -= nread;
+ break;
+
+ case SOUP_SOCKET_EOF:
+ if (read_to_eof)
+ return TRUE;
+ /* else fall through */
+
+ case SOUP_SOCKET_ERROR:
+ failed_read (msg->priv->socket, msg);
+ return FALSE;
+
+ case SOUP_SOCKET_WOULD_BLOCK:
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+#define SOUP_TRANSFER_EOL "\r\n"
+#define SOUP_TRANSFER_EOL_LEN 2
+
+#define SOUP_TRANSFER_DOUBLE_EOL "\r\n\r\n"
+#define SOUP_TRANSFER_DOUBLE_EOL_LEN 4
+
+static void
+do_read (SoupSocket *sock, SoupMessage *msg)
+{
+ SoupMessageReadState *r = msg->priv->read_state;
+
+ while (1) {
+ switch (msg->priv->status) {
+ case SOUP_MESSAGE_STATUS_READING_HEADERS:
+ if (!read_metadata (msg, SOUP_TRANSFER_DOUBLE_EOL,
+ SOUP_TRANSFER_DOUBLE_EOL_LEN))
+ return;
+
+ r->meta_buf->len -= SOUP_TRANSFER_DOUBLE_EOL_LEN;
+ if (r->read_headers_cb) {
+ SOUP_MESSAGE_READ_PREPARE_FOR_CALLBACK;
+ r->read_headers_cb (msg,
+ r->meta_buf->data,
+ r->meta_buf->len,
+ &r->encoding,
+ &r->read_length);
+ SOUP_MESSAGE_READ_RETURN_IF_CANCELLED;
+ }
+ g_byte_array_set_size (r->meta_buf, 0);
+
+ switch (r->encoding) {
+ case SOUP_TRANSFER_UNKNOWN:
+ case SOUP_TRANSFER_CONTENT_LENGTH:
+ msg->priv->status =
+ SOUP_MESSAGE_STATUS_READING_BODY;
+ break;
+ case SOUP_TRANSFER_CHUNKED:
+ msg->priv->status =
+ SOUP_MESSAGE_STATUS_READING_CHUNK_SIZE;
+ break;
+ }
+ break;
+
+ case SOUP_MESSAGE_STATUS_READING_BODY:
+ if (!read_body_chunk (msg, &r->read_length))
+ return;
+
+ goto done;
+ break;
+
+ case SOUP_MESSAGE_STATUS_READING_CHUNK_SIZE:
+ if (!read_metadata (msg, SOUP_TRANSFER_EOL,
+ SOUP_TRANSFER_EOL_LEN))
+ return;
+
+ r->read_length = strtoul (r->meta_buf->data, NULL, 16);
+ g_byte_array_set_size (r->meta_buf, 0);
+
+ if (r->read_length > 0) {
+ msg->priv->status =
+ SOUP_MESSAGE_STATUS_READING_CHUNK;
+ } else {
+ msg->priv->status =
+ SOUP_MESSAGE_STATUS_READING_TRAILERS;
+ }
+ break;
+
+ case SOUP_MESSAGE_STATUS_READING_CHUNK:
+ if (!read_body_chunk (msg, &r->read_length))
+ return;
+
+ msg->priv->status =
+ SOUP_MESSAGE_STATUS_READING_CHUNK_END;
+ break;
+
+ case SOUP_MESSAGE_STATUS_READING_CHUNK_END:
+ if (!read_metadata (msg, SOUP_TRANSFER_EOL,
+ SOUP_TRANSFER_EOL_LEN))
+ return;
+
+ g_byte_array_set_size (r->meta_buf, 0);
+ msg->priv->status =
+ SOUP_MESSAGE_STATUS_READING_CHUNK_SIZE;
+ break;
+
+ case SOUP_MESSAGE_STATUS_READING_TRAILERS:
+ if (!read_metadata (msg, SOUP_TRANSFER_EOL,
+ SOUP_TRANSFER_EOL_LEN))
+ return;
+
+ if (r->meta_buf->len == SOUP_TRANSFER_EOL_LEN)
+ goto done;
+
+ /* FIXME: process trailers */
+ g_byte_array_set_size (r->meta_buf, 0);
+ break;
+
+ default:
+ g_return_if_reached ();
+ }
+ }
+
+ done:
+ msg->priv->status = SOUP_MESSAGE_STATUS_FINISHED_READING;
+ issue_final_callback (msg);
+}
+
+static gboolean
+idle_read (gpointer user_data)
+{
+ SoupMessage *msg = user_data;
+ SoupMessageReadState *r = msg->priv->read_state;
+
+ g_return_val_if_fail (r != NULL, FALSE);
+
+ r->idle_tag = 0;
+ do_read (msg->priv->socket, msg);
+ return FALSE;
+}
+
+void
+soup_message_read (SoupMessage *msg,
+ SoupMessageReadHeadersFn read_headers_cb,
+ SoupMessageReadChunkFn read_chunk_cb,
+ SoupMessageReadBodyFn read_body_cb,
+ SoupMessageReadErrorFn error_cb)
+{
+ SoupMessageReadState *r;
+
+ g_return_if_fail (SOUP_IS_MESSAGE (msg));
+ g_return_if_fail (msg->priv->socket != NULL);
+ g_return_if_fail (read_body_cb && error_cb);
+
+ r = g_new0 (SoupMessageReadState, 1);
+ r->read_headers_cb = read_headers_cb;
+ r->read_chunk_cb = read_chunk_cb;
+ r->read_body_cb = read_body_cb;
+ r->error_cb = error_cb;
+ r->encoding = SOUP_TRANSFER_UNKNOWN;
+
+ r->meta_buf = g_byte_array_new ();
+ if (!(msg->priv->msg_flags & SOUP_MESSAGE_OVERWRITE_CHUNKS))
+ r->body_buf = g_byte_array_new ();
+
+ r->read_tag = g_signal_connect (msg->priv->socket, "readable",
+ G_CALLBACK (do_read), msg);
+ r->err_tag = g_signal_connect (msg->priv->socket, "disconnected",
+ G_CALLBACK (failed_read), msg);
+
+ r->idle_tag = g_idle_add (idle_read, msg);
+
+ msg->priv->status = SOUP_MESSAGE_STATUS_READING_HEADERS;
+ msg->priv->read_state = r;
+}
+
+
+
+typedef struct {
+ guint idle_tag;
+ guint write_tag;
+ guint err_tag;
+
+ GString *buf;
+ SoupTransferEncoding encoding;
+ const SoupDataBuffer *body;
+ SoupDataBuffer chunk;
+ guint nwrote;
+
+ SoupMessageWriteGetHeaderFn get_header_cb;
+ SoupMessageWriteGetChunkFn get_chunk_cb;
+ SoupMessageWriteDoneFn write_done_cb;
+ SoupMessageWriteErrorFn error_cb;
+} SoupMessageWriteState;
+
+/* Put these around callback invocation if there is code afterward
+ * that depends on the write not having been cancelled or paused.
+ */
+#define SOUP_MESSAGE_WRITE_PREPARE_FOR_CALLBACK { gboolean cancelled; g_object_ref (msg);
+#define SOUP_MESSAGE_WRITE_RETURN_IF_CANCELLED cancelled = (msg->priv->write_state != w); g_object_unref (msg); if (cancelled || !w->write_tag) return; }
+
+void
+soup_message_write_cancel (SoupMessage *msg)
+{
+ SoupMessageWriteState *w = msg->priv->write_state;
+
+ if (!w)
+ return;
+
+ if (w->idle_tag)
+ g_source_remove (w->idle_tag);
+ if (w->err_tag)
+ g_signal_handler_disconnect (msg->priv->socket, w->err_tag);
+ if (w->write_tag)
+ g_signal_handler_disconnect (msg->priv->socket, w->write_tag);
+
+ g_string_free (w->buf, TRUE);
+
+ g_free (w);
+
+ msg->priv->write_state = NULL;
+}
+
+static void
+failed_write (SoupSocket *sock, SoupMessage *msg)
+{
+ SoupMessageWriteState *w = msg->priv->write_state;
+ SoupMessageWriteErrorFn error_cb = w->error_cb;
+
+ soup_message_write_cancel (msg);
+ error_cb (msg);
+}
+
+static gboolean
+write_data (SoupMessage *msg, const char *data, guint len)
+{
+ SoupMessageWriteState *w = msg->priv->write_state;
+ SoupSocketIOStatus status;
+ guint nwrote;
+
+ while (len - w->nwrote) {
+ status = soup_socket_write (msg->priv->socket,
+ data + w->nwrote,
+ len - w->nwrote,
+ &nwrote);
+ switch (status) {
+ case SOUP_SOCKET_EOF:
+ case SOUP_SOCKET_ERROR:
+ failed_write (msg->priv->socket, msg);
+ return FALSE;
+
+ case SOUP_SOCKET_WOULD_BLOCK:
+ return FALSE;
+
+ case SOUP_SOCKET_OK:
+ w->nwrote += nwrote;
+ break;
+ }
+ }
+
+ return TRUE;
+}
+
+static void
+do_write (SoupSocket *sock, SoupMessage *msg)
+{
+ SoupMessageWriteState *w = msg->priv->write_state;
+ SoupMessageWriteDoneFn write_done_cb = w->write_done_cb;
+
+ while (1) {
+ switch (msg->priv->status) {
+ case SOUP_MESSAGE_STATUS_WRITING_HEADERS:
+ if (w->get_header_cb) {
+ SOUP_MESSAGE_WRITE_PREPARE_FOR_CALLBACK;
+ w->get_header_cb (msg, w->buf);
+ SOUP_MESSAGE_WRITE_RETURN_IF_CANCELLED;
+
+ w->get_header_cb = NULL;
+ w->nwrote = 0;
+ }
+
+ if (!write_data (msg, w->buf->str, w->buf->len))
+ return;
+
+ g_string_truncate (w->buf, 0);
+ w->nwrote = 0;
+ if (w->encoding == SOUP_TRANSFER_CHUNKED) {
+ msg->priv->status =
+ SOUP_MESSAGE_STATUS_WRITING_CHUNK_SIZE;
+ } else {
+ msg->priv->status =
+ SOUP_MESSAGE_STATUS_WRITING_BODY;
+ }
+ break;
+
+ case SOUP_MESSAGE_STATUS_WRITING_BODY:
+ if (!write_data (msg, w->body->body,
+ w->body->length))
+ return;
+
+ goto done;
+
+ case SOUP_MESSAGE_STATUS_WRITING_CHUNK_SIZE:
+ if (!w->buf->len) {
+ gboolean got_chunk;
+
+ SOUP_MESSAGE_WRITE_PREPARE_FOR_CALLBACK;
+ got_chunk =
+ w->get_chunk_cb (msg, &w->chunk);
+ SOUP_MESSAGE_WRITE_RETURN_IF_CANCELLED;
+
+ if (!got_chunk) {
+ /* No more chunks. Write the
+ * 0-length chunk to signify
+ * the end.
+ */
+ w->chunk.length = 0;
+ w->get_chunk_cb = NULL;
+ }
+
+ g_string_append_printf (w->buf, "%x\r\n",
+ w->chunk.length);
+ w->nwrote = 0;
+ }
+
+ if (!write_data (msg, w->buf->str, w->buf->len))
+ return;
+
+ g_string_truncate (w->buf, 0);
+ w->nwrote = 0;
+ msg->priv->status = SOUP_MESSAGE_STATUS_WRITING_CHUNK;
+ /* fall through */
+
+ case SOUP_MESSAGE_STATUS_WRITING_CHUNK:
+ if (!write_data (msg, w->chunk.body,
+ w->chunk.length))
+ return;
+
+ if (w->chunk.owner == SOUP_BUFFER_SYSTEM_OWNED)
+ g_free (w->chunk.body);
+ memset (&w->chunk, 0, sizeof (SoupDataBuffer));
+
+ w->nwrote = 0;
+ msg->priv->status = SOUP_MESSAGE_STATUS_WRITING_CHUNK_END;
+ /* fall through */
+
+ case SOUP_MESSAGE_STATUS_WRITING_CHUNK_END:
+ if (!write_data (msg, SOUP_TRANSFER_EOL,
+ SOUP_TRANSFER_EOL_LEN))
+ return;
+
+ w->nwrote = 0;
+ if (w->get_chunk_cb) {
+ msg->priv->status =
+ SOUP_MESSAGE_STATUS_WRITING_CHUNK_SIZE;
+ break;
+ }
+
+ msg->priv->status =
+ SOUP_MESSAGE_STATUS_WRITING_TRAILERS;
+ /* fall through */
+
+ case SOUP_MESSAGE_STATUS_WRITING_TRAILERS:
+ if (!write_data (msg, SOUP_TRANSFER_EOL,
+ SOUP_TRANSFER_EOL_LEN))
+ return;
+
+ goto done;
+
+ default:
+ g_return_if_reached ();
+ }
+ }
+
+ done:
+ msg->priv->status = SOUP_MESSAGE_STATUS_FINISHED_WRITING;
+ soup_message_write_cancel (msg);
+ write_done_cb (msg);
+}
+
+static gboolean
+idle_write (gpointer user_data)
+{
+ SoupMessage *msg = user_data;
+ SoupMessageWriteState *w = msg->priv->write_state;
+
+ w->idle_tag = 0;
+ do_write (msg->priv->socket, msg);
+ return FALSE;
+}
+
+static SoupMessageWriteState *
+create_writer (SoupMessage *msg,
+ SoupTransferEncoding encoding,
+ SoupMessageWriteGetHeaderFn get_header_cb,
+ SoupMessageWriteDoneFn write_done_cb,
+ SoupMessageWriteErrorFn error_cb)
+{
+ SoupMessageWriteState *w;
+
+ w = g_new0 (SoupMessageWriteState, 1);
+ w->encoding = encoding;
+ w->buf = g_string_new (NULL);
+ w->get_header_cb = get_header_cb;
+ w->write_done_cb = write_done_cb;
+ w->error_cb = error_cb;
+
+ w->write_tag =
+ g_signal_connect (msg->priv->socket, "writable",
+ G_CALLBACK (do_write), msg);
+ w->err_tag =
+ g_signal_connect (msg->priv->socket, "disconnected",
+ G_CALLBACK (failed_write), msg);
+
+ w->idle_tag = g_idle_add (idle_write, msg);
+
+ msg->priv->status = SOUP_MESSAGE_STATUS_WRITING_HEADERS;
+ msg->priv->write_state = w;
+
+ return w;
+}
+
+void
+soup_message_write_simple (SoupMessage *msg,
+ const SoupDataBuffer *body,
+ SoupMessageWriteGetHeaderFn get_header_cb,
+ SoupMessageWriteDoneFn write_done_cb,
+ SoupMessageWriteErrorFn error_cb)
+{
+ SoupMessageWriteState *w;
+
+ w = create_writer (msg, SOUP_TRANSFER_CONTENT_LENGTH,
+ get_header_cb, write_done_cb, error_cb);
+
+ w->body = body;
+}
+
+void
+soup_message_write (SoupMessage *msg,
+ SoupTransferEncoding encoding,
+ SoupMessageWriteGetHeaderFn get_header_cb,
+ SoupMessageWriteGetChunkFn get_chunk_cb,
+ SoupMessageWriteDoneFn write_done_cb,
+ SoupMessageWriteErrorFn error_cb)
+{
+ SoupMessageWriteState *w;
+
+ w = create_writer (msg, encoding, get_header_cb,
+ write_done_cb, error_cb);
+ w->get_chunk_cb = get_chunk_cb;
+}
+
+void
+soup_message_write_pause (SoupMessage *msg)
+{
+ SoupMessageWriteState *w;
+
+ g_return_if_fail (SOUP_IS_MESSAGE (msg));
+
+ w = msg->priv->write_state;
+ g_return_if_fail (w != NULL);
+
+ if (w->write_tag) {
+ g_signal_handler_disconnect (msg->priv->socket, w->write_tag);
+ w->write_tag = 0;
+ }
+ if (w->idle_tag) {
+ g_source_remove (w->idle_tag);
+ w->idle_tag = 0;
+ }
+}
+
+void
+soup_message_write_unpause (SoupMessage *msg)
+{
+ SoupMessageWriteState *w;
+
+ g_return_if_fail (SOUP_IS_MESSAGE (msg));
+
+ w = msg->priv->write_state;
+ if (!w)
+ return;
+
+ if (!w->write_tag) {
+ w->write_tag = g_signal_connect (msg->priv->socket, "writable",
+ G_CALLBACK (do_write), msg);
+ }
+ if (!w->idle_tag)
+ w->idle_tag = g_idle_add (idle_write, msg);
+}
--- /dev/null
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2000-2003, Ximian, Inc.
+ */
+
+#ifndef SOUP_MESSAGE_PRIVATE_H
+#define SOUP_MESSAGE_PRIVATE_H 1
+
+typedef enum {
+ SOUP_MESSAGE_STATUS_IDLE,
+ SOUP_MESSAGE_STATUS_QUEUED,
+ SOUP_MESSAGE_STATUS_CONNECTING,
+ SOUP_MESSAGE_STATUS_FINISHED,
+
+ SOUP_MESSAGE_STATUS_WRITING_HEADERS,
+ SOUP_MESSAGE_STATUS_WRITING_BODY,
+ SOUP_MESSAGE_STATUS_WRITING_CHUNK_SIZE,
+ SOUP_MESSAGE_STATUS_WRITING_CHUNK,
+ SOUP_MESSAGE_STATUS_WRITING_CHUNK_END,
+ SOUP_MESSAGE_STATUS_WRITING_TRAILERS,
+ SOUP_MESSAGE_STATUS_FINISHED_WRITING,
+
+ SOUP_MESSAGE_STATUS_READING_HEADERS,
+ SOUP_MESSAGE_STATUS_READING_BODY,
+ SOUP_MESSAGE_STATUS_READING_CHUNK_SIZE,
+ SOUP_MESSAGE_STATUS_READING_CHUNK,
+ SOUP_MESSAGE_STATUS_READING_CHUNK_END,
+ SOUP_MESSAGE_STATUS_READING_TRAILERS,
+ SOUP_MESSAGE_STATUS_FINISHED_READING
+
+} SoupMessageStatus;
+
+#define SOUP_MESSAGE_IS_STARTING(msg) (msg->priv->status == SOUP_MESSAGE_STATUS_QUEUED || msg->priv->status == SOUP_MESSAGE_STATUS_CONNECTING)
+#define SOUP_MESSAGE_IS_WRITING(msg) (msg->priv->status >= SOUP_MESSAGE_STATUS_WRITING_HEADERS && msg->priv->status <= SOUP_MESSAGE_STATUS_WRITING_TRAILERS)
+#define SOUP_MESSAGE_IS_READING(msg) (msg->priv->status >= SOUP_MESSAGE_STATUS_READING_HEADERS && msg->priv->status <= SOUP_MESSAGE_STATUS_READING_TRAILERS)
+
+
+struct SoupMessagePrivate {
+ SoupMessageStatus status;
+
+ SoupConnectId connect_tag;
+ gpointer read_state;
+ gpointer write_state;
+
+ guint retries;
+
+ SoupCallbackFn callback;
+ gpointer user_data;
+
+ guint msg_flags;
+
+ GSList *content_handlers;
+
+ SoupHttpVersion http_version;
+
+ SoupConnection *connection;
+ SoupSocket *socket;
+};
+
+void soup_message_issue_callback (SoupMessage *req);
+void soup_message_run_handlers (SoupMessage *msg,
+ SoupHandlerPhase invoke_phase);
+
+void soup_message_cleanup (SoupMessage *req);
+
+
+typedef void (*SoupMessageReadHeadersFn) (SoupMessage *msg,
+ char *headers,
+ guint header_len,
+ SoupTransferEncoding *encoding,
+ int *content_len);
+
+typedef void (*SoupMessageReadChunkFn) (SoupMessage *msg,
+ const char *chunk,
+ guint len);
+
+typedef void (*SoupMessageReadBodyFn) (SoupMessage *msg,
+ char *body,
+ guint len);
+
+typedef void (*SoupMessageReadErrorFn) (SoupMessage *msg);
+
+
+void soup_message_read (SoupMessage *msg,
+ SoupMessageReadHeadersFn read_headers_cb,
+ SoupMessageReadChunkFn read_chunk_cb,
+ SoupMessageReadBodyFn read_body_cb,
+ SoupMessageReadErrorFn error_cb);
+void soup_message_read_set_callbacks (SoupMessage *msg,
+ SoupMessageReadHeadersFn read_headers_cb,
+ SoupMessageReadChunkFn read_chunk_cb,
+ SoupMessageReadBodyFn read_body_cb,
+ SoupMessageReadErrorFn error_cb);
+void soup_message_read_cancel (SoupMessage *msg);
+
+
+typedef void (*SoupMessageWriteGetHeaderFn) (SoupMessage *msg,
+ GString *out_hdr);
+
+typedef gboolean (*SoupMessageWriteGetChunkFn) (SoupMessage *msg,
+ SoupDataBuffer *out_next);
+
+typedef void (*SoupMessageWriteDoneFn) (SoupMessage *msg);
+
+typedef void (*SoupMessageWriteErrorFn) (SoupMessage *msg);
+
+void soup_message_write (SoupMessage *msg,
+ SoupTransferEncoding encoding,
+ SoupMessageWriteGetHeaderFn get_header_cb,
+ SoupMessageWriteGetChunkFn get_chunk_cb,
+ SoupMessageWriteDoneFn write_done_cb,
+ SoupMessageWriteErrorFn error_cb);
+void soup_message_write_simple (SoupMessage *msg,
+ const SoupDataBuffer *body,
+ SoupMessageWriteGetHeaderFn get_header_cb,
+ SoupMessageWriteDoneFn write_done_cb,
+ SoupMessageWriteErrorFn error_cb);
+void soup_message_write_cancel (SoupMessage *msg);
+
+void soup_message_write_pause (SoupMessage *msg);
+void soup_message_write_unpause (SoupMessage *msg);
+
+
+#endif /* SOUP_MESSAGE_PRIVATE_H */
#include "soup-auth.h"
#include "soup-error.h"
#include "soup-message.h"
+#include "soup-message-private.h"
#include "soup-misc.h"
#include "soup-context.h"
#include "soup-private.h"
#include "soup-queue.h"
-#include "soup-transfer.h"
#define PARENT_TYPE G_TYPE_OBJECT
static GObjectClass *parent_class;
msg->priv = g_new0 (SoupMessagePrivate, 1);
- msg->status = SOUP_STATUS_IDLE;
+ msg->priv->status = SOUP_MESSAGE_STATUS_IDLE;
msg->request_headers = g_hash_table_new (soup_str_case_hash,
soup_str_case_equal);
* Creates a new empty #SoupMessage, which will connect to the URL
* represented by @context. A reference will be added to @context.
*
- * The new message has a status of %SOUP_STATUS_IDLE.
- *
* Return value: the new #SoupMessage.
*/
SoupMessage *
SoupMessage *msg;
msg = g_object_new (SOUP_TYPE_MESSAGE, NULL);
- msg->status = SOUP_STATUS_IDLE;
msg->method = method ? method : SOUP_METHOD_GET;
soup_message_set_context (msg, context);
* request data buffer will be filled from @req_owner, @req_body, and
* @req_length.
*
- * The new message has a status of %SOUP_STATUS_IDLE.
- *
* Return value: the new #SoupMessage.
*/
SoupMessage *
return msg;
}
-static void
-release_connection (char *body,
- guint len,
- gpointer user_data)
-{
- SoupConnection *conn = user_data;
-
- soup_connection_set_in_use (conn, FALSE);
- g_object_unref (conn);
- g_free (body);
-}
-
-static void
-release_and_close_connection (gboolean headers_done, gpointer user_data)
-{
- SoupConnection *conn = user_data;
-
- soup_connection_disconnect (conn);
- g_object_unref (conn);
-}
-
/**
* soup_message_cleanup:
* @req: a #SoupMessage.
{
g_return_if_fail (SOUP_IS_MESSAGE (req));
- if (req->priv->connection && req->priv->read_tag &&
- req->status == SOUP_STATUS_READING_RESPONSE) {
- soup_transfer_read_set_callbacks (req->priv->read_tag,
- NULL,
- NULL,
- release_connection,
- release_and_close_connection,
- req->priv->connection);
- soup_transfer_read_unref (req->priv->read_tag);
- req->priv->read_tag = NULL;
- req->priv->connection = NULL;
-
- g_object_unref (req->priv->socket);
- req->priv->socket = NULL;
- }
-
- if (req->priv->read_tag) {
- soup_transfer_read_cancel (req->priv->read_tag);
- req->priv->read_tag = NULL;
- }
+ if (req->priv->read_state)
+ soup_message_read_cancel (req);
- if (req->priv->write_tag) {
- soup_transfer_write_cancel (req->priv->write_tag);
- req->priv->write_tag = NULL;
- }
+ if (req->priv->write_state)
+ soup_message_write_cancel (req);
if (req->priv->connect_tag) {
soup_context_cancel_connect (req->priv->connect_tag);
if (req->priv->callback) {
(*req->priv->callback) (req, req->priv->user_data);
- if (req->status != SOUP_STATUS_QUEUED)
+ if (!SOUP_MESSAGE_IS_STARTING (req))
g_object_unref (req);
}
}
* soup_message_cancel:
* @msg: a #SoupMessage currently being processed.
*
- * Cancel a running message, and issue completion callback with a
- * #SoupTransferStatus of %SOUP_ERROR_CANCELLED. If not requeued by
- * the completion callback, the @msg will be destroyed.
+ * Cancel a running message, and issue completion callback with an
+ * error code of %SOUP_ERROR_CANCELLED. If not requeued by the
+ * completion callback, the @msg will be destroyed.
*/
void
soup_message_cancel (SoupMessage *msg)
}
static void
-requeue_read_error (gboolean body_started, gpointer user_data)
+requeue_read_error (SoupMessage *msg)
{
- SoupMessage *msg = user_data;
-
soup_message_disconnect (msg);
soup_queue_message (msg,
msg->priv->callback,
}
static void
-requeue_read_finished (char *body,
- guint len,
- gpointer user_data)
+requeue_read_finished (SoupMessage *msg, char *body, guint len)
{
- SoupMessage *msg = user_data;
SoupConnection *conn = msg->priv->connection;
g_free (body);
{
g_return_if_fail (SOUP_IS_MESSAGE (req));
- if (req->priv->connection && req->priv->read_tag) {
- soup_transfer_read_set_callbacks (req->priv->read_tag,
- NULL,
- NULL,
- requeue_read_finished,
- requeue_read_error,
- req);
- soup_transfer_read_unref (req->priv->read_tag);
- req->priv->read_tag = NULL;
-
- if (req->priv->write_tag) {
- soup_transfer_write_cancel (req->priv->write_tag);
- req->priv->write_tag = NULL;
- }
- } else
+ if (req->priv->connection && req->priv->read_state) {
+ soup_message_read_set_callbacks (req, NULL, NULL,
+ requeue_read_finished,
+ requeue_read_error);
+
+ if (req->priv->write_state)
+ soup_message_write_cancel (req);
+ } else {
soup_queue_message (req,
req->priv->callback,
req->priv->user_data);
+ }
}
/**
while (1) {
g_main_iteration (TRUE);
- if (msg->status == SOUP_STATUS_FINISHED ||
+ if (msg->priv->status == SOUP_MESSAGE_STATUS_FINISHED ||
SOUP_ERROR_IS_TRANSPORT (msg->errorcode))
break;
return msg->errorclass;
}
-static void
-authorize_handler (SoupMessage *msg, gboolean proxy)
-{
- SoupContext *ctx;
-
- ctx = proxy ? soup_get_proxy () : msg->context;
- if (soup_context_update_auth (ctx, msg))
- soup_message_requeue (msg);
- else {
- soup_message_set_error (msg,
- proxy ?
- SOUP_ERROR_CANT_AUTHENTICATE_PROXY :
- SOUP_ERROR_CANT_AUTHENTICATE);
- }
-}
-
-static void
-redirect_handler (SoupMessage *msg, gpointer user_data)
-{
- const char *new_loc;
- const SoupUri *old_uri;
- SoupUri *new_uri;
- SoupContext *new_ctx;
-
- if (msg->priv->msg_flags & SOUP_MESSAGE_NO_REDIRECT)
- return;
-
- new_loc = soup_message_get_header (msg->response_headers, "Location");
- if (!new_loc)
- return;
-
- old_uri = soup_context_get_uri (msg->context);
-
- new_uri = soup_uri_new (new_loc);
- if (!new_uri)
- goto INVALID_REDIRECT;
-
- /*
- * Copy auth info from original URI.
- */
- if (old_uri->user && !new_uri->user)
- soup_uri_set_auth (new_uri,
- old_uri->user,
- old_uri->passwd,
- old_uri->authmech);
-
- new_ctx = soup_context_from_uri (new_uri);
-
- soup_uri_free (new_uri);
-
- if (!new_ctx)
- goto INVALID_REDIRECT;
-
- soup_message_set_context (msg, new_ctx);
- g_object_unref (new_ctx);
-
- soup_message_requeue (msg);
- return;
-
- INVALID_REDIRECT:
- soup_message_set_error_full (msg,
- SOUP_ERROR_MALFORMED,
- "Invalid Redirect URL");
-}
-
-typedef enum {
- RESPONSE_HEADER_HANDLER = 1,
- RESPONSE_ERROR_CODE_HANDLER,
- RESPONSE_ERROR_CLASS_HANDLER
-} SoupHandlerKind;
-
-typedef struct {
- SoupHandlerType type;
- SoupCallbackFn handler_cb;
- gpointer user_data;
-
- SoupHandlerKind kind;
- union {
- guint errorcode;
- SoupErrorClass errorclass;
- const char *header;
- } data;
-} SoupHandlerData;
-
-static SoupHandlerData global_handlers [] = {
- /*
- * Handle redirect response codes 300, 301, 302, 303, and 305.
- */
- {
- SOUP_HANDLER_PRE_BODY,
- redirect_handler,
- NULL,
- RESPONSE_ERROR_CLASS_HANDLER,
- { SOUP_ERROR_CLASS_REDIRECT }
- },
- /*
- * Handle authorization.
- */
- {
- SOUP_HANDLER_PRE_BODY,
- (SoupCallbackFn) authorize_handler,
- GINT_TO_POINTER (FALSE),
- RESPONSE_ERROR_CODE_HANDLER,
- { 401 }
- },
- /*
- * Handle proxy authorization.
- */
- {
- SOUP_HANDLER_PRE_BODY,
- (SoupCallbackFn) authorize_handler,
- GINT_TO_POINTER (TRUE),
- RESPONSE_ERROR_CODE_HANDLER,
- { 407 }
- },
- { 0 }
-};
-
-static inline void
-run_handler (SoupMessage *msg,
- SoupHandlerType invoke_type,
- SoupHandlerData *data)
-{
- if (data->type != invoke_type) return;
-
- switch (data->kind) {
- case RESPONSE_HEADER_HANDLER:
- if (!soup_message_get_header (msg->response_headers,
- data->data.header))
- return;
- break;
- case RESPONSE_ERROR_CODE_HANDLER:
- if (msg->errorcode != data->data.errorcode) return;
- break;
- case RESPONSE_ERROR_CLASS_HANDLER:
- if (msg->errorclass != data->data.errorclass) return;
- break;
- default:
- break;
- }
-
- (*data->handler_cb) (msg, data->user_data);
-}
-
-/*
- * Run each handler with matching criteria (first per-message then global
- * handlers). If a handler requeues a message, we stop processing and terminate
- * the current request.
- *
- * After running all handlers, if there is an error set or the invoke type was
- * post_body, issue the final callback.
- *
- * FIXME: If the errorcode is changed by a handler, we should restart the
- * processing.
- */
-gboolean
-soup_message_run_handlers (SoupMessage *msg, SoupHandlerType invoke_type)
-{
- GSList *list;
- SoupHandlerData *data;
-
- g_return_val_if_fail (SOUP_IS_MESSAGE (msg), FALSE);
-
- for (list = msg->priv->content_handlers; list; list = list->next) {
- data = list->data;
-
- run_handler (msg, invoke_type, data);
-
- if (msg->status == SOUP_STATUS_QUEUED ||
- msg->status == SOUP_STATUS_CONNECTING) return TRUE;
- }
-
- for (data = global_handlers; data->type; data++) {
- run_handler (msg, invoke_type, data);
-
- if (msg->status == SOUP_STATUS_QUEUED ||
- msg->status == SOUP_STATUS_CONNECTING) return TRUE;
- }
-
- /*
- * Issue final callback if the invoke_type is POST_BODY and the error
- * class is not INFORMATIONAL.
- */
- if (invoke_type == SOUP_HANDLER_POST_BODY &&
- msg->errorclass != SOUP_ERROR_CLASS_INFORMATIONAL) {
- soup_message_issue_callback (msg);
- return TRUE;
- }
-
- return FALSE;
-}
-
-static void
-add_handler (SoupMessage *msg,
- SoupHandlerType type,
- SoupCallbackFn handler_cb,
- gpointer user_data,
- SoupHandlerKind kind,
- const char *header,
- guint errorcode,
- guint errorclass)
-{
- SoupHandlerData *data;
-
- data = g_new0 (SoupHandlerData, 1);
- data->type = type;
- data->handler_cb = handler_cb;
- data->user_data = user_data;
- data->kind = kind;
-
- switch (kind) {
- case RESPONSE_HEADER_HANDLER:
- data->data.header = header;
- break;
- case RESPONSE_ERROR_CODE_HANDLER:
- data->data.errorcode = errorcode;
- break;
- case RESPONSE_ERROR_CLASS_HANDLER:
- data->data.errorclass = errorclass;
- break;
- default:
- break;
- }
-
- msg->priv->content_handlers =
- g_slist_append (msg->priv->content_handlers, data);
-}
-
-void
-soup_message_add_header_handler (SoupMessage *msg,
- const char *header,
- SoupHandlerType type,
- SoupCallbackFn handler_cb,
- gpointer user_data)
-{
- g_return_if_fail (SOUP_IS_MESSAGE (msg));
- g_return_if_fail (header != NULL);
- g_return_if_fail (handler_cb != NULL);
-
- add_handler (msg, type, handler_cb, user_data,
- RESPONSE_HEADER_HANDLER, header, 0, 0);
-}
-
-void
-soup_message_add_error_code_handler (SoupMessage *msg,
- guint errorcode,
- SoupHandlerType type,
- SoupCallbackFn handler_cb,
- gpointer user_data)
-{
- g_return_if_fail (SOUP_IS_MESSAGE (msg));
- g_return_if_fail (errorcode != 0);
- g_return_if_fail (handler_cb != NULL);
-
- add_handler (msg, type, handler_cb, user_data,
- RESPONSE_ERROR_CODE_HANDLER, NULL, errorcode, 0);
-}
-
-void
-soup_message_add_error_class_handler (SoupMessage *msg,
- SoupErrorClass errorclass,
- SoupHandlerType type,
- SoupCallbackFn handler_cb,
- gpointer user_data)
-{
- g_return_if_fail (SOUP_IS_MESSAGE (msg));
- g_return_if_fail (errorclass != 0);
- g_return_if_fail (handler_cb != NULL);
-
- add_handler (msg, type, handler_cb, user_data,
- RESPONSE_ERROR_CLASS_HANDLER, NULL, 0, errorclass);
-}
-
-void
-soup_message_add_handler (SoupMessage *msg,
- SoupHandlerType type,
- SoupCallbackFn handler_cb,
- gpointer user_data)
-{
- g_return_if_fail (SOUP_IS_MESSAGE (msg));
- g_return_if_fail (handler_cb != NULL);
-
- add_handler (msg, type, handler_cb, user_data, 0, NULL, 0, 0);
-}
-
-void
-soup_message_remove_handler (SoupMessage *msg,
- SoupHandlerType type,
- SoupCallbackFn handler_cb,
- gpointer user_data)
-{
- GSList *iter = msg->priv->content_handlers;
-
- while (iter) {
- SoupHandlerData *data = iter->data;
-
- if (data->handler_cb == handler_cb &&
- data->user_data == user_data &&
- data->type == type) {
- msg->priv->content_handlers =
- g_slist_remove_link (
- msg->priv->content_handlers,
- iter);
- g_free (data);
- break;
- }
-
- iter = iter->next;
- }
-}
-
void
soup_message_set_flags (SoupMessage *msg, guint flags)
{
typedef struct SoupMessagePrivate SoupMessagePrivate;
typedef enum {
- SOUP_STATUS_IDLE = 0,
- SOUP_STATUS_QUEUED,
- SOUP_STATUS_CONNECTING,
- SOUP_STATUS_SENDING_REQUEST,
- SOUP_STATUS_READING_RESPONSE,
- SOUP_STATUS_FINISHED
-} SoupTransferStatus;
+ SOUP_TRANSFER_UNKNOWN = 0,
+ SOUP_TRANSFER_CHUNKED,
+ SOUP_TRANSFER_CONTENT_LENGTH,
+} SoupTransferEncoding;
typedef enum {
SOUP_BUFFER_SYSTEM_OWNED = 0,
const char *method;
- SoupTransferStatus status;
-
guint errorcode;
SoupErrorClass errorclass;
const char *errorphrase;
SOUP_HANDLER_PRE_BODY = 1,
SOUP_HANDLER_BODY_CHUNK,
SOUP_HANDLER_POST_BODY
-} SoupHandlerType;
+} SoupHandlerPhase;
void soup_message_add_handler (SoupMessage *msg,
- SoupHandlerType type,
+ SoupHandlerPhase type,
SoupCallbackFn handler_cb,
gpointer user_data);
void soup_message_add_header_handler (SoupMessage *msg,
const char *header,
- SoupHandlerType type,
+ SoupHandlerPhase type,
SoupCallbackFn handler_cb,
gpointer user_data);
void soup_message_add_error_code_handler (
SoupMessage *msg,
guint errorcode,
- SoupHandlerType type,
+ SoupHandlerPhase type,
SoupCallbackFn handler_cb,
gpointer user_data);
void soup_message_add_error_class_handler (
SoupMessage *msg,
SoupErrorClass errorclass,
- SoupHandlerType type,
+ SoupHandlerPhase type,
SoupCallbackFn handler_cb,
gpointer user_data);
void soup_message_remove_handler (SoupMessage *msg,
- SoupHandlerType type,
+ SoupHandlerPhase type,
SoupCallbackFn handler_cb,
gpointer user_data);
#define soup_sockaddr_max sockaddr_in
#endif
-struct SoupMessagePrivate {
- SoupConnectId connect_tag;
- gpointer read_tag;
- gpointer write_tag;
- guint timeout_tag;
-
- guint retries;
-
- SoupCallbackFn callback;
- gpointer user_data;
-
- guint msg_flags;
-
- GSList *content_handlers;
-
- SoupHttpVersion http_version;
-
- SoupConnection *connection;
- SoupSocket *socket;
-};
-
/* from soup-context.c */
SoupAuth *soup_context_lookup_auth (SoupContext *ctx,
void soup_context_invalidate_auth (SoupContext *ctx,
SoupAuth *auth);
-/* from soup-message.c */
-
-void soup_message_issue_callback (SoupMessage *req);
-
-gboolean soup_message_run_handlers (SoupMessage *msg,
- SoupHandlerType invoke_type);
-
-void soup_message_cleanup (SoupMessage *req);
-
/* from soup-misc.c */
guint soup_str_case_hash (gconstpointer key);
#include "soup-queue.h"
#include "soup-auth.h"
#include "soup-message.h"
+#include "soup-message-private.h"
#include "soup-context.h"
#include "soup-headers.h"
#include "soup-misc.h"
#include "soup-private.h"
#include "soup-ssl.h"
-#include "soup-transfer.h"
static GSList *soup_active_requests = NULL, *soup_active_request_next = NULL;
}
static void
-soup_queue_error_cb (gboolean body_started, gpointer user_data)
+soup_queue_error_cb (SoupMessage *req)
{
- SoupMessage *req = user_data;
SoupConnection *conn = soup_message_get_connection (req);
+ const SoupUri *uri;
gboolean conn_is_new;
conn_is_new = soup_connection_is_new (conn);
soup_message_disconnect (req);
- switch (req->status) {
- case SOUP_STATUS_IDLE:
- case SOUP_STATUS_QUEUED:
- case SOUP_STATUS_FINISHED:
+ switch (req->priv->status) {
+ case SOUP_MESSAGE_STATUS_IDLE:
+ case SOUP_MESSAGE_STATUS_QUEUED:
+ case SOUP_MESSAGE_STATUS_FINISHED:
break;
- case SOUP_STATUS_CONNECTING:
+ case SOUP_MESSAGE_STATUS_CONNECTING:
soup_message_set_error (req, SOUP_ERROR_CANT_CONNECT);
soup_message_issue_callback (req);
break;
- case SOUP_STATUS_READING_RESPONSE:
- case SOUP_STATUS_SENDING_REQUEST:
- if (!body_started) {
- const SoupUri *uri = soup_context_get_uri (req->context);
-
- if (uri->protocol == SOUP_PROTOCOL_HTTPS) {
- /*
- * This can happen if the SSL handshake fails
- * for some reason (untrustable signatures,
- * etc.)
- */
- if (req->priv->retries >= 3) {
- soup_message_set_error (
- req,
- SOUP_ERROR_SSL_FAILED);
- soup_message_issue_callback (req);
- } else {
- req->priv->retries++;
- soup_message_requeue (req);
- }
- } else if (conn_is_new) {
- soup_message_set_error (
- req,
- SOUP_ERROR_CANT_CONNECT);
+ case SOUP_MESSAGE_STATUS_WRITING_HEADERS:
+ case SOUP_MESSAGE_STATUS_READING_HEADERS:
+ uri = soup_context_get_uri (req->context);
+
+ if (uri->protocol == SOUP_PROTOCOL_HTTPS) {
+ /* FIXME: what does this really do? */
+
+ /*
+ * This can happen if the SSL handshake fails
+ * for some reason (untrustable signatures,
+ * etc.)
+ */
+ if (req->priv->retries >= 3) {
+ soup_message_set_error (req, SOUP_ERROR_SSL_FAILED);
soup_message_issue_callback (req);
} else {
- /* Must have timed out. Try a new connection */
+ req->priv->retries++;
soup_message_requeue (req);
}
- } else {
- soup_message_set_error (req, SOUP_ERROR_IO);
+ } else if (conn_is_new) {
+ soup_message_set_error (req, SOUP_ERROR_CANT_CONNECT);
soup_message_issue_callback (req);
+ } else {
+ /* Must have timed out. Try a new connection */
+ soup_message_requeue (req);
}
break;
}
static void
-soup_queue_read_headers_cb (char *headers,
+soup_queue_read_headers_cb (SoupMessage *req,
+ char *headers,
guint headers_len,
SoupTransferEncoding *encoding,
- int *content_len,
- gpointer user_data)
+ int *content_len)
{
- SoupMessage *req = user_data;
const char *length, *enc;
SoupHttpVersion version;
GHashTable *resp_hdrs;
}
static void
-soup_queue_read_chunk_cb (const char *chunk,
- guint len,
- gpointer user_data)
+soup_queue_read_chunk_cb (SoupMessage *req,
+ const char *chunk,
+ guint len)
{
- SoupMessage *req = user_data;
-
req->response.owner = SOUP_BUFFER_STATIC;
req->response.length = len;
req->response.body = (char *)chunk;
}
static void
-soup_queue_read_done_cb (char *body,
- guint len,
- gpointer user_data)
+soup_queue_read_done_cb (SoupMessage *req, char *body, guint len)
{
- SoupMessage *req = user_data;
-
if (soup_message_is_keepalive (req))
soup_connection_mark_old (soup_message_get_connection (req));
else
req->response.length = len;
req->response.body = body;
- soup_transfer_read_unref (req->priv->read_tag);
-
if (req->errorclass == SOUP_ERROR_CLASS_INFORMATIONAL) {
- SoupSocket *sock;
- gboolean overwrt;
-
- sock = soup_message_get_socket (req);
- overwrt = req->priv->msg_flags & SOUP_MESSAGE_OVERWRITE_CHUNKS;
-
- req->priv->read_tag =
- soup_transfer_read (sock,
- overwrt,
- soup_queue_read_headers_cb,
- soup_queue_read_chunk_cb,
- soup_queue_read_done_cb,
- soup_queue_error_cb,
- req);
- }
- else {
- req->status = SOUP_STATUS_FINISHED;
- req->priv->read_tag = NULL;
- }
+ soup_message_read (req,
+ soup_queue_read_headers_cb,
+ soup_queue_read_chunk_cb,
+ soup_queue_read_done_cb,
+ soup_queue_error_cb);
+ } else
+ req->priv->status = SOUP_MESSAGE_STATUS_FINISHED;
soup_message_run_handlers (req, SOUP_HANDLER_POST_BODY);
}
}
}
-static GString *
-soup_get_request_header (SoupMessage *req)
+static void
+soup_queue_get_request_header_cb (SoupMessage *req, GString *header)
{
- GString *header;
- gchar *uri;
+ char *uri;
SoupContext *proxy;
const SoupUri *suri;
struct SoupUsedHeaders hdrs = {
NULL
};
- header = hdrs.out = g_string_new (NULL);
+ hdrs.out = header;
proxy = soup_get_proxy ();
suri = soup_context_get_uri (req->context);
soup_encode_http_auth (req, header, FALSE);
g_string_append (header, "\r\n");
-
- return header;
}
static void
-soup_queue_write_done_cb (gpointer user_data)
+soup_queue_write_done_cb (SoupMessage *req)
{
- SoupMessage *req = user_data;
-
- soup_transfer_write_unref (req->priv->write_tag);
- req->priv->write_tag = NULL;
- req->status = SOUP_STATUS_READING_RESPONSE;
+ soup_message_read (req,
+ soup_queue_read_headers_cb,
+ soup_queue_read_chunk_cb,
+ soup_queue_read_done_cb,
+ soup_queue_error_cb);
}
static void
start_request (SoupContext *ctx, SoupMessage *req)
{
SoupSocket *sock;
- gboolean overwrt;
sock = soup_message_get_socket (req);
if (!sock) { /* FIXME */
return;
}
- req->priv->write_tag =
- soup_transfer_write_simple (sock,
- soup_get_request_header (req),
- &req->request,
- soup_queue_write_done_cb,
- soup_queue_error_cb,
- req);
-
- overwrt = req->priv->msg_flags & SOUP_MESSAGE_OVERWRITE_CHUNKS;
-
- req->priv->read_tag =
- soup_transfer_read (sock,
- overwrt,
- soup_queue_read_headers_cb,
- soup_queue_read_chunk_cb,
- soup_queue_read_done_cb,
- soup_queue_error_cb,
- req);
-
- req->status = SOUP_STATUS_SENDING_REQUEST;
+ soup_message_write_simple (req, &req->request,
+ soup_queue_get_request_header_cb,
+ soup_queue_write_done_cb,
+ soup_queue_error_cb);
}
static void
for (; req; req = soup_queue_next_request ()) {
SoupContext *ctx, *proxy;
- if (req->status != SOUP_STATUS_QUEUED)
+ if (req->priv->status != SOUP_MESSAGE_STATUS_QUEUED)
continue;
proxy = soup_get_proxy ();
ctx = proxy ? proxy : req->context;
- req->status = SOUP_STATUS_CONNECTING;
+ req->priv->status = SOUP_MESSAGE_STATUS_CONNECTING;
conn = soup_message_get_connection (req);
if (conn && soup_connection_is_connected (conn))
return;
}
- if (req->status != SOUP_STATUS_IDLE)
+ if (req->priv->status != SOUP_MESSAGE_STATUS_IDLE)
soup_message_cleanup (req);
switch (req->response.owner) {
req->errorphrase = NULL;
}
- req->status = SOUP_STATUS_QUEUED;
+ req->priv->status = SOUP_MESSAGE_STATUS_QUEUED;
soup_queue_add_request (req);
#include <unistd.h>
#include "soup-server-message.h"
+#include "soup-message-private.h"
#include "soup-private.h"
-#include "soup-transfer.h"
struct SoupServerMessagePrivate {
SoupServer *server;
smsg->priv->started = TRUE;
- soup_transfer_write_unpause (SOUP_MESSAGE (smsg)->priv->write_tag);
+ soup_message_write_unpause (SOUP_MESSAGE (smsg));
}
gboolean
smsg->priv->chunks = g_slist_append (smsg->priv->chunks, buf);
- soup_transfer_write_unpause (SOUP_MESSAGE (smsg)->priv->write_tag);
+ soup_message_write_unpause (SOUP_MESSAGE (smsg));
}
SoupDataBuffer *
smsg->priv->started = TRUE;
smsg->priv->finished = TRUE;
- soup_transfer_write_unpause (SOUP_MESSAGE (smsg)->priv->write_tag);
+ soup_message_write_unpause (SOUP_MESSAGE (smsg));
}
gboolean
#include <libsoup/soup-message.h>
#include <libsoup/soup-server.h>
-#include <libsoup/soup-transfer.h>
#define SOUP_TYPE_SERVER_MESSAGE (soup_server_message_get_type ())
#define SOUP_SERVER_MESSAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_SERVER_MESSAGE, SoupServerMessage))
#include "soup-server.h"
#include "soup-headers.h"
#include "soup-private.h"
+#include "soup-message-private.h"
#include "soup-ssl.h"
-#include "soup-transfer.h"
#define PARENT_TYPE G_TYPE_OBJECT
static GObjectClass *parent_class;
static void start_request (SoupServer *, SoupSocket *);
static void
-error_cb (gboolean body_started, gpointer msg)
+error_cb (SoupMessage *msg)
{
g_object_unref (msg);
}
static void
-write_done_cb (gpointer user_data)
+write_done_cb (SoupMessage *msg)
{
- SoupServerMessage *smsg = user_data;
- SoupMessage *msg = user_data;
+ SoupServerMessage *smsg = SOUP_SERVER_MESSAGE (msg);
- soup_transfer_write_unref (msg->priv->write_tag);
- msg->priv->write_tag = 0;
-
- start_request (soup_server_message_get_server (smsg),
- soup_message_get_socket (msg));
+ if (soup_message_is_keepalive (msg)) {
+ /* Start a new request */
+ start_request (soup_server_message_get_server (smsg),
+ soup_message_get_socket (msg));
+ }
g_object_unref (msg);
}
static void
-write_header (char *key, char *value, GString *ret)
+write_header (gpointer name, gpointer value, gpointer headers)
{
- g_string_sprintfa (ret, "%s: %s\r\n", key, value);
+ g_string_append_printf (headers, "%s: %s\r\n",
+ (char *)name, (char *)value);
}
-static GString *
-get_response_header (SoupMessage *req,
- SoupTransferEncoding encoding)
+static void
+get_response_header_cb (SoupMessage *msg,
+ GString *headers)
{
- GString *ret = g_string_new (NULL);
+ SoupServerMessage *smsg = SOUP_SERVER_MESSAGE (msg);
+ SoupTransferEncoding encoding =
+ soup_server_message_get_encoding (smsg);
- g_string_sprintfa (ret, "HTTP/1.1 %d %s\r\n",
- req->errorcode, req->errorphrase);
+ if (!soup_server_message_is_started (smsg)) {
+ soup_message_write_pause (msg);
+ return;
+ }
- if (encoding == SOUP_TRANSFER_CONTENT_LENGTH)
- g_string_sprintfa (ret,
- "Content-Length: %d\r\n",
- req->response.length);
- else if (encoding == SOUP_TRANSFER_CHUNKED)
- g_string_append (ret, "Transfer-Encoding: chunked\r\n");
+ g_string_append_printf (headers, "HTTP/1.1 %d %s\r\n",
+ msg->errorcode, msg->errorphrase);
- soup_message_foreach_header (req->response_headers,
- (GHFunc) write_header,
- ret);
+ if (encoding == SOUP_TRANSFER_CONTENT_LENGTH) {
+ g_string_append_printf (headers, "Content-Length: %d\r\n",
+ msg->response.length);
+ } else if (encoding == SOUP_TRANSFER_CHUNKED)
+ g_string_append (headers, "Transfer-Encoding: chunked\r\n");
- g_string_append (ret, "\r\n");
+ soup_message_foreach_header (msg->response_headers,
+ write_header, headers);
- return ret;
+ g_string_append (headers, "\r\n");
}
static inline void
static void
issue_bad_request (SoupMessage *msg)
{
- GString *header;
+ soup_message_read_cancel (msg);
set_response_error (msg, SOUP_ERROR_BAD_REQUEST, NULL, NULL);
soup_message_add_header (msg->response_headers,
"Connection", "close");
+ soup_server_message_finish (SOUP_SERVER_MESSAGE (msg));
- if (msg->priv->read_tag) {
- soup_transfer_read_cancel (msg->priv->read_tag);
- msg->priv->read_tag = 0;
- }
-
- header = get_response_header (msg, SOUP_TRANSFER_CONTENT_LENGTH);
-
- msg->priv->write_tag =
- soup_transfer_write_simple (soup_message_get_socket (msg),
- header,
- &msg->response,
- write_done_cb,
- error_cb,
- msg);
+ soup_message_write_simple (msg, &msg->response,
+ get_response_header_cb,
+ write_done_cb, error_cb);
}
static void
-read_headers_cb (char *headers,
+read_headers_cb (SoupMessage *msg,
+ char *headers,
guint headers_len,
SoupTransferEncoding *encoding,
- int *content_len,
- gpointer user_data)
+ int *content_len)
{
- SoupMessage *msg = user_data;
SoupContext *ctx;
char *req_path = NULL, *url;
const char *length, *enc, *req_host;
server = soup_server_message_get_server (SOUP_SERVER_MESSAGE (req));
handler_path = soup_context_get_uri (req->context)->path;
- req->status = SOUP_STATUS_FINISHED;
hand = soup_server_get_handler (server, handler_path);
if (!hand) {
set_response_error (req, SOUP_ERROR_NOT_FOUND, NULL, NULL);
soup_server_auth_free (auth);
}
-static void
-get_header_cb (GString **out_hdr,
- gpointer user_data)
+static gboolean
+get_chunk_cb (SoupMessage *msg, SoupDataBuffer *out_next)
{
- SoupMessage *msg = user_data;
- SoupServerMessage *smsg = user_data;
- SoupTransferEncoding encoding;
-
- if (soup_server_message_is_started (smsg)) {
- if (msg->priv->http_version == SOUP_HTTP_1_0)
- encoding = SOUP_TRANSFER_UNKNOWN;
- else
- encoding = SOUP_TRANSFER_CHUNKED;
-
- *out_hdr = get_response_header (msg, encoding);
- } else
- soup_transfer_write_pause (msg->priv->write_tag);
-}
-
-static SoupTransferDone
-get_chunk_cb (SoupDataBuffer *out_next, gpointer user_data)
-{
- SoupMessage *msg = user_data;
- SoupServerMessage *smsg = user_data;
+ SoupServerMessage *smsg = SOUP_SERVER_MESSAGE (msg);
SoupDataBuffer *next;
next = soup_server_message_get_chunk (smsg);
out_next->length = next->length;
g_free (next);
- return SOUP_TRANSFER_CONTINUE;
- } else if (soup_server_message_is_finished (smsg)) {
- return SOUP_TRANSFER_END;
- } else {
- soup_transfer_write_pause (msg->priv->write_tag);
- return SOUP_TRANSFER_CONTINUE;
+ return TRUE;
}
+
+ if (!soup_server_message_is_finished (smsg))
+ soup_message_write_pause (msg);
+ return FALSE;
}
static void
-read_done_cb (char *body,
- guint len,
- gpointer user_data)
+read_done_cb (SoupMessage *req,
+ char *body,
+ guint len)
{
- SoupMessage *req = user_data;
- SoupServerMessage *smsg = user_data;
- SoupSocket *server_sock = soup_message_get_socket (req);
+ SoupServerMessage *smsg = SOUP_SERVER_MESSAGE (req);
SoupTransferEncoding encoding;
- soup_transfer_read_unref (req->priv->read_tag);
- req->priv->read_tag = 0;
-
req->request.owner = SOUP_BUFFER_SYSTEM_OWNED;
req->request.body = body;
req->request.length = len;
encoding = soup_server_message_get_encoding (smsg);
if (encoding == SOUP_TRANSFER_CONTENT_LENGTH) {
- GString *header;
- header = get_response_header (req, encoding);
- req->priv->write_tag =
- soup_transfer_write_simple (server_sock,
- header,
- &req->response,
- write_done_cb,
- error_cb,
- req);
+ soup_message_write_simple (req, &req->response,
+ get_response_header_cb,
+ write_done_cb, error_cb);
+ soup_server_message_start (smsg);
} else {
- req->priv->write_tag =
- soup_transfer_write (server_sock,
- encoding,
- get_header_cb,
- get_chunk_cb,
- write_done_cb,
- error_cb,
- req);
-
- /* Pause write until soup_server_message_start() */
- if (!soup_server_message_is_started (smsg))
- soup_transfer_write_pause (req->priv->write_tag);
+ soup_message_write (req, encoding,
+ get_response_header_cb, get_chunk_cb,
+ write_done_cb, error_cb);
}
return;
/* Listen for another request on this connection */
msg = (SoupMessage *)soup_server_message_new (server, server_sock);
- msg->priv->read_tag =
- soup_transfer_read (server_sock,
- FALSE,
- read_headers_cb,
- NULL,
- read_done_cb,
- error_cb,
- msg);
+ soup_message_read (msg, read_headers_cb, NULL,
+ read_done_cb, error_cb);
}
static void
+++ /dev/null
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * soup-queue.c: Asyncronous Callback-based HTTP Request Queue.
- *
- * Authors:
- * Alex Graveley (alex@ximian.com)
- *
- * Copyright (C) 2000-2002, Ximian, 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 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;
-
- SoupSocket *sock;
- guint idle_tag;
- guint read_tag;
- guint err_tag;
-
- SoupReaderState state;
- GByteArray *body_buf;
- GByteArray *meta_buf;
-
- SoupTransferEncoding encoding;
- guint read_length;
-
- SoupReadHeadersDoneFn headers_done_cb;
- SoupReadChunkFn read_chunk_cb;
- SoupReadDoneFn read_done_cb;
- SoupReadErrorFn error_cb;
- gpointer user_data;
-};
-
-/* Stops reading and releases soup-transfer's ref. */
-static void
-soup_transfer_read_stop (SoupReader *r)
-{
- if (!r->err_tag)
- return;
-
- g_signal_handler_disconnect (r->sock, r->read_tag);
- r->read_tag = 0;
- 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);
-}
-
-void
-soup_transfer_read_ref (SoupReader *r)
-{
- r->ref_count++;
-}
-
-gboolean
-soup_transfer_read_unref (SoupReader *r)
-{
- r->ref_count--;
- if (r->ref_count)
- return TRUE;
-
- soup_transfer_read_stop (r);
- 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;
-}
-
-void
-soup_transfer_read_cancel (SoupReader *r)
-{
- soup_transfer_read_stop (r);
- soup_transfer_read_unref (r);
-}
-
-void
-soup_transfer_read_set_callbacks (SoupReader *r,
- SoupReadHeadersDoneFn headers_done_cb,
- SoupReadChunkFn read_chunk_cb,
- SoupReadDoneFn read_done_cb,
- SoupReadErrorFn error_cb,
- gpointer user_data)
-{
- g_assert (read_done_cb && error_cb);
-
- r->headers_done_cb = headers_done_cb;
- r->read_chunk_cb = read_chunk_cb;
- r->read_done_cb = read_done_cb;
- r->error_cb = error_cb;
-
- r->user_data = user_data;
-}
-
-static void
-issue_final_callback (SoupReader *r)
-{
- char *body;
- guint len;
-
- 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) (body, len, r->user_data);
- soup_transfer_read_unref (r);
-}
-
-static void
-reader_disconnected (SoupSocket *sock, SoupReader *r)
-{
- 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->state == SOUP_READER_STATE_READ_TO_EOF)
- issue_final_callback (r);
- else {
- (*r->error_cb) (r->state > SOUP_READER_STATE_HEADERS,
- r->user_data);
- }
-
- soup_transfer_read_unref (r);
-}
-
-static gboolean
-soup_reader_read_metadata (SoupReader *r, const char *boundary, int boundary_len)
-{
- SoupSocketIOStatus status;
- char read_buf[RESPONSE_BLOCK_SIZE];
- guint nread;
- gboolean done;
-
- 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;
-
- case SOUP_SOCKET_ERROR:
- case SOUP_SOCKET_EOF:
- reader_disconnected (r->sock, r);
- return FALSE;
-
- case SOUP_SOCKET_WOULD_BLOCK:
- return FALSE;
- }
- } while (!done);
-
- return TRUE;
-}
-
-static gboolean
-soup_reader_read_body_chunk (SoupReader *r, guint *size)
-{
- SoupSocketIOStatus status;
- char read_buf[RESPONSE_BLOCK_SIZE];
- guint nread, len = sizeof (read_buf);
-
- while (!size || *size > 0) {
- if (size)
- len = MIN (len, *size);
-
- status = soup_socket_read (r->sock, read_buf, len, &nread);
-
- switch (status) {
- case SOUP_SOCKET_OK:
- if (!nread)
- break;
-
- if (r->read_chunk_cb) {
- soup_transfer_read_ref (r);
- r->read_chunk_cb (read_buf, nread,
- r->user_data);
- if (!soup_transfer_read_unref (r))
- return FALSE;
- }
- if (r->body_buf)
- g_byte_array_append (r->body_buf, read_buf, nread);
- if (size)
- *size -= nread;
- break;
-
- case SOUP_SOCKET_EOF:
- if (!size)
- return TRUE;
- /* else fall through */
-
- case SOUP_SOCKET_ERROR:
- reader_disconnected (r->sock, r);
- return FALSE;
-
- case SOUP_SOCKET_WOULD_BLOCK:
- return FALSE;
- }
- }
-
- return TRUE;
-}
-
-#define SOUP_TRANSFER_EOL "\r\n"
-#define SOUP_TRANSFER_EOL_LEN 2
-
-#define SOUP_TRANSFER_DOUBLE_EOL "\r\n\r\n"
-#define SOUP_TRANSFER_DOUBLE_EOL_LEN 4
-
-static void
-reader_read (SoupSocket *sock, SoupReader *r)
-{
- 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))
- return;
-
- r->meta_buf->len -= SOUP_TRANSFER_DOUBLE_EOL_LEN;
- if (r->headers_done_cb) {
- soup_transfer_read_ref (r);
- (*r->headers_done_cb) (r->meta_buf->data,
- r->meta_buf->len,
- &r->encoding,
- &r->read_length,
- r->user_data);
- if (!soup_transfer_read_unref (r))
- return;
- }
- 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;
-
- case SOUP_READER_STATE_READ_TO_EOF:
- if (!soup_reader_read_body_chunk (r, NULL))
- return;
-
- goto done;
- break;
-
- case SOUP_READER_STATE_CONTENT_LENGTH:
- if (!soup_reader_read_body_chunk (r, &r->read_length))
- return;
-
- goto done;
- break;
-
- case SOUP_READER_STATE_CHUNK_SIZE:
- if (!soup_reader_read_metadata (r, SOUP_TRANSFER_EOL,
- SOUP_TRANSFER_EOL_LEN))
- return;
-
- r->read_length = strtoul (r->meta_buf->data, NULL, 16);
- g_byte_array_set_size (r->meta_buf, 0);
-
- if (r->read_length > 0)
- r->state = SOUP_READER_STATE_CHUNK;
- else
- r->state = SOUP_READER_STATE_TRAILERS;
- break;
-
- case SOUP_READER_STATE_CHUNK:
- if (!soup_reader_read_body_chunk (r, &r->read_length))
- return;
-
- r->state = SOUP_READER_STATE_BETWEEN_CHUNKS;
- break;
-
- case SOUP_READER_STATE_BETWEEN_CHUNKS:
- if (!soup_reader_read_metadata (r, SOUP_TRANSFER_EOL,
- SOUP_TRANSFER_EOL_LEN))
- return;
-
- g_byte_array_set_size (r->meta_buf, 0);
- r->state = SOUP_READER_STATE_CHUNK_SIZE;
- break;
-
- case SOUP_READER_STATE_TRAILERS:
- if (!soup_reader_read_metadata (r, SOUP_TRANSFER_EOL,
- SOUP_TRANSFER_EOL_LEN))
- return;
-
- if (r->meta_buf->len == SOUP_TRANSFER_EOL_LEN)
- goto done;
-
- /* FIXME: process trailers */
- g_byte_array_set_size (r->meta_buf, 0);
- break;
-
- }
- }
-
- done:
- issue_final_callback (r);
-}
-
-static gboolean
-idle_read (gpointer user_data)
-{
- SoupReader *r = user_data;
-
- r->idle_tag = 0;
- reader_read (r->sock, r);
- return FALSE;
-}
-
-/**
- * soup_transfer_read:
- * @chan: the iochannel to read from
- * @overwrite_chunks: if %TRUE, body chunks will not be preserved after
- * chunk callbacks.
- * @headers_done_cb: (optional) callback to call after headers have
- * been read.
- * @read_chunk_cb: (optional) callback to call as body data is being read
- * @read_done_cb: (mandatory) callback to call when the body has been
- * completely read
- * @error_cb: (mandatory) callback to call when an error occurs
- * @user_data: data to pass to the callbacks.
- *
- * Attempts to read a single HTTP message from @chan.
- *
- * Unless the caller calls soup_transfer_read_cancel(), either
- * @read_done_cb or @read_error_cb will eventually be called.
- *
- * Return value: a #SoupReader, which must eventually be freed by
- * calling either soup_transfer_read_unref() or
- * soup_transfer_read_cancel().
- **/
-SoupReader *
-soup_transfer_read (SoupSocket *sock,
- gboolean overwrite_chunks,
- SoupReadHeadersDoneFn headers_done_cb,
- SoupReadChunkFn read_chunk_cb,
- SoupReadDoneFn read_done_cb,
- SoupReadErrorFn error_cb,
- gpointer user_data)
-{
- SoupReader *reader;
-
- g_assert (read_done_cb && error_cb);
-
- reader = g_new0 (SoupReader, 1);
- 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->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_signal_connect (sock, "readable",
- G_CALLBACK (reader_read), reader);
-
- reader->err_tag =
- 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.
- */
- reader->ref_count = 2;
-
- 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_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;
- }
-
- /* Give up soup-transfer's ref */
- soup_transfer_write_unref (w);
-}
-
-void
-soup_transfer_write_ref (SoupWriter *w)
-{
- w->ref_count++;
-}
-
-gboolean
-soup_transfer_write_unref (SoupWriter *w)
-{
- w->ref_count--;
- if (w->ref_count)
- return TRUE;
-
- soup_transfer_write_stop (w);
- g_byte_array_free (w->write_buf, TRUE);
- g_object_unref (w->sock);
- g_free (w);
- return FALSE;
-}
-
-void
-soup_transfer_write_cancel (SoupWriter *w)
-{
- soup_transfer_write_stop (w);
- soup_transfer_write_unref (w);
-}
-
-static void
-writer_disconnected (SoupSocket *sock, SoupWriter *w)
-{
- soup_transfer_write_stop (w);
- (*w->error_cb) (w->headers_done, w->user_data);
-}
-
-static gboolean
-get_header (SoupWriter *w)
-{
- GString *header = NULL;
-
- (*w->get_header_cb) (&header, w->user_data);
-
- if (!header)
- return FALSE;
-
- g_byte_array_append (w->write_buf, header->str, header->len);
- g_string_free (header, TRUE);
-
- w->get_header_cb = NULL;
- return TRUE;
-}
-
-static void
-write_chunk_sep (GByteArray *arr, gint len, gint chunk_cnt)
-{
- gchar *hex;
- gchar *end = "0\r\n\r\n";
-
- /*
- * Only prefix the chunk length with a \r\n if its not the first chunk
- */
- if (chunk_cnt)
- g_byte_array_append (arr, "\r\n", 2);
-
- if (len) {
- hex = g_strdup_printf ("%x\r\n", len);
- g_byte_array_append (arr, hex, strlen (hex));
- g_free (hex);
- } else
- g_byte_array_append (arr, end, strlen (end));
-}
-
-static void
-get_next_chunk (SoupWriter *w)
-{
- SoupTransferStatus ret = SOUP_TRANSFER_END;
- SoupDataBuffer buf = { 0 , NULL, 0 };
-
- ret = (*w->get_chunk_cb) (&buf, w->user_data);
-
- if (buf.length) {
- if (w->encoding == SOUP_TRANSFER_CHUNKED)
- write_chunk_sep (w->write_buf,
- buf.length,
- w->chunk_cnt++);
-
- g_byte_array_append (w->write_buf, buf.body, buf.length);
-
- if (buf.owner == SOUP_BUFFER_SYSTEM_OWNED)
- g_free (buf.body);
- }
-
- if (ret == SOUP_TRANSFER_END) {
- if (w->encoding == SOUP_TRANSFER_CHUNKED)
- write_chunk_sep (w->write_buf, 0, w->chunk_cnt);
-
- w->get_chunk_cb = NULL;
- }
-}
-
-static void
-writer_write (SoupSocket *sock, SoupWriter *w)
-{
- SoupSocketIOStatus status;
- guint bytes_written = 0;
-
- /* Get the header and first data chunk (if available). */
- if (w->get_header_cb) {
- soup_transfer_write_ref (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;
- }
-
- WRITE_AGAIN:
- while (w->write_buf->len) {
- 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;
- }
- }
-
- /* 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. */
- if (w->get_chunk_cb) {
- soup_transfer_write_ref (w);
- get_next_chunk (w);
- if (!soup_transfer_write_unref (w))
- return;
-
- if (!w->write_tag)
- return;
-
- goto WRITE_AGAIN;
- }
-
- soup_transfer_write_ref (w);
- 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 (SoupSocket *sock,
- SoupTransferEncoding encoding,
- SoupWriteDoneFn write_done_cb,
- SoupWriteErrorFn error_cb,
- gpointer user_data)
-{
- SoupWriter *writer;
-
- g_assert (write_done_cb && error_cb);
-
- writer = g_new0 (SoupWriter, 1);
- writer->sock = g_object_ref (sock);
- writer->encoding = encoding;
- writer->write_buf = g_byte_array_new ();
- writer->write_done_cb = write_done_cb;
- writer->error_cb = error_cb;
- writer->user_data = user_data;
-
- writer->write_tag =
- g_signal_connect (sock, "writable",
- G_CALLBACK (writer_write), writer);
-
- writer->err_tag =
- 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.
- */
- writer->ref_count = 2;
-
- return writer;
-}
-
-/**
- * soup_transfer_write_simple:
- * @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
- * completely written
- * @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 @sock using identity
- * encoding and Content-Length.
- *
- * Unless the caller calls soup_transfer_write_cancel(), either
- * @write_done_cb or @write_error_cb will eventually be called.
- *
- * Return value: a #SoupWriter, which must eventually be freed by
- * calling either soup_transfer_write_unref() or
- * soup_transfer_write_cancel().
- **/
-SoupWriter *
-soup_transfer_write_simple (SoupSocket *sock,
- GString *header,
- const SoupDataBuffer *src,
- SoupWriteDoneFn write_done_cb,
- SoupWriteErrorFn error_cb,
- gpointer user_data)
-{
- SoupWriter *writer;
-
- writer = create_writer (sock,
- SOUP_TRANSFER_CONTENT_LENGTH,
- write_done_cb,
- error_cb,
- user_data);
-
- if (header) {
- g_byte_array_append (writer->write_buf,
- header->str,
- header->len);
- g_string_free (header, TRUE);
- }
-
- if (src && src->length)
- g_byte_array_append (writer->write_buf,
- src->body,
- src->length);
-
- return writer;
-}
-
-/**
- * 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
- * @write_done_cb: (mandatory) callback to call when the body has been
- * completely written
- * @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 @sock using @encoding.
- *
- * Unless the caller calls soup_transfer_write_cancel(), either
- * @write_done_cb or @write_error_cb will eventually be called.
- *
- * Return value: a #SoupWriter, which must eventually be freed by
- * calling either soup_transfer_write_unref() or
- * soup_transfer_write_cancel().
- **/
-SoupWriter *
-soup_transfer_write (SoupSocket *sock,
- SoupTransferEncoding encoding,
- SoupWriteGetHeaderFn get_header_cb,
- SoupWriteGetChunkFn get_chunk_cb,
- SoupWriteDoneFn write_done_cb,
- SoupWriteErrorFn error_cb,
- gpointer user_data)
-{
- SoupWriter *writer;
-
- writer = create_writer (sock,
- encoding,
- write_done_cb,
- error_cb,
- user_data);
-
- writer->get_header_cb = get_header_cb;
- writer->get_chunk_cb = get_chunk_cb;
-
- return writer;
-}
-
-void
-soup_transfer_write_pause (SoupWriter *w)
-{
- g_return_if_fail (w != NULL);
-
- if (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
-soup_transfer_write_unpause (SoupWriter *w)
-{
- g_return_if_fail (w != NULL);
-
- if (!w->write_tag) {
- w->write_tag =
- g_signal_connect (w->sock, "writable",
- G_CALLBACK (writer_write), w);
- }
- if (!w->idle_tag)
- w->idle_tag = g_idle_add (idle_write, w);
-}
+++ /dev/null
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * soup-queue.c: Asyncronous Callback-based HTTP Request Queue.
- *
- * Authors:
- * Alex Graveley (alex@ximian.com)
- *
- * Copyright (C) 2000-2002, Ximian, Inc.
- */
-
-#ifndef SOUP_TRANSFER_H
-#define SOUP_TRANSFER_H 1
-
-#include <glib.h>
-
-#include <libsoup/soup-message.h>
-
-typedef enum {
- SOUP_TRANSFER_END = 0,
- SOUP_TRANSFER_CONTINUE,
-} SoupTransferDone;
-
-typedef enum {
- SOUP_TRANSFER_UNKNOWN = 0,
- SOUP_TRANSFER_CHUNKED,
- SOUP_TRANSFER_CONTENT_LENGTH,
-} SoupTransferEncoding;
-
-typedef struct _SoupReader SoupReader;
-typedef struct _SoupWriter SoupWriter;
-
-typedef void (*SoupReadHeadersDoneFn) (char *headers,
- guint header_len,
- SoupTransferEncoding *encoding,
- int *content_len,
- gpointer user_data);
-
-typedef void (*SoupReadChunkFn) (const char *chunk,
- guint len,
- 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 (SoupSocket *sock,
- 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_ref (SoupReader *r);
-gboolean soup_transfer_read_unref (SoupReader *r);
-void soup_transfer_read_cancel (SoupReader *r);
-
-void soup_transfer_read_set_callbacks (SoupReader *r,
- SoupReadHeadersDoneFn headers_done_cb,
- SoupReadChunkFn read_chunk_cb,
- SoupReadDoneFn read_done_cb,
- SoupReadErrorFn error_cb,
- gpointer user_data);
-
-
-typedef void (*SoupWriteDoneFn) (gpointer user_data);
-
-typedef void (*SoupWriteErrorFn) (gboolean headers_done, gpointer user_data);
-
-SoupWriter *soup_transfer_write_simple (SoupSocket *sock,
- GString *header,
- const SoupDataBuffer *src,
- SoupWriteDoneFn write_done_cb,
- SoupWriteErrorFn error_cb,
- gpointer user_data);
-
-typedef void (*SoupWriteGetHeaderFn) (GString **out_hdr,
- gpointer user_data);
-
-typedef SoupTransferDone (*SoupWriteGetChunkFn) (SoupDataBuffer *out_next,
- gpointer user_data);
-
-SoupWriter *soup_transfer_write (SoupSocket *sock,
- SoupTransferEncoding encoding,
- SoupWriteGetHeaderFn get_header_cb,
- SoupWriteGetChunkFn get_chunk_cb,
- SoupWriteDoneFn write_done_cb,
- SoupWriteErrorFn error_cb,
- gpointer user_data);
-
-void soup_transfer_write_pause (SoupWriter *w);
-
-void soup_transfer_write_unpause (SoupWriter *w);
-
-void soup_transfer_write_ref (SoupWriter *w);
-gboolean soup_transfer_write_unref (SoupWriter *w);
-void soup_transfer_write_cancel (SoupWriter *w);
-
-#endif /*SOUP_TRANSFER_H*/