Change the SoupURI properties to SoupAddress properties.
[platform/upstream/libsoup.git] / libsoup / soup-message.c
index e4241f3..2aa0473 100644 (file)
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 /*
- * soup-message.c: Asyncronous Callback-based SOAP Request Queue.
+ * soup-message.c: HTTP request/response
  *
- * Authors:
- *      Alex Graveley (alex@helixcode.com)
- *
- * Copyright (C) 2000, Helix Code, Inc.
+ * Copyright (C) 2000-2003, Ximian, Inc.
  */
 
+#include <stdlib.h>
+#include <string.h>
+
+#include "soup-address.h"
 #include "soup-auth.h"
+#include "soup-enum-types.h"
+#include "soup-marshal.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"
+#include "soup-uri.h"
 
 /**
- * soup_message_new:
- * @context: a %SoupContext for the destination endpoint.
- * @method: a string which will be used as the HTTP method for the created
- * request.
- * 
- * Creates a new empty %SoupMessage, which will connect to the URL represented
- * by @context. The new message has a status of @SOUP_STATUS_IDLE.
+ * SECTION:soup-message
+ * @short_description: An HTTP request and response.
+ * @see_also: #SoupMessageHeaders, #SoupMessageBody
  *
- * Return value: the new %SoupMessage.
- */
-SoupMessage *
-soup_message_new (SoupContext *context, const gchar *method) 
-{
-       SoupMessage *ret;
+ * A #SoupMessage represents an HTTP message that is being sent or
+ * received.
+ *
+ * For client-side usage, you would create a #SoupMessage with
+ * soup_message_new() or soup_message_new_from_uri(), set up its
+ * fields appropriate, and send it via a #SoupSession.
+ *
+ * For server-side usage, #SoupServer will create #SoupMessage<!--
+ * -->s automatically for incoming requests, which your application
+ * will receive via handlers.
+ *
+ * Note that libsoup's terminology here does not quite match the HTTP
+ * specification: in RFC 2616, an "HTTP-message" is
+ * <emphasis>either</emphasis> a Request, <emphasis>or</emphasis> a
+ * Response. In libsoup, a #SoupMessage combines both the request and
+ * the response.
+ **/
 
-       g_return_val_if_fail (context, NULL);
+/**
+ * SoupMessage:
+ * @method: the HTTP method
+ * @status_code: the HTTP status code
+ * @reason_phrase: the status phrase associated with @status_code
+ * @request_body: the request body
+ * @request_headers: the request headers
+ * @response_body: the response body
+ * @response_headers: the response headers
+ *
+ * Represents an HTTP message being sent or received.
+ *
+ * As described in the #SoupMessageBody documentation, the
+ * @request_body and @response_body %data fields will not necessarily
+ * be filled in at all times. When they are filled in, they will be
+ * terminated with a '\0' byte (which is not included in the %length),
+ * so you can use them as ordinary C strings (assuming that you know
+ * that the body doesn't have any other '\0' bytes).
+ *
+ * For a client-side #SoupMessage, @request_body's %data is usually
+ * filled in right before libsoup writes the request to the network,
+ * but you should not count on this; use soup_message_body_flatten()
+ * if you want to ensure that %data is filled in. @response_body's
+ * %data will be filled in before #SoupMessage::finished is emitted.
+ *
+ * For a server-side #SoupMessage, @request_body's %data will be
+ * filled in before #SoupMessage::got_body is emitted.
+ *
+ * To prevent the %data field from being filled in at all (eg, if you
+ * are handling the data from a #SoupMessage::got_chunk, and so don't
+ * need to see it all at the end), call
+ * soup_message_body_set_accumulate() on @response_body or
+ * @request_body as appropriate, passing %FALSE.
+ **/
+
+G_DEFINE_TYPE (SoupMessage, soup_message, G_TYPE_OBJECT)
+
+enum {
+       WROTE_INFORMATIONAL,
+       WROTE_HEADERS,
+       WROTE_CHUNK,
+       WROTE_BODY_DATA,
+       WROTE_BODY,
+
+       GOT_INFORMATIONAL,
+       GOT_HEADERS,
+       GOT_CHUNK,
+       GOT_BODY,
+
+       RESTARTED,
+       FINISHED,
+
+       LAST_SIGNAL
+};
 
-       ret          = g_new0 (SoupMessage, 1);
-       ret->priv    = g_new0 (SoupMessagePrivate, 1);
-       ret->status  = SOUP_STATUS_IDLE;
-       ret->context = context;
-       ret->method  = method ? method : SOUP_METHOD_POST;
+static guint signals[LAST_SIGNAL] = { 0 };
 
-       ret->request_headers = g_hash_table_new (soup_str_case_hash, 
-                                                soup_str_case_equal);
+enum {
+       PROP_0,
 
-       ret->response_headers = g_hash_table_new (soup_str_case_hash, 
-                                                 soup_str_case_equal);
+       PROP_METHOD,
+       PROP_URI,
+       PROP_HTTP_VERSION,
+       PROP_FLAGS,
+       PROP_SERVER_SIDE,
+       PROP_STATUS_CODE,
+       PROP_REASON_PHRASE,
 
-       ret->priv->http_version = SOUP_HTTP_1_1;
+       LAST_PROP
+};
 
-       soup_context_ref (context);
+static void got_body (SoupMessage *req);
+static void restarted (SoupMessage *req);
+static void finished (SoupMessage *req);
 
-       return ret;
-}
+static void set_property (GObject *object, guint prop_id,
+                         const GValue *value, GParamSpec *pspec);
+static void get_property (GObject *object, guint prop_id,
+                         GValue *value, GParamSpec *pspec);
 
-/**
- * soup_message_new_full:
- * @context: a %SoupContext for the destination endpoint.
- * @method: a string which will be used as the HTTP method for the created
- * request.
- * @req_owner: the %SoupOwnership of the passed data buffer.
- * @req_body: a data buffer containing the body of the message request.
- * @req_length: the byte length of @req_body.
- * 
- * Creates a new %SoupMessage, which will connect to the URL represented by
- * @context. The new message has a status of @SOUP_STATUS_IDLE. The request data
- * buffer will be filled from @req_owner, @req_body, and @req_length
- * respectively.
- *
- * Return value: the new %SoupMessage.
- */
-SoupMessage *
-soup_message_new_full (SoupContext   *context,
-                      const gchar   *method,
-                      SoupOwnership  req_owner,
-                      gchar         *req_body,
-                      gulong         req_length)
+static void
+soup_message_init (SoupMessage *msg)
 {
-       SoupMessage *ret = soup_message_new (context, method);
+       SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
 
-       ret->request.owner = req_owner;
-       ret->request.body = req_body;
-       ret->request.length = req_length;
+       priv->io_status = SOUP_MESSAGE_IO_STATUS_IDLE;
+       priv->http_version = priv->orig_http_version = SOUP_HTTP_1_1;
 
-       return ret;
+       msg->request_body = soup_message_body_new ();
+       msg->request_headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_REQUEST);
+       msg->response_body = soup_message_body_new ();
+       msg->response_headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_RESPONSE);
 }
 
-/**
- * soup_message_cleanup:
- * @req: a %SoupMessage.
- * 
- * Frees any temporary resources created in the processing of @req. Request and
- * response data buffers are left intact.
- */
-void 
-soup_message_cleanup (SoupMessage *req)
+static void
+finalize (GObject *object)
 {
-       g_return_if_fail (req != NULL);
+       SoupMessage *msg = SOUP_MESSAGE (object);
+       SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
 
-       if (req->priv->read_tag) {
-               soup_transfer_read_cancel (req->priv->read_tag);
-               req->priv->read_tag = 0;
-       }
+       soup_message_io_cleanup (msg);
+       if (priv->chunk_allocator_dnotify)
+               priv->chunk_allocator_dnotify (priv->chunk_allocator_data);
 
-       if (req->priv->write_tag) {
-               soup_transfer_write_cancel (req->priv->write_tag);
-               req->priv->write_tag = 0;
-       }
+       if (priv->uri)
+               soup_uri_free (priv->uri);
+       if (priv->addr)
+               g_object_unref (priv->addr);
 
-       if (req->priv->connect_tag) {
-               soup_context_cancel_connect (req->priv->connect_tag);
-               req->priv->connect_tag = NULL;
-       }
-       if (req->connection) {
-               soup_connection_release (req->connection);
-               req->connection = NULL;
-       }
+       if (priv->auth)
+               g_object_unref (priv->auth);
+       if (priv->proxy_auth)
+               g_object_unref (priv->proxy_auth);
+
+       soup_message_body_free (msg->request_body);
+       soup_message_headers_free (msg->request_headers);
+       soup_message_body_free (msg->response_body);
+       soup_message_headers_free (msg->response_headers);
+
+       g_free (msg->reason_phrase);
 
-       soup_active_requests = g_slist_remove (soup_active_requests, req);
+       G_OBJECT_CLASS (soup_message_parent_class)->finalize (object);
 }
 
 static void
-free_header (gchar *name, GSList *vals, gpointer unused)
+soup_message_class_init (SoupMessageClass *message_class)
 {
-       g_free (name);
-       g_slist_foreach (vals, (GFunc) g_free, NULL);
-       g_slist_free (vals);
+       GObjectClass *object_class = G_OBJECT_CLASS (message_class);
+
+       g_type_class_add_private (message_class, sizeof (SoupMessagePrivate));
+
+       /* virtual method definition */
+       message_class->got_body     = got_body;
+       message_class->restarted    = restarted;
+       message_class->finished     = finished;
+
+       /* virtual method override */
+       object_class->finalize = finalize;
+       object_class->set_property = set_property;
+       object_class->get_property = get_property;
+
+       /* signals */
+
+       /**
+        * SoupMessage::wrote-informational:
+        * @msg: the message
+        *
+        * Emitted immediately after writing a 1xx (Informational)
+        * response for a (server-side) message.
+        **/
+       signals[WROTE_INFORMATIONAL] =
+               g_signal_new ("wrote_informational",
+                             G_OBJECT_CLASS_TYPE (object_class),
+                             G_SIGNAL_RUN_FIRST,
+                             G_STRUCT_OFFSET (SoupMessageClass, wrote_informational),
+                             NULL, NULL,
+                             soup_marshal_NONE__NONE,
+                             G_TYPE_NONE, 0);
+
+       /**
+        * SoupMessage::wrote-headers:
+        * @msg: the message
+        *
+        * Emitted immediately after writing the headers for a
+        * message. (For a client-side message, this is after writing
+        * the request headers; for a server-side message, it is after
+        * writing the response headers.)
+        **/
+       signals[WROTE_HEADERS] =
+               g_signal_new ("wrote_headers",
+                             G_OBJECT_CLASS_TYPE (object_class),
+                             G_SIGNAL_RUN_FIRST,
+                             G_STRUCT_OFFSET (SoupMessageClass, wrote_headers),
+                             NULL, NULL,
+                             soup_marshal_NONE__NONE,
+                             G_TYPE_NONE, 0);
+
+       /**
+        * SoupMessage::wrote-chunk:
+        * @msg: the message
+        *
+        * Emitted immediately after writing a body chunk for a message.
+        *
+        * Note that this signal is not parallel to
+        * #SoupMessage::got_chunk; it is emitted only when a complete
+        * chunk (added with soup_message_body_append() or
+        * soup_message_body_append_buffer()) has been written. To get
+        * more useful continuous progress information, use
+        * #SoupMessage::wrote_body_data.
+        **/
+       signals[WROTE_CHUNK] =
+               g_signal_new ("wrote_chunk",
+                             G_OBJECT_CLASS_TYPE (object_class),
+                             G_SIGNAL_RUN_FIRST,
+                             G_STRUCT_OFFSET (SoupMessageClass, wrote_chunk),
+                             NULL, NULL,
+                             soup_marshal_NONE__NONE,
+                             G_TYPE_NONE, 0);
+
+       /**
+        * SoupMessage::wrote-body-data:
+        * @msg: the message
+        * @chunk: the data written
+        *
+        * Emitted immediately after writing a portion of the message
+        * body to the network.
+        *
+        * Unlike #SoupMessage::wrote_chunk, this is emitted after
+        * every successful write() call, not only after finishing a
+        * complete "chunk".
+        **/
+       signals[WROTE_BODY_DATA] =
+               g_signal_new ("wrote_body_data",
+                             G_OBJECT_CLASS_TYPE (object_class),
+                             G_SIGNAL_RUN_FIRST,
+                             0, /* FIXME after next ABI break */
+                             NULL, NULL,
+                             soup_marshal_NONE__BOXED,
+                             G_TYPE_NONE, 1,
+                             SOUP_TYPE_BUFFER);
+
+       /**
+        * SoupMessage::wrote-body:
+        * @msg: the message
+        *
+        * Emitted immediately after writing the complete body for a
+        * message. (For a client-side message, this means that
+        * libsoup is done writing and is now waiting for the response
+        * from the server. For a server-side message, this means that
+        * libsoup has finished writing the response and is nearly
+        * done with the message.)
+        **/
+       signals[WROTE_BODY] =
+               g_signal_new ("wrote_body",
+                             G_OBJECT_CLASS_TYPE (object_class),
+                             G_SIGNAL_RUN_FIRST,
+                             G_STRUCT_OFFSET (SoupMessageClass, wrote_body),
+                             NULL, NULL,
+                             soup_marshal_NONE__NONE,
+                             G_TYPE_NONE, 0);
+
+       /**
+        * SoupMessage::got-informational:
+        * @msg: the message
+        *
+        * Emitted after receiving a 1xx (Informational) response for
+        * a (client-side) message. The response_headers will be
+        * filled in with the headers associated with the
+        * informational response; however, those header values will
+        * be erased after this signal is done.
+        *
+        * If you cancel or requeue @msg while processing this signal,
+        * then the current HTTP I/O will be stopped after this signal
+        * emission finished, and @msg's connection will be closed.
+        **/
+       signals[GOT_INFORMATIONAL] =
+               g_signal_new ("got_informational",
+                             G_OBJECT_CLASS_TYPE (object_class),
+                             G_SIGNAL_RUN_FIRST,
+                             G_STRUCT_OFFSET (SoupMessageClass, got_informational),
+                             NULL, NULL,
+                             soup_marshal_NONE__NONE,
+                             G_TYPE_NONE, 0);
+
+       /**
+        * SoupMessage::got-headers:
+        * @msg: the message
+        *
+        * Emitted after receiving all message headers for a message.
+        * (For a client-side message, this is after receiving the
+        * Status-Line and response headers; for a server-side
+        * message, it is after receiving the Request-Line and request
+        * headers.)
+        *
+        * See also soup_message_add_header_handler() and
+        * soup_message_add_status_code_handler(), which can be used
+        * to connect to a subset of emissions of this signal.
+        *
+        * If you cancel or requeue @msg while processing this signal,
+        * then the current HTTP I/O will be stopped after this signal
+        * emission finished, and @msg's connection will be closed.
+        * (If you need to requeue a message--eg, after handling
+        * authentication or redirection--it is usually better to
+        * requeue it from a #SoupMessage::got_body handler rather
+        * than a #SoupMessage::got_header handler, so that the
+        * existing HTTP connection can be reused.)
+        **/
+       signals[GOT_HEADERS] =
+               g_signal_new ("got_headers",
+                             G_OBJECT_CLASS_TYPE (object_class),
+                             G_SIGNAL_RUN_FIRST,
+                             G_STRUCT_OFFSET (SoupMessageClass, got_headers),
+                             NULL, NULL,
+                             soup_marshal_NONE__NONE,
+                             G_TYPE_NONE, 0);
+
+       /**
+        * SoupMessage::got-chunk:
+        * @msg: the message
+        * @chunk: the just-read chunk
+        *
+        * Emitted after receiving a chunk of a message body. Note
+        * that "chunk" in this context means any subpiece of the
+        * body, not necessarily the specific HTTP 1.1 chunks sent by
+        * the other side.
+        *
+        * If you cancel or requeue @msg while processing this signal,
+        * then the current HTTP I/O will be stopped after this signal
+        * emission finished, and @msg's connection will be closed.
+        **/
+       signals[GOT_CHUNK] =
+               g_signal_new ("got_chunk",
+                             G_OBJECT_CLASS_TYPE (object_class),
+                             G_SIGNAL_RUN_FIRST,
+                             G_STRUCT_OFFSET (SoupMessageClass, got_chunk),
+                             NULL, NULL,
+                             soup_marshal_NONE__BOXED,
+                             G_TYPE_NONE, 1,
+                             /* Use %G_SIGNAL_TYPE_STATIC_SCOPE so that
+                              * the %SOUP_MEMORY_TEMPORARY buffers used
+                              * by soup-message-io.c when emitting this
+                              * signal don't get forcibly copied by
+                              * g_signal_emit().
+                              */
+                             SOUP_TYPE_BUFFER | G_SIGNAL_TYPE_STATIC_SCOPE);
+
+       /**
+        * SoupMessage::got-body:
+        * @msg: the message
+        *
+        * Emitted after receiving the complete message body. (For a
+        * server-side message, this means it has received the request
+        * body. For a client-side message, this means it has received
+        * the response body and is nearly done with the message.)
+        *
+        * See also soup_message_add_header_handler() and
+        * soup_message_add_status_code_handler(), which can be used
+        * to connect to a subset of emissions of this signal.
+        **/
+       signals[GOT_BODY] =
+               g_signal_new ("got_body",
+                             G_OBJECT_CLASS_TYPE (object_class),
+                             G_SIGNAL_RUN_FIRST,
+                             G_STRUCT_OFFSET (SoupMessageClass, got_body),
+                             NULL, NULL,
+                             soup_marshal_NONE__NONE,
+                             G_TYPE_NONE, 0);
+
+       /**
+        * SoupMessage::restarted:
+        * @msg: the message
+        *
+        * Emitted when a request that was already sent once is now
+        * being sent again (eg, because the first attempt received a
+        * redirection response, or because we needed to use
+        * authentication).
+        **/
+       signals[RESTARTED] =
+               g_signal_new ("restarted",
+                             G_OBJECT_CLASS_TYPE (object_class),
+                             G_SIGNAL_RUN_FIRST,
+                             G_STRUCT_OFFSET (SoupMessageClass, restarted),
+                             NULL, NULL,
+                             soup_marshal_NONE__NONE,
+                             G_TYPE_NONE, 0);
+
+       /**
+        * SoupMessage::finished:
+        * @msg: the message
+        *
+        * Emitted when all HTTP processing is finished for a message.
+        * (After #SoupMessage::got_body for client-side messages, or
+        * after #SoupMessage::wrote_body for server-side messages.)
+        **/
+       signals[FINISHED] =
+               g_signal_new ("finished",
+                             G_OBJECT_CLASS_TYPE (object_class),
+                             G_SIGNAL_RUN_FIRST,
+                             G_STRUCT_OFFSET (SoupMessageClass, finished),
+                             NULL, NULL,
+                             soup_marshal_NONE__NONE,
+                             G_TYPE_NONE, 0);
+
+       /* properties */
+       g_object_class_install_property (
+               object_class, PROP_METHOD,
+               g_param_spec_string (SOUP_MESSAGE_METHOD,
+                                    "Method",
+                                    "The message's HTTP method",
+                                    SOUP_METHOD_GET,
+                                    G_PARAM_READWRITE));
+       g_object_class_install_property (
+               object_class, PROP_URI,
+               g_param_spec_boxed (SOUP_MESSAGE_URI,
+                                   "URI",
+                                   "The message's Request-URI",
+                                   SOUP_TYPE_URI,
+                                   G_PARAM_READWRITE));
+       g_object_class_install_property (
+               object_class, PROP_HTTP_VERSION,
+               g_param_spec_enum (SOUP_MESSAGE_HTTP_VERSION,
+                                  "HTTP Version",
+                                  "The HTTP protocol version to use",
+                                  SOUP_TYPE_HTTP_VERSION,
+                                  SOUP_HTTP_1_1,
+                                  G_PARAM_READWRITE));
+       g_object_class_install_property (
+               object_class, PROP_FLAGS,
+               g_param_spec_flags (SOUP_MESSAGE_FLAGS,
+                                   "Flags",
+                                   "Various message options",
+                                   SOUP_TYPE_MESSAGE_FLAGS,
+                                   0,
+                                   G_PARAM_READWRITE));
+       g_object_class_install_property (
+               object_class, PROP_SERVER_SIDE,
+               g_param_spec_boolean (SOUP_MESSAGE_SERVER_SIDE,
+                                     "Server-side",
+                                     "Whether or not the message is server-side rather than client-side",
+                                     FALSE,
+                                     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+       g_object_class_install_property (
+               object_class, PROP_STATUS_CODE,
+               g_param_spec_uint (SOUP_MESSAGE_STATUS_CODE,
+                                  "Status code",
+                                  "The HTTP response status code",
+                                  0, 599, 0,
+                                  G_PARAM_READWRITE));
+       g_object_class_install_property (
+               object_class, PROP_REASON_PHRASE,
+               g_param_spec_string (SOUP_MESSAGE_REASON_PHRASE,
+                                    "Reason phrase",
+                                    "The HTTP response reason phrase",
+                                    NULL,
+                                    G_PARAM_READWRITE));
 }
 
 static void
-finalize_message (SoupMessage *req)
+set_property (GObject *object, guint prop_id,
+             const GValue *value, GParamSpec *pspec)
 {
-       soup_context_unref (req->context);
+       SoupMessage *msg = SOUP_MESSAGE (object);
+       SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
 
-       if (req->request.owner == SOUP_BUFFER_SYSTEM_OWNED)
-               g_free (req->request.body);
-       if (req->response.owner == SOUP_BUFFER_SYSTEM_OWNED)
-               g_free (req->response.body);
+       switch (prop_id) {
+       case PROP_METHOD:
+               msg->method = g_intern_string (g_value_get_string (value));
+               break;
+       case PROP_URI:
+               soup_message_set_uri (msg, g_value_get_boxed (value));
+               break;
+       case PROP_HTTP_VERSION:
+               soup_message_set_http_version (msg, g_value_get_enum (value));
+               break;
+       case PROP_FLAGS:
+               soup_message_set_flags (msg, g_value_get_flags (value));
+               break;
+       case PROP_SERVER_SIDE:
+               priv->server_side = g_value_get_boolean (value);
+               break;
+       case PROP_STATUS_CODE:
+               soup_message_set_status (msg, g_value_get_uint (value));
+               break;
+       case PROP_REASON_PHRASE:
+               soup_message_set_status_full (msg, msg->status_code,
+                                             g_value_get_string (value));
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+               break;
+       }
+}
 
-       if (req->priv->req_header) 
-               g_string_free (req->priv->req_header, TRUE);
+static void
+get_property (GObject *object, guint prop_id,
+             GValue *value, GParamSpec *pspec)
+{
+       SoupMessage *msg = SOUP_MESSAGE (object);
+       SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
 
-       if (req->request_headers) {
-               g_hash_table_foreach (req->request_headers,
-                                     (GHFunc) free_header,
-                                     NULL);
-               g_hash_table_destroy (req->request_headers);
+       switch (prop_id) {
+       case PROP_METHOD:
+               g_value_set_string (value, msg->method);
+               break;
+       case PROP_URI:
+               g_value_set_boxed (value, priv->uri);
+               break;
+       case PROP_HTTP_VERSION:
+               g_value_set_enum (value, priv->http_version);
+               break;
+       case PROP_FLAGS:
+               g_value_set_flags (value, priv->msg_flags);
+               break;
+       case PROP_SERVER_SIDE:
+               g_value_set_boolean (value, priv->server_side);
+               break;
+       case PROP_STATUS_CODE:
+               g_value_set_uint (value, msg->status_code);
+               break;
+       case PROP_REASON_PHRASE:
+               g_value_set_string (value, msg->reason_phrase);
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+               break;
        }
+}
 
-       if (req->response_headers) {
-               g_hash_table_foreach (req->response_headers,
-                                     (GHFunc) free_header,
-                                     NULL);
-               g_hash_table_destroy (req->response_headers);
-       }
 
-       g_slist_foreach (req->priv->content_handlers, (GFunc) g_free, NULL);
-       g_slist_free (req->priv->content_handlers);
+/**
+ * soup_message_new:
+ * @method: the HTTP method for the created request
+ * @uri_string: the destination endpoint (as a string)
+ * 
+ * Creates a new empty #SoupMessage, which will connect to @uri
+ *
+ * Return value: the new #SoupMessage (or %NULL if @uri could not
+ * be parsed).
+ */
+SoupMessage *
+soup_message_new (const char *method, const char *uri_string)
+{
+       SoupMessage *msg;
+       SoupURI *uri;
+
+       g_return_val_if_fail (method != NULL, NULL);
+       g_return_val_if_fail (uri_string != NULL, NULL);
+
+       uri = soup_uri_new (uri_string);
+       if (!uri)
+               return NULL;
+       if (!uri->host) {
+               soup_uri_free (uri);
+               return NULL;
+       }
 
-       g_free ((gchar *) req->errorphrase);
-       g_free (req->priv);
-       g_free (req);
+       msg = soup_message_new_from_uri (method, uri);
+       soup_uri_free (uri);
+       return msg;
 }
 
 /**
- * soup_message_free:
- * @req: a %SoupMessage to destroy.
+ * soup_message_new_from_uri:
+ * @method: the HTTP method for the created request
+ * @uri: the destination endpoint (as a #SoupURI)
  * 
- * Destroys the %SoupMessage pointed to by @req. Request and response headers
- * are freed. Request and response data buffers are also freed if their
- * ownership is %SOUP_BUFFER_SYSTEM_OWNED. The message's destination context
- * will be de-referenced.
+ * Creates a new empty #SoupMessage, which will connect to @uri
+ *
+ * Return value: the new #SoupMessage
  */
-void 
-soup_message_free (SoupMessage *req)
+SoupMessage *
+soup_message_new_from_uri (const char *method, SoupURI *uri)
 {
-       g_return_if_fail (req != NULL);
-
-       soup_message_cleanup (req);
-
-       finalize_message (req);
+       return g_object_new (SOUP_TYPE_MESSAGE,
+                            SOUP_MESSAGE_METHOD, method,
+                            SOUP_MESSAGE_URI, uri,
+                            NULL);
 }
 
 /**
- * soup_message_issue_callback:
- * @req: a %SoupMessage currently being processed.
- * @error: a %SoupErrorCode to be passed to %req's completion callback.
+ * soup_message_set_request:
+ * @msg: the message
+ * @content_type: MIME Content-Type of the body
+ * @req_use: a #SoupMemoryUse describing how to handle @req_body
+ * @req_body: a data buffer containing the body of the message request.
+ * @req_length: the byte length of @req_body.
  * 
- * Finalizes the message request, by first freeing any temporary resources, then
- * issuing the callback function pointer passed in %soup_message_new or
- * %soup_message_new_full. If, after returning from the callback, the message
- * has not been requeued, @msg is destroyed using %soup_message_free.
+ * Convenience function to set the request body of a #SoupMessage. If
+ * @content_type is %NULL, the request body must be empty as well.
  */
 void
-soup_message_issue_callback (SoupMessage *req)
+soup_message_set_request (SoupMessage    *msg,
+                         const char     *content_type,
+                         SoupMemoryUse   req_use,
+                         const char     *req_body,
+                         gsize           req_length)
 {
-       g_return_if_fail (req != NULL);
-
-       /* 
-        * Make sure we don't have some icky recursion if the callback 
-        * runs the main loop, and the connection has some data or error 
-        * which causes the callback to be run again.
-        */
-       soup_message_cleanup (req);
-
-       if (req->priv->callback) {
-               (*req->priv->callback) (req, req->priv->user_data);
-
-               if (req->status != SOUP_STATUS_QUEUED)
-                       finalize_message (req);
+       g_return_if_fail (SOUP_IS_MESSAGE (msg));
+       g_return_if_fail (content_type != NULL || req_length == 0);
+
+       if (content_type) {
+               soup_message_headers_replace (msg->request_headers,
+                                             "Content-Type", content_type);
+               soup_message_body_append (msg->request_body, req_use,
+                                         req_body, req_length);
+       } else {
+               soup_message_headers_remove (msg->request_headers,
+                                            "Content-Type");
+               soup_message_body_truncate (msg->request_body);
        }
 }
 
 /**
- * soup_message_cancel:
- * @req: a %SoupMessage currently being processed.
+ * soup_message_set_response:
+ * @msg: the message
+ * @content_type: MIME Content-Type of the body
+ * @resp_use: a #SoupMemoryUse describing how to handle @resp_body
+ * @resp_body: a data buffer containing the body of the message response.
+ * @resp_length: the byte length of @resp_body.
  * 
- * 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.
+ * Convenience function to set the response body of a #SoupMessage. If
+ * @content_type is %NULL, the response body must be empty as well.
  */
-void 
-soup_message_cancel (SoupMessage *msg) 
+void
+soup_message_set_response (SoupMessage    *msg,
+                          const char     *content_type,
+                          SoupMemoryUse   resp_use,
+                          const char     *resp_body,
+                          gsize           resp_length)
 {
-       soup_message_set_error (msg, SOUP_ERROR_CANCELLED);
-       soup_message_issue_callback (msg);
+       g_return_if_fail (SOUP_IS_MESSAGE (msg));
+       g_return_if_fail (content_type != NULL || resp_length == 0);
+
+       if (content_type) {
+               soup_message_headers_replace (msg->response_headers,
+                                             "Content-Type", content_type);
+               soup_message_body_append (msg->response_body, resp_use,
+                                         resp_body, resp_length);
+       } else {
+               soup_message_headers_remove (msg->response_headers,
+                                            "Content-Type");
+               soup_message_body_truncate (msg->response_body);
+       }
 }
 
-void 
-soup_message_add_header (GHashTable  *hash,
-                        const gchar *name,
-                        const gchar *value) 
+/**
+ * soup_message_wrote_informational:
+ * @msg: a #SoupMessage
+ *
+ * Emits the %wrote_informational signal, indicating that the IO layer
+ * finished writing an informational (1xx) response for @msg.
+ **/
+void
+soup_message_wrote_informational (SoupMessage *msg)
 {
-       gboolean exists = FALSE;
-       gpointer old_name;
-       GSList *old_value;
-
-       g_return_if_fail (hash != NULL);
-       g_return_if_fail (name != NULL || name [0] != '\0');    
-
-       exists = g_hash_table_lookup_extended (hash, 
-                                              name, 
-                                              &old_name, 
-                                              (gpointer *) &old_value);
-
-       if (value && exists)
-               old_value = g_slist_append (old_value,
-                                           g_strdup (value));
-       else if (value && !exists)
-               g_hash_table_insert (hash, 
-                                    g_strdup (name), 
-                                    g_slist_append (NULL, 
-                                                    g_strdup (value)));
-       else if (!value && exists) {
-               g_hash_table_remove (hash, name);
-               free_header (old_name, old_value, NULL);
-       } 
+       g_signal_emit (msg, signals[WROTE_INFORMATIONAL], 0);
 }
 
 /**
- * soup_message_get_header:
- * @req: a %SoupMessage.
- * @name: header name.
- * 
- * Lookup the first transport header with a key equal to @name.
+ * soup_message_wrote_headers:
+ * @msg: a #SoupMessage
  *
- * Return value: the header's value or NULL if not found.
- */
-const gchar *
-soup_message_get_header (GHashTable *hash,
-                        const gchar *name)
+ * Emits the %wrote_headers signal, indicating that the IO layer
+ * finished writing the (non-informational) headers for @msg.
+ **/
+void
+soup_message_wrote_headers (SoupMessage *msg)
 {
-       GSList *vals;
+       g_signal_emit (msg, signals[WROTE_HEADERS], 0);
+}
 
-       g_return_val_if_fail (hash != NULL, NULL);
-       g_return_val_if_fail (name != NULL || name [0] != '\0', NULL);  
+/**
+ * soup_message_wrote_chunk:
+ * @msg: a #SoupMessage
+ *
+ * Emits the %wrote_chunk signal, indicating that the IO layer
+ * finished writing a chunk of @msg's body.
+ **/
+void
+soup_message_wrote_chunk (SoupMessage *msg)
+{
+       g_signal_emit (msg, signals[WROTE_CHUNK], 0);
+}
 
-       vals = g_hash_table_lookup (hash, name);
-       if (vals) 
-               return vals->data;
+/**
+ * soup_message_wrote_body_data:
+ * @msg: a #SoupMessage
+ * @chunk: the data written
+ *
+ * Emits the %wrote_body_data signal, indicating that the IO layer
+ * finished writing a portion of @msg's body.
+ **/
+void
+soup_message_wrote_body_data (SoupMessage *msg, SoupBuffer *chunk)
+{
+       g_signal_emit (msg, signals[WROTE_BODY_DATA], 0, chunk);
+}
 
-       return NULL;
+/**
+ * soup_message_wrote_body:
+ * @msg: a #SoupMessage
+ *
+ * Emits the %wrote_body signal, indicating that the IO layer finished
+ * writing the body for @msg.
+ **/
+void
+soup_message_wrote_body (SoupMessage *msg)
+{
+       g_signal_emit (msg, signals[WROTE_BODY], 0);
 }
 
 /**
- * soup_message_get_header_list:
- * @req: a %SoupMessage.
- * @name: header name.
- * 
- * Lookup the all transport request headers with a key equal to @name.
+ * soup_message_got_informational:
+ * @msg: a #SoupMessage
  *
- * Return value: a const pointer to a GSList of header values or NULL if not
- * found.  
- */
-const GSList *
-soup_message_get_header_list (GHashTable  *hash,
-                             const gchar *name)
+ * Emits the %got_informational signal, indicating that the IO layer
+ * read a complete informational (1xx) response for @msg.
+ **/
+void
+soup_message_got_informational (SoupMessage *msg)
 {
-       g_return_val_if_fail (hash != NULL, NULL);
-       g_return_val_if_fail (name != NULL || name [0] != '\0', NULL);  
+       g_signal_emit (msg, signals[GOT_INFORMATIONAL], 0);
+}
 
-       return g_hash_table_lookup (hash, name);
+/**
+ * soup_message_got_headers:
+ * @msg: a #SoupMessage
+ *
+ * Emits the %got_headers signal, indicating that the IO layer
+ * finished reading the (non-informational) headers for @msg.
+ **/
+void
+soup_message_got_headers (SoupMessage *msg)
+{
+       g_signal_emit (msg, signals[GOT_HEADERS], 0);
 }
 
 /**
- * soup_message_set_request_header:
- * @req: a %SoupMessage.
- * @name: header name.
- * @value: header value.
+ * soup_message_got_chunk:
+ * @msg: a #SoupMessage
+ * @chunk: the newly-read chunk
  *
- * ** DEPRECATED **
- * 
- * Adds a new transport header to be sent on an outgoing request. Passing a NULL
- * @value will remove all headers with a name equal to @name.
- */
+ * Emits the %got_chunk signal, indicating that the IO layer finished
+ * reading a chunk of @msg's body.
+ **/
 void
-soup_message_set_request_header (SoupMessage *req,
-                                const gchar *name,
-                                const gchar *value) 
+soup_message_got_chunk (SoupMessage *msg, SoupBuffer *chunk)
+{
+       g_signal_emit (msg, signals[GOT_CHUNK], 0, chunk);
+}
+
+static void
+got_body (SoupMessage *req)
 {
-       g_return_if_fail (req != NULL);
-       g_return_if_fail (name != NULL || name [0] != '\0');
+       SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (req);
+       SoupMessageBody *body;
 
-       g_warning ("soup_message_set_request_header is DEPRECATED. Use "
-                  "soup_message_add_header, with msg->request_headers as "
-                  "the first argument.\n");
+       body = priv->server_side ? req->request_body : req->response_body;
+       if (soup_message_body_get_accumulate (body)) {
+               SoupBuffer *buffer;
 
-       soup_message_add_header (req->request_headers, name, value);
+               buffer = soup_message_body_flatten (body);
+               soup_buffer_free (buffer);
+       }
 }
 
 /**
- * soup_message_get_request_header:
- * @req: a %SoupMessage.
- * @name: header name.
- * 
- * ** DEPRECATED **
- * 
- * Lookup the first transport request header with a key equal to @name.
+ * soup_message_got_body:
+ * @msg: a #SoupMessage
  *
- * Return value: the header's value or NULL if not found.
- */
-const gchar *
-soup_message_get_request_header (SoupMessage *req,
-                                const gchar *name) 
-{
-       GSList *vals;
-       g_return_val_if_fail (req != NULL, NULL);
-       g_return_val_if_fail (name != NULL || name [0] != '\0', NULL);
-
-       g_warning ("soup_message_get_request_header is DEPRECATED. Use "
-                  "soup_message_get_header, with msg->request_headers as "
-                  "the first argument.\n");
-
-       if (req->request_headers) {
-               vals = g_hash_table_lookup (req->request_headers, name);
-               if (vals) 
-                       return vals->data;
-       }
+ * Emits the %got_body signal, indicating that the IO layer finished
+ * reading the body for @msg.
+ **/
+void
+soup_message_got_body (SoupMessage *msg)
+{
+       g_signal_emit (msg, signals[GOT_BODY], 0);
+}
 
-       return NULL;
+static void
+restarted (SoupMessage *req)
+{
+       soup_message_io_stop (req);
 }
 
 /**
- * soup_message_set_response_header:
- * @req: a %SoupMessage.
- * @name: header name.
- * @value: header value.
- * 
- * ** DEPRECATED **
- * 
- * Adds a new transport header to be sent on an outgoing response. Passing a
- * NULL @value will remove all headers with a name equal to @name.
- */
+ * soup_message_restarted:
+ * @msg: a #SoupMessage
+ *
+ * Emits the %restarted signal, indicating that @msg should be
+ * requeued.
+ **/
 void
-soup_message_set_response_header (SoupMessage *req,
-                                 const gchar *name,
-                                 const gchar *value) 
+soup_message_restarted (SoupMessage *msg)
 {
-       g_return_if_fail (req != NULL);
-       g_return_if_fail (name != NULL || name [0] != '\0');
+       g_signal_emit (msg, signals[RESTARTED], 0);
+}
 
-       g_warning ("soup_message_set_response_header is DEPRECATED. Use "
-                  "soup_message_add_header, with msg->response_headers as "
-                  "the first argument.\n");
+static void
+finished (SoupMessage *req)
+{
+       SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (req);
 
-       soup_message_add_header (req->response_headers, name, value);
+       soup_message_io_stop (req);
+       priv->io_status = SOUP_MESSAGE_IO_STATUS_FINISHED;
 }
 
 /**
- * soup_message_get_response_header:
- * @req: a %SoupMessage.
- * @name: header name.
- * 
- * ** DEPRECATED **
- * 
- * Lookup the transport response header with a key equal to @name.
+ * soup_message_finished:
+ * @msg: a #SoupMessage
  *
- * Return value: the header's value or NULL if not found.
- */
-const gchar *
-soup_message_get_response_header (SoupMessage *req,
-                                 const gchar *name) 
-{
-       GSList *vals;
-       g_return_val_if_fail (req != NULL, NULL);
-       g_return_val_if_fail (name != NULL || name [0] != '\0', NULL);
-
-       g_warning ("soup_message_get_response_header is DEPRECATED. Use "
-                  "soup_message_get_header, with msg->response_headers as "
-                  "the first argument.\n");
-
-       if (req->response_headers) {
-               vals = g_hash_table_lookup (req->response_headers, name);
-               if (vals) 
-                       return vals->data;
-       }
+ * Emits the %finished signal, indicating that @msg has been completely
+ * processed.
+ **/
+void
+soup_message_finished (SoupMessage *msg)
+{
+       g_signal_emit (msg, signals[FINISHED], 0);
+}
 
-       return NULL;
+static void
+header_handler_free (gpointer header_name, GClosure *closure)
+{
+       g_free (header_name);
 }
 
-/**
- * soup_message_queue:
- * @req: a %SoupMessage.
- * @callback: a %SoupCallbackFn which will be called after the message completes
- * or when an unrecoverable error occurs.
- * @user_data: a pointer passed to @callback.
- * 
- * Queues the message @req for sending. All messages are processed while the
- * glib main loop runs. If this %SoupMessage has been processed before, any
- * resources related to the time it was last sent are freed.
- *
- * If the response %SoupDataBuffer has an owner of %SOUP_BUFFER_USER_OWNED, the
- * message will not be queued, and @callback will be called with a
- * %SoupErrorCode of %SOUP_ERROR_CANCELLED.
- *
- * Upon message completetion, the callback specified in @callback will be
- * invoked. If after returning from this callback the message has not been
- * requeued using %soup_message_queue, %soup_message_free will be called on
- * @req.
- */
-void 
-soup_message_queue (SoupMessage    *req,
-                   SoupCallbackFn  callback, 
-                   gpointer        user_data)
+static void
+header_handler_metamarshal (GClosure *closure, GValue *return_value,
+                           guint n_param_values, const GValue *param_values,
+                           gpointer invocation_hint, gpointer marshal_data)
 {
-       soup_queue_message (req, callback, user_data);
+       SoupMessage *msg = g_value_get_object (&param_values[0]);
+       SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+       const char *header_name = marshal_data;
+       SoupMessageHeaders *hdrs;
+
+       if (priv->io_status != SOUP_MESSAGE_IO_STATUS_RUNNING)
+               return;
+
+       hdrs = priv->server_side ? msg->request_headers : msg->response_headers;
+       if (soup_message_headers_get (hdrs, header_name)) {
+               closure->marshal (closure, return_value, n_param_values,
+                                 param_values, invocation_hint,
+                                 ((GCClosure *)closure)->callback);
+       }
 }
 
 /**
- * soup_message_send:
- * @msg: a %SoupMessage.
- * 
- * Syncronously send @msg. This call will not return until the transfer is
- * finished successfully or there is an unrecoverable error. 
+ * soup_message_add_header_handler:
+ * @msg: a #SoupMessage
+ * @signal: signal to connect the handler to.
+ * @header: HTTP response header to match against
+ * @callback: the header handler
+ * @user_data: data to pass to @handler_cb
  *
- * @msg is not free'd upon return.
+ * Adds a signal handler to @msg for @signal, as with
+ * g_signal_connect(), but with two differences: the @callback will
+ * only be run if @msg has a header named @header, and it will only be
+ * run if no earlier handler cancelled or requeued the message.
  *
- * Return value: the %SoupErrorClass of the error encountered while sending or
- * reading the response.
- */
-SoupErrorClass
-soup_message_send (SoupMessage *msg)
+ * If @signal is one of the "got" signals (eg, "got_headers"), or
+ * "finished" or "restarted", then @header is matched against the
+ * incoming message headers (that is, the #request_headers for a
+ * client #SoupMessage, or the #response_headers for a server
+ * #SoupMessage). If @signal is one of the "wrote" signals, then
+ * @header is matched against the outgoing message headers.
+ *
+ * Return value: the handler ID from g_signal_connect()
+ **/
+guint
+soup_message_add_header_handler (SoupMessage *msg,
+                                const char  *signal,
+                                const char  *header,
+                                GCallback    callback,
+                                gpointer     user_data)
 {
-       soup_message_queue (msg, NULL, NULL);
+       SoupMessagePrivate *priv;
+       GClosure *closure;
+       char *header_name;
 
-       while (1) {
-               g_main_iteration (TRUE); 
-               if (msg->status == SOUP_STATUS_FINISHED || 
-                   SOUP_ERROR_IS_TRANSPORT (msg->errorcode))
-                       break;
-       }
+       g_return_val_if_fail (SOUP_IS_MESSAGE (msg), 0);
+       g_return_val_if_fail (signal != NULL, 0);
+       g_return_val_if_fail (header != NULL, 0);
+       g_return_val_if_fail (callback != NULL, 0);
 
-       return msg->errorclass;
-}
+       priv = SOUP_MESSAGE_GET_PRIVATE (msg);
 
-static void 
-authorize_handler (SoupMessage *msg, gboolean proxy)
-{
-       const char *auth_header;
-       SoupAuth *auth;
-       SoupContext *ctx;
+       closure = g_cclosure_new (callback, user_data, NULL);
 
-       ctx = proxy ? soup_get_proxy () : msg->context;
+       header_name = g_strdup (header);
+       g_closure_set_meta_marshal (closure, header_name,
+                                   header_handler_metamarshal);
+       g_closure_add_finalize_notifier (closure, header_name,
+                                        header_handler_free);
 
-       if (!soup_context_get_uri (ctx)->user) 
-               goto THROW_CANT_AUTHENTICATE;
+       return g_signal_connect_closure (msg, signal, closure, FALSE);
+}
 
-       auth_header = 
-               soup_message_get_response_header (
-                       msg, 
-                       proxy ? "Proxy-Authenticate" : "WWW-Authenticate");
-       if (!auth_header) goto THROW_CANT_AUTHENTICATE;
+static void
+status_handler_metamarshal (GClosure *closure, GValue *return_value,
+                           guint n_param_values, const GValue *param_values,
+                           gpointer invocation_hint, gpointer marshal_data)
+{
+       SoupMessage *msg = g_value_get_object (&param_values[0]);
+       SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+       guint status = GPOINTER_TO_UINT (marshal_data);
 
-        auth = soup_auth_new_from_header (ctx, auth_header);
-       if (!auth) {
-               soup_message_set_error_full (
-                       msg, 
-                       proxy ? 
-                               SOUP_ERROR_CANT_AUTHENTICATE_PROXY : 
-                               SOUP_ERROR_CANT_AUTHENTICATE,
-                       proxy ? 
-                               "Unknown authentication scheme "
-                               "required by proxy" :
-                               "Unknown authentication scheme "
-                               "required");
+       if (priv->io_status != SOUP_MESSAGE_IO_STATUS_RUNNING)
                return;
-       }
 
-       if (ctx->auth) {
-               if (soup_auth_invalidates_prior (auth, ctx->auth))
-                       soup_auth_free (ctx->auth);
-               else {
-                       soup_auth_free (auth);
-                       goto THROW_CANT_AUTHENTICATE;
-               }
+       if (msg->status_code == status) {
+               closure->marshal (closure, return_value, n_param_values,
+                                 param_values, invocation_hint,
+                                 ((GCClosure *)closure)->callback);
        }
+}
 
-       ctx->auth = auth;
+/**
+ * soup_message_add_status_code_handler:
+ * @msg: a #SoupMessage
+ * @signal: signal to connect the handler to.
+ * @status_code: status code to match against
+ * @callback: the header handler
+ * @user_data: data to pass to @handler_cb
+ *
+ * Adds a signal handler to @msg for @signal, as with
+ * g_signal_connect() but with two differences: the @callback will
+ * only be run if @msg has the status @status_code, and it will only
+ * be run if no earlier handler cancelled or requeued the message.
+ *
+ * @signal must be a signal that will be emitted after @msg's status
+ * is set. For a client #SoupMessage, this means it can't be a "wrote"
+ * signal. For a server #SoupMessage, this means it can't be a "got"
+ * signal.
+ *
+ * Return value: the handler ID from g_signal_connect()
+ **/
+guint
+soup_message_add_status_code_handler (SoupMessage *msg,
+                                     const char  *signal,
+                                     guint        status_code,
+                                     GCallback    callback,
+                                     gpointer     user_data)
+{
+       GClosure *closure;
 
-       soup_message_queue (msg, msg->priv->callback, msg->priv->user_data);
+       g_return_val_if_fail (SOUP_IS_MESSAGE (msg), 0);
+       g_return_val_if_fail (signal != NULL, 0);
+       g_return_val_if_fail (callback != NULL, 0);
 
-        return;
+       closure = g_cclosure_new (callback, user_data, NULL);
+       g_closure_set_meta_marshal (closure, GUINT_TO_POINTER (status_code),
+                                   status_handler_metamarshal);
 
- THROW_CANT_AUTHENTICATE:
-       soup_message_set_error (msg, 
-                               proxy ? 
-                                       SOUP_ERROR_CANT_AUTHENTICATE_PROXY : 
-                                       SOUP_ERROR_CANT_AUTHENTICATE);
+       return g_signal_connect_closure (msg, signal, closure, FALSE);
 }
 
-static void 
-redirect_handler (SoupMessage *msg, gpointer user_data)
-{
-       const gchar *new_url;
 
-       if (msg->errorclass != SOUP_ERROR_CLASS_REDIRECT || 
-           msg->priv->msg_flags & SOUP_MESSAGE_NO_REDIRECT) return;
+/**
+ * soup_message_set_auth:
+ * @msg: a #SoupMessage
+ * @auth: a #SoupAuth, or %NULL
+ *
+ * Sets @msg to authenticate to its destination using @auth, which
+ * must have already been fully authenticated. If @auth is %NULL, @msg
+ * will not authenticate to its destination.
+ **/
+void
+soup_message_set_auth (SoupMessage *msg, SoupAuth *auth)
+{
+       SoupMessagePrivate *priv;
+       char *token;
 
-       new_url = soup_message_get_response_header (msg, "Location");
+       g_return_if_fail (SOUP_IS_MESSAGE (msg));
+       g_return_if_fail (auth == NULL || SOUP_IS_AUTH (auth));
+       g_return_if_fail (auth == NULL || soup_auth_is_authenticated (auth));
 
-       if (new_url) {
-               SoupContext *new_ctx, *old_ctx;
+       priv = SOUP_MESSAGE_GET_PRIVATE (msg);
 
-               new_ctx = soup_context_get (new_url);
-               if (!new_ctx) {
-                       soup_message_set_error_full (msg, 
-                                                    SOUP_ERROR_MALFORMED,
-                                                    "Invalid Redirect URL");
-                       return;
-               }
+       if (priv->auth) {
+               g_object_unref (priv->auth);
+               soup_message_headers_remove (msg->request_headers,
+                                            "Authorization");
+       }
+       priv->auth = auth;
+       if (!priv->auth)
+               return;
 
-               old_ctx = msg->context;
-               msg->context = new_ctx;
+       g_object_ref (priv->auth);
+       token = soup_auth_get_authorization (auth, msg);
+       soup_message_headers_replace (msg->request_headers,
+                                     "Authorization", token);
+       g_free (token);
+}
 
-               soup_message_queue (msg,
-                                   msg->priv->callback, 
-                                   msg->priv->user_data);
+/**
+ * soup_message_get_auth:
+ * @msg: a #SoupMessage
+ *
+ * Gets the #SoupAuth used by @msg for authentication.
+ *
+ * Return value: the #SoupAuth used by @msg for authentication, or
+ * %NULL if @msg is unauthenticated.
+ **/
+SoupAuth *
+soup_message_get_auth (SoupMessage *msg)
+{
+       g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
 
-               soup_context_unref (old_ctx);
-       }
+       return SOUP_MESSAGE_GET_PRIVATE (msg)->auth;
 }
 
-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 gchar      *header;
-       } data;
-} SoupHandlerData;
-
-static SoupHandlerData global_handlers [] = {
-       /* 
-        * 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 }
-       },
-       /* 
-        * Handle redirect response codes 300, 301, 302, 303, and 305.
-        */
-       {
-               SOUP_HANDLER_PRE_BODY,
-               redirect_handler, 
-               NULL, 
-               RESPONSE_HEADER_HANDLER, 
-               { (guint) "Location" }
-       },
-       { 0 }
-};
-
-static inline void 
-run_handler (SoupMessage     *msg, 
-            SoupHandlerType  invoke_type, 
-            SoupHandlerData *data)
+/**
+ * soup_message_set_proxy_auth:
+ * @msg: a #SoupMessage
+ * @auth: a #SoupAuth, or %NULL
+ *
+ * Sets @msg to authenticate to its proxy using @auth, which must have
+ * already been fully authenticated. If @auth is %NULL, @msg will not
+ * authenticate to its proxy.
+ **/
+void
+soup_message_set_proxy_auth (SoupMessage *msg, SoupAuth *auth)
 {
-       if (data->type != invoke_type) return;
+       SoupMessagePrivate *priv;
+       char *token;
 
-       switch (data->kind) {
-       case RESPONSE_HEADER_HANDLER:
-               if (!soup_message_get_response_header (msg,
-                                                      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;
+       g_return_if_fail (SOUP_IS_MESSAGE (msg));
+       g_return_if_fail (auth == NULL || SOUP_IS_AUTH (auth));
+       g_return_if_fail (auth == NULL || soup_auth_is_authenticated (auth));
+
+       priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+
+       if (priv->proxy_auth) {
+               g_object_unref (priv->proxy_auth);
+               soup_message_headers_remove (msg->request_headers,
+                                            "Proxy-Authorization");
        }
+       priv->proxy_auth = auth;
+       if (!priv->proxy_auth)
+               return;
 
-       (*data->handler_cb) (msg, data->user_data);
+       g_object_ref (priv->proxy_auth);
+       token = soup_auth_get_authorization (auth, msg);
+       soup_message_headers_replace (msg->request_headers,
+                                     "Proxy-Authorization", token);
+       g_free (token);
 }
 
-/*
- * 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. 
+/**
+ * soup_message_get_proxy_auth:
+ * @msg: a #SoupMessage
  *
- * After running all handlers, if there is an error set or the invoke type was
- * post_body, issue the final callback.  
+ * Gets the #SoupAuth used by @msg for authentication to its proxy..
  *
- * FIXME: If the errorcode is changed by a handler, we should restart the
- * processing.  
- */
-gboolean
-soup_message_run_handlers (SoupMessage *msg, SoupHandlerType invoke_type)
+ * Return value: the #SoupAuth used by @msg for authentication to its
+ * proxy, or %NULL if @msg isn't authenticated to its proxy.
+ **/
+SoupAuth *
+soup_message_get_proxy_auth (SoupMessage *msg)
 {
-       GSList *list;
-       SoupHandlerData *data;
+       g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
 
-       g_return_val_if_fail (msg != NULL, FALSE);
+       return SOUP_MESSAGE_GET_PRIVATE (msg)->proxy_auth;
+}
 
-       for (list = msg->priv->content_handlers; list; list = list->next) {
-               data = list->data;
+/**
+ * soup_message_cleanup_response:
+ * @req: a #SoupMessage
+ *
+ * Cleans up all response data on @req, so that the request can be sent
+ * again and receive a new response. (Eg, as a result of a redirect or
+ * authorization request.)
+ **/
+void
+soup_message_cleanup_response (SoupMessage *req)
+{
+       SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (req);
 
-               run_handler (msg, invoke_type, data);
+       soup_message_body_truncate (req->response_body);
+       soup_message_headers_clear (req->response_headers);
 
-               if (msg->status == SOUP_STATUS_QUEUED) return TRUE;
+       req->status_code = SOUP_STATUS_NONE;
+       if (req->reason_phrase) {
+               g_free (req->reason_phrase);
+               req->reason_phrase = NULL;
        }
+       priv->http_version = priv->orig_http_version;
 
-       for (data = global_handlers; data->type; data++) {
-               run_handler (msg, invoke_type, data);
+       g_object_notify (G_OBJECT (req), SOUP_MESSAGE_STATUS_CODE);
+       g_object_notify (G_OBJECT (req), SOUP_MESSAGE_REASON_PHRASE);
+       g_object_notify (G_OBJECT (req), SOUP_MESSAGE_HTTP_VERSION);
+}
 
-               if (msg->status == SOUP_STATUS_QUEUED) return TRUE;
-       }
+/**
+ * SoupMessageFlags:
+ * @SOUP_MESSAGE_NO_REDIRECT: The session should not follow redirect
+ * (3xx) responses received by this message.
+ * @SOUP_MESSAGE_OVERWRITE_CHUNKS: Deprecated: equivalent to calling
+ * soup_message_body_set_accumulate() on the incoming message body
+ * (ie, %response_body for a client-side request), passing %FALSE.
+ *
+ * Various flags that can be set on a #SoupMessage to alter its
+ * behavior.
+ **/
 
-       /*
-        * 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;
+/**
+ * soup_message_set_flags:
+ * @msg: a #SoupMessage
+ * @flags: a set of #SoupMessageFlags values
+ *
+ * Sets the specified flags on @msg.
+ **/
+void
+soup_message_set_flags (SoupMessage *msg, SoupMessageFlags flags)
+{
+       SoupMessagePrivate *priv;
+
+       g_return_if_fail (SOUP_IS_MESSAGE (msg));
+       priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+
+       if ((priv->msg_flags ^ flags) & SOUP_MESSAGE_OVERWRITE_CHUNKS) {
+               soup_message_body_set_accumulate (
+                       priv->server_side ? msg->request_body : msg->response_body,
+                       !(flags & SOUP_MESSAGE_OVERWRITE_CHUNKS));
        }
 
-       return FALSE;
+       priv->msg_flags = flags;
+       g_object_notify (G_OBJECT (msg), SOUP_MESSAGE_FLAGS);
 }
 
-static void 
-add_handler (SoupMessage      *msg,
-            SoupHandlerType   type,
-            SoupCallbackFn    handler_cb,
-            gpointer          user_data,
-            SoupHandlerKind   kind,
-            const gchar      *header,
-            guint             errorcode,
-            guint             errorclass)
+/**
+ * soup_message_get_flags:
+ * @msg: a #SoupMessage
+ *
+ * Gets the flags on @msg
+ *
+ * Return value: the flags
+ **/
+SoupMessageFlags
+soup_message_get_flags (SoupMessage *msg)
 {
-       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;
-       }
+       g_return_val_if_fail (SOUP_IS_MESSAGE (msg), 0);
 
-       msg->priv->content_handlers = 
-               g_slist_append (msg->priv->content_handlers, data);
-}
-
-void 
-soup_message_add_header_handler (SoupMessage      *msg,
-                                const gchar      *header,
-                                SoupHandlerType   type,
-                                SoupCallbackFn    handler_cb,
-                                gpointer          user_data)
-{
-       g_return_if_fail (msg != NULL);
-       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 (msg != NULL);
-       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 (msg != NULL);
-       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 (msg != NULL);
-       g_return_if_fail (handler_cb != NULL);
-
-       add_handler (msg, 
-                    type, 
-                    handler_cb, 
-                    user_data, 
-                    0, 
-                    NULL, 
-                    0,
-                    0);
+       return SOUP_MESSAGE_GET_PRIVATE (msg)->msg_flags;
 }
 
+/**
+ * SoupHTTPVersion:
+ * @SOUP_HTTP_1_0: HTTP 1.0 (RFC 1945)
+ * @SOUP_HTTP_1_1: HTTP 1.1 (RFC 2616)
+ *
+ * Indicates the HTTP protocol version being used.
+ **/
+
+/**
+ * soup_message_set_http_version:
+ * @msg: a #SoupMessage
+ * @version: the HTTP version
+ *
+ * Sets the HTTP version on @msg. The default version is
+ * %SOUP_HTTP_1_1. Setting it to %SOUP_HTTP_1_0 will prevent certain
+ * functionality from being used.
+ **/
 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;
-       }
+soup_message_set_http_version (SoupMessage *msg, SoupHTTPVersion version)
+{
+       SoupMessagePrivate *priv;
+
+       g_return_if_fail (SOUP_IS_MESSAGE (msg));
+       priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+
+       priv->http_version = version;
+       if (msg->status_code == SOUP_STATUS_NONE)
+               priv->orig_http_version = version;
+       g_object_notify (G_OBJECT (msg), SOUP_MESSAGE_HTTP_VERSION);
 }
 
-static inline gboolean
-ADDED_FLAG (SoupMessage *msg, guint newflags, SoupMessageFlags find)
+/**
+ * soup_message_get_http_version:
+ * @msg: a #SoupMessage
+ *
+ * Gets the HTTP version of @msg. This is the minimum of the
+ * version from the request and the version from the response.
+ *
+ * Return value: the HTTP version
+ **/
+SoupHTTPVersion
+soup_message_get_http_version (SoupMessage *msg)
 {
-       return ((newflags & find) && !(msg->priv->msg_flags & find));
+       g_return_val_if_fail (SOUP_IS_MESSAGE (msg), SOUP_HTTP_1_0);
+
+       return SOUP_MESSAGE_GET_PRIVATE (msg)->http_version;
 }
 
-static inline gboolean
-REMOVED_FLAG (SoupMessage *msg, guint newflags, SoupMessageFlags find)
+/**
+ * soup_message_is_keepalive:
+ * @msg: a #SoupMessage
+ *
+ * Determines whether or not @msg's connection can be kept alive for
+ * further requests after processing @msg, based on the HTTP version,
+ * Connection header, etc.
+ *
+ * Return value: %TRUE or %FALSE.
+ **/
+gboolean
+soup_message_is_keepalive (SoupMessage *msg)
 {
-       return (!(newflags & find) && (msg->priv->msg_flags & find));
+       const char *c_conn, *s_conn;
+
+       c_conn = soup_message_headers_get (msg->request_headers, "Connection");
+       s_conn = soup_message_headers_get (msg->response_headers, "Connection");
+
+       if (msg->status_code == SOUP_STATUS_OK &&
+           msg->method == SOUP_METHOD_CONNECT)
+               return TRUE;
+
+       if (SOUP_MESSAGE_GET_PRIVATE (msg)->http_version == SOUP_HTTP_1_0) {
+               /* Only persistent if the client requested keepalive
+                * and the server agreed.
+                */
+
+               if (!c_conn || !s_conn)
+                       return FALSE;
+               if (soup_header_contains (c_conn, "Keep-Alive") ||
+                   soup_header_contains (s_conn, "Keep-Alive"))
+                       return FALSE;
+
+               return TRUE;
+       } else {
+               /* Normally persistent unless either side requested otherwise */
+               if (c_conn && soup_header_contains (c_conn, "close"))
+                       return FALSE;
+               if (s_conn && soup_header_contains (s_conn, "close"))
+                       return FALSE;
+
+               /* But not if the server sent a terminate-by-EOF response */
+               if (soup_message_headers_get_encoding (msg->response_headers) == SOUP_ENCODING_EOF)
+                       return FALSE;
+
+               return TRUE;
+       }
 }
 
+/**
+ * soup_message_set_uri:
+ * @msg: a #SoupMessage
+ * @uri: the new #SoupURI
+ *
+ * Sets @msg's URI to @uri. If @msg has already been sent and you want
+ * to re-send it with the new URI, you need to call
+ * soup_session_requeue_message().
+ **/
 void
-soup_message_set_flags (SoupMessage *msg, guint flags)
+soup_message_set_uri (SoupMessage *msg, SoupURI *uri)
 {
-       g_return_if_fail (msg != NULL);
+       SoupMessagePrivate *priv;
+
+       g_return_if_fail (SOUP_IS_MESSAGE (msg));
+       priv = SOUP_MESSAGE_GET_PRIVATE (msg);
 
-       msg->priv->msg_flags = flags;
+       if (priv->uri)
+               soup_uri_free (priv->uri);
+       if (priv->addr) {
+               g_object_unref (priv->addr);
+               priv->addr = NULL;
+       }
+       priv->uri = soup_uri_copy (uri);
+
+       g_object_notify (G_OBJECT (msg), SOUP_MESSAGE_URI);
 }
 
-guint
-soup_message_get_flags (SoupMessage *msg)
+/**
+ * soup_message_get_uri:
+ * @msg: a #SoupMessage
+ *
+ * Gets @msg's URI
+ *
+ * Return value: the URI @msg is targeted for.
+ **/
+SoupURI *
+soup_message_get_uri (SoupMessage *msg)
 {
-       g_return_val_if_fail (msg != NULL, 0);
+       g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
 
-       return msg->priv->msg_flags;
+       return SOUP_MESSAGE_GET_PRIVATE (msg)->uri;
 }
 
-void 
-soup_message_set_http_version  (SoupMessage *msg, SoupHttpVersion version)
+/**
+ * soup_message_get_address:
+ * @msg: a #SoupMessage
+ *
+ * Gets the address @msg's URI points to. After first setting the
+ * URI on a message, this will be unresolved, although the message's
+ * session will resolve it before sending the message.
+ *
+ * Return value: the address @msg's URI points to
+ **/
+SoupAddress *
+soup_message_get_address (SoupMessage *msg)
 {
-       g_return_if_fail (msg != NULL);
+       SoupMessagePrivate *priv;
 
-       msg->priv->http_version = version;
+       g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
+
+       priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+       if (!priv->addr) {
+               priv->addr = soup_address_new (priv->uri->host,
+                                              priv->uri->port);
+       }
+       return priv->addr;
 }
 
+/**
+ * soup_message_set_status:
+ * @msg: a #SoupMessage
+ * @status_code: an HTTP status code
+ *
+ * Sets @msg's status code to @status_code. If @status_code is a
+ * known value, it will also set @msg's reason_phrase.
+ **/
 void
-soup_message_set_error (SoupMessage *msg, SoupKnownErrorCode errcode)
+soup_message_set_status (SoupMessage *msg, guint status_code)
 {
-       g_return_if_fail (msg != NULL);
-       g_return_if_fail (errcode != 0);
+       g_return_if_fail (SOUP_IS_MESSAGE (msg));
+       g_return_if_fail (status_code != 0);
 
-       g_free ((gchar *) msg->errorphrase);
+       g_free (msg->reason_phrase);
 
-       msg->errorcode = errcode;
-       msg->errorclass = soup_get_error_class (errcode);
-       msg->errorphrase = g_strdup (soup_get_error_phrase (errcode));
+       msg->status_code = status_code;
+       msg->reason_phrase = g_strdup (soup_status_get_phrase (status_code));
+       g_object_notify (G_OBJECT (msg), SOUP_MESSAGE_STATUS_CODE);
+       g_object_notify (G_OBJECT (msg), SOUP_MESSAGE_REASON_PHRASE);
 }
 
+/**
+ * soup_message_set_status_full:
+ * @msg: a #SoupMessage
+ * @status_code: an HTTP status code
+ * @reason_phrase: a description of the status
+ *
+ * Sets @msg's status code and reason phrase.
+ **/
 void
-soup_message_set_error_full (SoupMessage *msg, 
-                            guint        errcode, 
-                            const gchar *errphrase)
+soup_message_set_status_full (SoupMessage *msg,
+                             guint        status_code,
+                             const char  *reason_phrase)
 {
-       g_return_if_fail (msg != NULL);
-       g_return_if_fail (errcode != 0);
-       g_return_if_fail (errphrase != NULL);
+       g_return_if_fail (SOUP_IS_MESSAGE (msg));
+       g_return_if_fail (status_code != 0);
+       g_return_if_fail (reason_phrase != NULL);
 
-       g_free ((gchar *) msg->errorphrase);
+       g_free (msg->reason_phrase);
 
-       msg->errorcode = errcode;
-       msg->errorclass = soup_get_error_class (errcode);
-       msg->errorphrase = g_strdup (errphrase);
+       msg->status_code = status_code;
+       msg->reason_phrase = g_strdup (reason_phrase);
+       g_object_notify (G_OBJECT (msg), SOUP_MESSAGE_STATUS_CODE);
+       g_object_notify (G_OBJECT (msg), SOUP_MESSAGE_REASON_PHRASE);
 }
 
 void
-soup_message_set_handler_error (SoupMessage *msg, 
-                               guint        errcode, 
-                               const gchar *errphrase)
-{
-       g_return_if_fail (msg != NULL);
-       g_return_if_fail (errcode != 0);
-       g_return_if_fail (errphrase != NULL);
-
-       g_free ((gchar *) msg->errorphrase);
-
-       msg->errorcode = errcode;
-       msg->errorclass = SOUP_ERROR_CLASS_HANDLER;
-       msg->errorphrase = g_strdup (errphrase);
-}
-
-struct {
-       guint sc;
-       const gchar *phrase;
-} error_code_phrases [] = {
-       /* 
-        * SOUP_ERROR_CLASS_TRANSPORT 
-        */
-       { SOUP_ERROR_CANCELLED,               "Cancelled" },
-       { SOUP_ERROR_CANT_CONNECT,            "Cannot connect to destination" },
-       { SOUP_ERROR_CANT_CONNECT_PROXY,      "Cannot connect to proxy" },
-       { SOUP_ERROR_IO,                      "Connection terminated "
-                                             "unexpectadly" },
-       { SOUP_ERROR_MALFORMED,               "Message Corrupt" },
-       { SOUP_ERROR_CANT_AUTHENTICATE,       "Authentication Failed" },
-       { SOUP_ERROR_CANT_AUTHENTICATE_PROXY, "Proxy Authentication Failed" },
-
-       /* 
-        * SOUP_ERROR_CLASS_INFORMATIONAL 
-        */
-       { SOUP_ERROR_CONTINUE,        "Continue" },
-       { SOUP_ERROR_PROTOCOL_SWITCH, "Protocol Switch" },
-       { SOUP_ERROR_DAV_PROCESSING,  "Processing" },
-
-       /* 
-        * SOUP_ERROR_CLASS_SUCCESS 
-        */
-       { SOUP_ERROR_OK,                "OK" },
-       { SOUP_ERROR_CREATED,           "Created" },
-       { SOUP_ERROR_ACCEPTED,          "Accepted" },
-       { SOUP_ERROR_NON_AUTHORITATIVE, "Non-Authoritative" },
-       { SOUP_ERROR_NO_CONTENT,        "No Content" },
-       { SOUP_ERROR_RESET_CONTENT,     "Reset Content" },
-       { SOUP_ERROR_PARTIAL_CONTENT,   "Partial Content" },
-       { SOUP_ERROR_DAV_MULTISTATUS,   "Multi-Status" },
-
-       /* 
-        * SOUP_ERROR_CLASS_REDIRECT 
-        */
-       { SOUP_ERROR_MULTIPLE_CHOICES,   "Multiple Choices" },
-       { SOUP_ERROR_MOVED_PERMANANTLY,  "Moved Permanantly" },
-       { SOUP_ERROR_FOUND,              "Found" },
-       { SOUP_ERROR_SEE_OTHER,          "See Other" },
-       { SOUP_ERROR_NOT_MODIFIED,       "Not Modified" },
-       { SOUP_ERROR_USE_PROXY,          "Use Proxy" },
-       { SOUP_ERROR_TEMPORARY_REDIRECT, "Temporary Redirect" },
-
-       /* 
-        * SOUP_ERROR_CLASS_CLIENT_ERROR 
-        */
-       { SOUP_ERROR_BAD_REQUEST,           "Bad Request" },
-       { SOUP_ERROR_UNAUTHORIZED,          "Unauthorized" },
-       { SOUP_ERROR_PAYMENT_REQUIRED,      "Payment Required" },
-       { SOUP_ERROR_FORBIDDEN,             "Forbidden" },
-       { SOUP_ERROR_NOT_FOUND,             "Not Found" },
-       { SOUP_ERROR_METHOD_NOT_ALLOWED,    "Method Not Allowed" },
-       { SOUP_ERROR_NOT_ACCEPTABLE,        "Not Acceptable" },
-       { SOUP_ERROR_PROXY_UNAUTHORIZED,    "Proxy Unauthorized" },
-       { SOUP_ERROR_TIMED_OUT,             "Timed Out" },
-       { SOUP_ERROR_CONFLICT,              "Conflict" },
-       { SOUP_ERROR_GONE,                  "Gone" },
-       { SOUP_ERROR_LENGTH_REQUIRED,       "Length Required" },
-       { SOUP_ERROR_PRECONDITION_FAILED,   "Precondition Failed" },
-       { SOUP_ERROR_BODY_TOO_LARGE,        "Entity Body Too Large" },
-       { SOUP_ERROR_URI_TOO_LARGE,         "Request-URI Too Large" },
-       { SOUP_ERROR_UNKNOWN_MEDIA_TYPE,    "Unknown Media Type" },
-       { SOUP_ERROR_INVALID_RANGE,         "Invalid Range" },
-       { SOUP_ERROR_EXPECTATION_FAILED,    "Expectation Failed" },
-       { SOUP_ERROR_DAV_UNPROCESSABLE,     "Unprocessable Entity" },
-       { SOUP_ERROR_DAV_LOCKED,            "Locked" },
-       { SOUP_ERROR_DAV_DEPENDENCY_FAILED, "Dependency Failed" },
-
-       /* 
-        * SOUP_ERROR_CLASS_SERVER_ERROR 
-        */
-       { SOUP_ERROR_INTERNAL,            "Internal Server Error" },
-       { SOUP_ERROR_NOT_IMPLEMENTED,     "Not Implemented" },
-       { SOUP_ERROR_BAD_GATEWAY,         "Bad Gateway" },
-       { SOUP_ERROR_SERVICE_UNAVAILABLE, "Service Unavailable" },
-       { SOUP_ERROR_GATEWAY_TIMEOUT,     "Gateway Timeout" },
-       { SOUP_ERROR_VERSION_UNSUPPORTED, "Version Unsupported" },
-       { SOUP_ERROR_DAV_OUT_OF_SPACE,    "Out Of Space" },
-
-       { 0 }
-};
-
-const gchar *
-soup_get_error_phrase (SoupKnownErrorCode errcode)
+soup_message_set_io_status (SoupMessage          *msg,
+                           SoupMessageIOStatus   status)
 {
-       gint i;
+       SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
 
-       for (i = 0; error_code_phrases [i].sc; i++) {
-               if (error_code_phrases [i].sc == (guint) errcode)
-                       return error_code_phrases [i].phrase;
-       }
+       priv->io_status = status;
+}
+
+SoupMessageIOStatus
+soup_message_get_io_status (SoupMessage *msg)
+{
+       SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
 
-       return "Unknown Error";
+       return priv->io_status;
 }
 
-SoupErrorClass
-soup_get_error_class (SoupKnownErrorCode errcode)
+/**
+ * SoupChunkAllocator:
+ * @msg: the #SoupMessage the chunk is being allocated for
+ * @max_len: the maximum length that will be read, or 0.
+ * @user_data: the data passed to soup_message_set_chunk_allocator()
+ *
+ * The prototype for a chunk allocation callback. This should allocate
+ * a new #SoupBuffer and return it for the I/O layer to read message
+ * body data off the network into.
+ *
+ * If @max_len is non-0, it indicates the maximum number of bytes that
+ * could be read, based on what is known about the message size. Note
+ * that this might be a very large number, and you should not simply
+ * try to allocate that many bytes blindly. If @max_len is 0, that
+ * means that libsoup does not know how many bytes remain to be read,
+ * and the allocator should return a buffer of a size that it finds
+ * convenient.
+ *
+ * If the allocator returns %NULL, the message will be paused. It is
+ * up to the application to make sure that it gets unpaused when it
+ * becomes possible to allocate a new buffer.
+ *
+ * Return value: the new buffer (or %NULL)
+ **/
+
+/**
+ * soup_message_set_chunk_allocator:
+ * @msg: a #SoupMessage
+ * @allocator: the chunk allocator callback
+ * @user_data: data to pass to @allocator
+ * @destroy_notify: destroy notifier to free @user_data when @msg is
+ * destroyed
+ *
+ * Sets an alternate chunk-allocation function to use when reading
+ * @msg's body. Every time data is available to read, libsoup will
+ * call @allocator, which should return a #SoupBuffer. (See
+ * #SoupChunkAllocator for additional details.) Libsoup will then read
+ * data from the network into that buffer, and update the buffer's
+ * %length to indicate how much data it read.
+ *
+ * Generally, a custom chunk allocator would be used in conjunction
+ * with soup_message_body_set_accumulate() %FALSE and
+ * #SoupMessage::got_chunk, as part of a strategy to avoid unnecessary
+ * copying of data. However, you cannot assume that every call to the
+ * allocator will be followed by a call to your %got_chunk handler; if
+ * an I/O error occurs, then the buffer will be unreffed without ever
+ * having been used. If your buffer-allocation strategy requires
+ * special cleanup, use soup_buffer_new_with_owner() rather than doing
+ * the cleanup from the %got_chunk handler.
+ *
+ * The other thing to remember when using non-accumulating message
+ * bodies is that the buffer passed to the %got_chunk handler will be
+ * unreffed after the handler returns, just as it would be in the
+ * non-custom-allocated case. If you want to hand the chunk data off
+ * to some other part of your program to use later, you'll need to ref
+ * the #SoupBuffer (or its owner, in the soup_buffer_new_with_owner()
+ * case) to ensure that the data remains valid.
+ **/
+void
+soup_message_set_chunk_allocator (SoupMessage *msg,
+                                 SoupChunkAllocator allocator,
+                                 gpointer user_data,
+                                 GDestroyNotify destroy_notify)
 {
-       if (errcode < 100) return SOUP_ERROR_CLASS_TRANSPORT;
-       if (errcode < 200) return SOUP_ERROR_CLASS_INFORMATIONAL;
-       if (errcode < 300) return SOUP_ERROR_CLASS_SUCCESS;
-       if (errcode < 400) return SOUP_ERROR_CLASS_REDIRECT;
-       if (errcode < 500) return SOUP_ERROR_CLASS_CLIENT_ERROR;
-       if (errcode < 600) return SOUP_ERROR_CLASS_SERVER_ERROR;
-       return SOUP_ERROR_CLASS_UNKNOWN;
+       SoupMessagePrivate *priv;
+
+       g_return_if_fail (SOUP_IS_MESSAGE (msg));
+
+       priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+
+       if (priv->chunk_allocator_dnotify)
+               priv->chunk_allocator_dnotify (priv->chunk_allocator_data);
+
+       priv->chunk_allocator         = allocator;
+       priv->chunk_allocator_data    = user_data;
+       priv->chunk_allocator_dnotify = destroy_notify;
 }