From: Dan Winship Date: Fri, 22 Aug 2003 18:49:02 +0000 (+0000) Subject: New file containing SoupMessagePrivate and some other X-Git-Tag: LIBSOUP_2_1_2~42 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=079cb63d3a5a693e0ca991bbd13f61de260e7910;p=platform%2Fupstream%2Flibsoup.git New file containing SoupMessagePrivate and some other * 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 --- diff --git a/ChangeLog b/ChangeLog index 147eddb..3e35433 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,42 @@ +2003-08-22 Dan Winship + + * 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 * libsoup/soup-message.c: Make this a GObject. (Note that since diff --git a/libsoup/Makefile.am b/libsoup/Makefile.am index 4dbb738..741336a 100644 --- a/libsoup/Makefile.am +++ b/libsoup/Makefile.am @@ -73,7 +73,10 @@ libsoup_2_2_la_SOURCES = \ 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 \ @@ -85,8 +88,6 @@ libsoup_2_2_la_SOURCES = \ soup-socket.c \ soup-ssl.h \ soup-ssl.c \ - soup-transfer.h \ - soup-transfer.c \ soup-uri.c EXTRA_DIST= soup-marshal.list diff --git a/libsoup/soup-message-handlers.c b/libsoup/soup-message-handlers.c new file mode 100644 index 0000000..8ae6be8 --- /dev/null +++ b/libsoup/soup-message-handlers.c @@ -0,0 +1,324 @@ +/* -*- 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"); +} diff --git a/libsoup/soup-message-io.c b/libsoup/soup-message-io.c new file mode 100644 index 0000000..e1c9a23 --- /dev/null +++ b/libsoup/soup-message-io.c @@ -0,0 +1,675 @@ +/* -*- 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 +#include + +#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); +} diff --git a/libsoup/soup-message-private.h b/libsoup/soup-message-private.h new file mode 100644 index 0000000..a5ee2b2 --- /dev/null +++ b/libsoup/soup-message-private.h @@ -0,0 +1,124 @@ +/* -*- 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 */ diff --git a/libsoup/soup-message.c b/libsoup/soup-message.c index 77d1fd0..fb4a3d4 100644 --- a/libsoup/soup-message.c +++ b/libsoup/soup-message.c @@ -10,11 +10,11 @@ #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; @@ -26,7 +26,7 @@ init (GObject *object) 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); @@ -89,8 +89,6 @@ SOUP_MAKE_TYPE (soup_message, SoupMessage, class_init, init, PARENT_TYPE) * 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 * @@ -99,7 +97,6 @@ soup_message_new (SoupContext *context, const char *method) 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); @@ -121,8 +118,6 @@ soup_message_new (SoupContext *context, const char *method) * 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 * @@ -141,27 +136,6 @@ soup_message_new_full (SoupContext *context, 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. @@ -175,31 +149,11 @@ soup_message_cleanup (SoupMessage *req) { 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); @@ -237,7 +191,7 @@ soup_message_issue_callback (SoupMessage *req) 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); } } @@ -262,9 +216,9 @@ soup_message_disconnect (SoupMessage *msg) * 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) @@ -431,10 +385,8 @@ soup_message_queue (SoupMessage *req, } 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, @@ -442,11 +394,8 @@ requeue_read_error (gboolean body_started, gpointer user_data) } 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); @@ -479,24 +428,18 @@ soup_message_requeue (SoupMessage *req) { 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); + } } /** @@ -520,7 +463,7 @@ soup_message_send (SoupMessage *msg) 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; @@ -532,317 +475,6 @@ soup_message_send (SoupMessage *msg) 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) { diff --git a/libsoup/soup-message.h b/libsoup/soup-message.h index 2c72252..96882a8 100644 --- a/libsoup/soup-message.h +++ b/libsoup/soup-message.h @@ -21,13 +21,10 @@ 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, @@ -50,8 +47,6 @@ typedef struct { const char *method; - SoupTransferStatus status; - guint errorcode; SoupErrorClass errorclass; const char *errorphrase; @@ -183,35 +178,35 @@ typedef enum { 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); diff --git a/libsoup/soup-private.h b/libsoup/soup-private.h index c40ac0b..0c8bf99 100644 --- a/libsoup/soup-private.h +++ b/libsoup/soup-private.h @@ -62,27 +62,6 @@ typedef struct { #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, @@ -97,15 +76,6 @@ gboolean soup_context_authenticate_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); diff --git a/libsoup/soup-queue.c b/libsoup/soup-queue.c index a087bd3..fdc2b49 100644 --- a/libsoup/soup-queue.c +++ b/libsoup/soup-queue.c @@ -22,12 +22,12 @@ #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; @@ -54,58 +54,51 @@ soup_debug_print_headers (SoupMessage *req) } 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; @@ -117,13 +110,12 @@ soup_queue_error_cb (gboolean body_started, gpointer user_data) } 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; @@ -211,12 +203,10 @@ soup_queue_read_headers_cb (char *headers, } 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; @@ -227,12 +217,8 @@ soup_queue_read_chunk_cb (const 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 @@ -242,28 +228,14 @@ soup_queue_read_done_cb (char *body, 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); } @@ -350,11 +322,10 @@ soup_check_used_headers (gchar *key, } } -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 = { @@ -367,7 +338,7 @@ soup_get_request_header (SoupMessage *req) NULL }; - header = hdrs.out = g_string_new (NULL); + hdrs.out = header; proxy = soup_get_proxy (); suri = soup_context_get_uri (req->context); @@ -431,25 +402,22 @@ soup_get_request_header (SoupMessage *req) 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 */ @@ -478,26 +446,10 @@ start_request (SoupContext *ctx, SoupMessage *req) 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 @@ -667,13 +619,13 @@ soup_idle_handle_new_requests (gpointer unused) 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)) @@ -726,7 +678,7 @@ soup_queue_message (SoupMessage *req, return; } - if (req->status != SOUP_STATUS_IDLE) + if (req->priv->status != SOUP_MESSAGE_STATUS_IDLE) soup_message_cleanup (req); switch (req->response.owner) { @@ -759,7 +711,7 @@ soup_queue_message (SoupMessage *req, req->errorphrase = NULL; } - req->status = SOUP_STATUS_QUEUED; + req->priv->status = SOUP_MESSAGE_STATUS_QUEUED; soup_queue_add_request (req); diff --git a/libsoup/soup-server-message.c b/libsoup/soup-server-message.c index 98ccbfa..43642f5 100644 --- a/libsoup/soup-server-message.c +++ b/libsoup/soup-server-message.c @@ -15,8 +15,8 @@ #include #include "soup-server-message.h" +#include "soup-message-private.h" #include "soup-private.h" -#include "soup-transfer.h" struct SoupServerMessagePrivate { SoupServer *server; @@ -145,7 +145,7 @@ soup_server_message_start (SoupServerMessage *smsg) smsg->priv->started = TRUE; - soup_transfer_write_unpause (SOUP_MESSAGE (smsg)->priv->write_tag); + soup_message_write_unpause (SOUP_MESSAGE (smsg)); } gboolean @@ -181,7 +181,7 @@ soup_server_message_add_chunk (SoupServerMessage *smsg, 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 * @@ -208,7 +208,7 @@ soup_server_message_finish (SoupServerMessage *smsg) 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 diff --git a/libsoup/soup-server-message.h b/libsoup/soup-server-message.h index f9e2b65..5798960 100644 --- a/libsoup/soup-server-message.h +++ b/libsoup/soup-server-message.h @@ -8,7 +8,6 @@ #include #include -#include #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)) diff --git a/libsoup/soup-server.c b/libsoup/soup-server.c index 6c678d3..6441886 100644 --- a/libsoup/soup-server.c +++ b/libsoup/soup-server.c @@ -21,8 +21,8 @@ #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; @@ -181,54 +181,57 @@ soup_server_get_protocol (SoupServer *server) 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 @@ -250,36 +253,25 @@ set_response_error (SoupMessage *req, 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; @@ -379,7 +371,6 @@ call_handler (SoupMessage *req) 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); @@ -443,30 +434,10 @@ call_handler (SoupMessage *req) 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); @@ -476,28 +447,22 @@ get_chunk_cb (SoupDataBuffer *out_next, gpointer user_data) 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; @@ -506,28 +471,14 @@ read_done_cb (char *body, 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; @@ -540,14 +491,8 @@ start_request (SoupServer *server, SoupSocket *server_sock) /* 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 diff --git a/libsoup/soup-transfer.c b/libsoup/soup-transfer.c deleted file mode 100644 index 16db62a..0000000 --- a/libsoup/soup-transfer.c +++ /dev/null @@ -1,816 +0,0 @@ -/* -*- 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 -#endif - -#include -#include -#include -#include -#include -#include - -#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); -} diff --git a/libsoup/soup-transfer.h b/libsoup/soup-transfer.h deleted file mode 100644 index 068529a..0000000 --- a/libsoup/soup-transfer.h +++ /dev/null @@ -1,101 +0,0 @@ -/* -*- 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 - -#include - -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*/