Higher-than-soup-message-io-level functions to do client-side IO. (Code
authorDan Winship <danw@src.gnome.org>
Tue, 26 Aug 2003 20:09:59 +0000 (20:09 +0000)
committerDan Winship <danw@src.gnome.org>
Tue, 26 Aug 2003 20:09:59 +0000 (20:09 +0000)
* libsoup/soup-message-client-io.c (soup_message_write_request,
soup_message_read_response): Higher-than-soup-message-io-level
functions to do client-side IO. (Code that used to be in
soup-queue.c)
(get_request_header_cb): Fix a bug in the generation of the Host:
header; need to include the port number if it's not the default.

* libsoup/soup-message-io.c (soup_message_write,
soup_message_write_simple): Take separate user_datas for the get_*
callbacks and the done callbacks.

* libsoup/soup-queue.c: Update to use soup_message_write_request
and soup_message_read_response.

* libsoup/soup-connection.c (soup_connection_new): Change the
prototype to take a SoupUri and a callback.

* libsoup/soup-context.c (try_create_connection,
soup_context_connect_cb): Update for soup_connection_new change.

* libsoup/soup-server.c (read_done_cb, issue_bad_request): Update
for soup_message_write changes

* libsoup/soup-uri.c (soup_uri_uses_default_port): new utility
function

12 files changed:
ChangeLog
libsoup/Makefile.am
libsoup/soup-connection.c
libsoup/soup-connection.h
libsoup/soup-context.c
libsoup/soup-message-client-io.c [new file with mode: 0644]
libsoup/soup-message-io.c
libsoup/soup-message-private.h
libsoup/soup-queue.c
libsoup/soup-server.c
libsoup/soup-uri.c
libsoup/soup-uri.h

index b0b16e6..52788d9 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,33 @@
 2003-08-26  Dan Winship  <danw@ximian.com>
 
+       * libsoup/soup-message-client-io.c (soup_message_write_request,
+       soup_message_read_response): Higher-than-soup-message-io-level
+       functions to do client-side IO. (Code that used to be in
+       soup-queue.c)
+       (get_request_header_cb): Fix a bug in the generation of the Host:
+       header; need to include the port number if it's not the default.
+
+       * libsoup/soup-message-io.c (soup_message_write,
+       soup_message_write_simple): Take separate user_datas for the get_*
+       callbacks and the done callbacks.
+
+       * libsoup/soup-queue.c: Update to use soup_message_write_request
+       and soup_message_read_response.
+
+       * libsoup/soup-connection.c (soup_connection_new): Change the
+       prototype to take a SoupUri and a callback.
+
+       * libsoup/soup-context.c (try_create_connection,
+       soup_context_connect_cb): Update for soup_connection_new change.
+
+       * libsoup/soup-server.c (read_done_cb, issue_bad_request): Update
+       for soup_message_write changes
+
+       * libsoup/soup-uri.c (soup_uri_uses_default_port): new utility
+       function
+
+2003-08-26  Dan Winship  <danw@ximian.com>
+
        * libsoup/soup-message-private.h: Define SoupMessage signal stuff
        (READ_HEADERS, READ_CHUNK, READ_BODY, READ_ERROR, WROTE_HEADERS,
        WROTE_CHUNK, WROTE_BODY, WRITE_ERROR).
index 741336a..4a6397e 100644 (file)
@@ -52,42 +52,43 @@ libsoup_2_2_la_LIBADD =             \
        $(GLIB_LIBS)            \
        $(GNUTLS_LIBS)
 
-libsoup_2_2_la_SOURCES =       \
-       $(MARSHAL_GENERATED)    \
-       md5-utils.h             \
-       md5-utils.c             \
-       soup-address.c          \
-       soup-auth.h             \
-       soup-auth.c             \
-       soup-auth-basic.h       \
-       soup-auth-basic.c       \
-       soup-auth-digest.h      \
-       soup-auth-digest.c      \
-       soup-auth-ntlm.h        \
-       soup-auth-ntlm.c        \
-       soup-connection.c       \
-       soup-context.c          \
-       soup-dns.h              \
-       soup-dns.c              \
-       soup-error.c            \
-       soup-gnutls.h           \
-       soup-gnutls.c           \
-       soup-headers.c          \
-       soup-message-private.h  \
-       soup-message.c          \
-       soup-message-handlers.c \
-       soup-message-io.c       \
-       soup-method.c           \
-       soup-misc.c             \
-       soup-private.h          \
-       soup-queue.h            \
-       soup-queue.c            \
-       soup-server.c           \
-       soup-server-auth.c      \
-       soup-server-message.c   \
-       soup-socket.c           \
-       soup-ssl.h              \
-       soup-ssl.c              \
+libsoup_2_2_la_SOURCES =               \
+       $(MARSHAL_GENERATED)            \
+       md5-utils.h                     \
+       md5-utils.c                     \
+       soup-address.c                  \
+       soup-auth.h                     \
+       soup-auth.c                     \
+       soup-auth-basic.h               \
+       soup-auth-basic.c               \
+       soup-auth-digest.h              \
+       soup-auth-digest.c              \
+       soup-auth-ntlm.h                \
+       soup-auth-ntlm.c                \
+       soup-connection.c               \
+       soup-context.c                  \
+       soup-dns.h                      \
+       soup-dns.c                      \
+       soup-error.c                    \
+       soup-gnutls.h                   \
+       soup-gnutls.c                   \
+       soup-headers.c                  \
+       soup-message.c                  \
+       soup-message-client-io.c        \
+       soup-message-handlers.c         \
+       soup-message-io.c               \
+       soup-message-private.h          \
+       soup-method.c                   \
+       soup-misc.c                     \
+       soup-private.h                  \
+       soup-queue.h                    \
+       soup-queue.c                    \
+       soup-server.c                   \
+       soup-server-auth.c              \
+       soup-server-message.c           \
+       soup-socket.c                   \
+       soup-ssl.h                      \
+       soup-ssl.c                      \
        soup-uri.c
 
 EXTRA_DIST= soup-marshal.list
index c2fa3fc..c5cd781 100644 (file)
@@ -39,6 +39,7 @@ struct SoupConnectionPrivate {
 static GObjectClass *parent_class;
 
 enum {
+       CONNECT_RESULT,
        DISCONNECTED,
        LAST_SIGNAL
 };
@@ -75,6 +76,15 @@ class_init (GObjectClass *object_class)
        object_class->finalize = finalize;
 
        /* signals */
+       signals[CONNECT_RESULT] =
+               g_signal_new ("connect_result",
+                             G_OBJECT_CLASS_TYPE (object_class),
+                             G_SIGNAL_RUN_FIRST,
+                             G_STRUCT_OFFSET (SoupConnectionClass, connect_result),
+                             NULL, NULL,
+                             soup_marshal_NONE__INT,
+                             G_TYPE_NONE, 1,
+                             G_TYPE_INT);
        signals[DISCONNECTED] =
                g_signal_new ("disconnected",
                              G_OBJECT_CLASS_TYPE (object_class),
@@ -88,22 +98,44 @@ class_init (GObjectClass *object_class)
 SOUP_MAKE_TYPE (soup_connection, SoupConnection, class_init, init, PARENT_TYPE)
 
 
+static void
+socket_disconnected (SoupSocket *sock, gpointer conn)
+{
+       soup_connection_disconnect (conn);
+}
+
+static void
+socket_connected (SoupSocket *sock, SoupKnownErrorCode status, gpointer conn)
+{
+       g_signal_emit (conn, signals[CONNECT_RESULT], 0, status);
+}
+
 /**
  * soup_connection_new:
- * @sock: a #SoupSocket
+ * @uri: remote machine to connect to
+ * @callback: callback to call after connecting
+ * @user_data: data for @callback
  *
- * Creates a new #SoupConnection, wrapped around @sock.
+ * Creates a connection to @uri. @callback will be called when the
+ * connection completes (or fails).
  *
- * Return value: the new #SoupConnection
+ * Return value: the new connection (not yet ready for use).
  **/
 SoupConnection *
-soup_connection_new (SoupSocket *sock)
+soup_connection_new (const SoupUri *uri,
+                    SoupConnectionCallback callback, gpointer user_data)
 {
        SoupConnection *conn;
 
        conn = g_object_new (SOUP_TYPE_CONNECTION, NULL);
-       conn->priv->socket = g_object_ref (sock);
 
+       soup_signal_connect_once (conn, "connect_result",
+                                 G_CALLBACK (callback), user_data);
+       conn->priv->socket = soup_socket_client_new (uri->host, uri->port,
+                                                    uri->protocol == SOUP_PROTOCOL_HTTPS,
+                                                    socket_connected, conn);
+       g_signal_connect (conn->priv->socket, "disconnected",
+                         G_CALLBACK (socket_disconnected), conn);
        return conn;
 }
 
index 343f090..723450f 100644 (file)
@@ -10,6 +10,7 @@
 
 #include <glib-object.h>
 #include <libsoup/soup-socket.h>
+#include <libsoup/soup-uri.h>
 
 #define SOUP_TYPE_CONNECTION            (soup_connection_get_type ())
 #define SOUP_CONNECTION(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_CONNECTION, SoupConnection))
@@ -30,19 +31,29 @@ typedef struct {
        GObjectClass parent_class;
 
        /* signals */
+       void (*connect_result) (SoupConnection *, SoupKnownErrorCode);
        void (*disconnected) (SoupConnection *);
 } SoupConnectionClass;
 
 GType soup_connection_get_type (void);
 
 
-SoupConnection *soup_connection_new            (SoupSocket     *sock);
+typedef void  (*SoupConnectionCallback)        (SoupConnection     *sock,
+                                               SoupKnownErrorCode  status,
+                                               gpointer            data);
+
+SoupConnection *soup_connection_new            (const SoupUri      *uri,
+                                               SoupConnectionCallback,
+                                               gpointer            data);
+
+gboolean        soup_connection_is_proxy       (SoupConnection *conn);
+
 void            soup_connection_disconnect     (SoupConnection *conn);
 gboolean        soup_connection_is_connected   (SoupConnection *conn);
 
 SoupSocket     *soup_connection_get_socket     (SoupConnection *conn);
 
-void            soup_connection_set_in_use     (SoupConnection *conn, 
+void            soup_connection_set_in_use     (SoupConnection *conn,
                                                gboolean        in_use);
 gboolean        soup_connection_is_in_use      (SoupConnection *conn);
 time_t          soup_connection_last_used      (SoupConnection *conn);
@@ -50,4 +61,4 @@ time_t          soup_connection_last_used      (SoupConnection *conn);
 gboolean        soup_connection_is_new         (SoupConnection *conn);
 void            soup_connection_mark_old       (SoupConnection *conn);
 
-#endif /*SOUP_CONNECTION_H*/
+#endif /* SOUP_CONNECTION_H */
index 637ef2f..cc0cee4 100644 (file)
@@ -287,7 +287,7 @@ struct SoupConnectData {
        gpointer               user_data;
 
        guint                  timeout_tag;
-       SoupSocket            *sock;
+       SoupConnection        *conn;
 };
 
 static void
@@ -330,37 +330,36 @@ prune_least_used_connection (void)
 static gboolean retry_connect_timeout_cb (struct SoupConnectData *data);
 
 static void
-soup_context_connect_cb (SoupSocket         *socket,
+soup_context_connect_cb (SoupConnection     *conn,
                         SoupKnownErrorCode  status,
                         gpointer            user_data)
 {
        struct SoupConnectData *data = user_data;
        SoupContext            *ctx = data->ctx;
-       SoupConnection         *new_conn = NULL;
 
        switch (status) {
        case SOUP_ERROR_OK:
-               new_conn = soup_connection_new (socket);
-
-               g_signal_connect (new_conn, "disconnected",
+               g_signal_connect (conn, "disconnected",
                                  G_CALLBACK (connection_disconnected),
                                  ctx->priv->server);
 
                /* FIXME */
-               g_object_set_data (G_OBJECT (new_conn), "SoupContext-port",
+               g_object_set_data (G_OBJECT (conn), "SoupContext-port",
                                   GUINT_TO_POINTER (ctx->priv->uri->port));
 
                ctx->priv->server->connections =
-                       g_slist_prepend (ctx->priv->server->connections, new_conn);
+                       g_slist_prepend (ctx->priv->server->connections, conn);
 
                break;
 
        case SOUP_ERROR_CANT_RESOLVE:
                connection_count--;
+               g_object_unref (conn);
                break;
 
        default:
                connection_count--;
+               g_object_unref (conn);
 
                /*
                 * Check if another connection exists to this server
@@ -378,9 +377,8 @@ soup_context_connect_cb (SoupSocket         *socket,
                break;
        }
 
-       (*data->cb) (ctx, status, new_conn, data->user_data);
+       (*data->cb) (ctx, status, conn, data->user_data);
        g_object_unref (ctx);
-       g_object_unref (data->sock);
        g_free (data);
 }
 
@@ -415,7 +413,6 @@ static gboolean
 try_create_connection (struct SoupConnectData *data)
 {
        int conn_limit = soup_get_connection_limit ();
-       SoupUri *uri;
 
        /* 
         * Check if we are allowed to create a new connection, otherwise wait
@@ -424,17 +421,15 @@ try_create_connection (struct SoupConnectData *data)
        if (conn_limit &&
            connection_count >= conn_limit &&
            !prune_least_used_connection ()) {
-               data->sock = NULL;
+               data->conn = NULL;
                return FALSE;
        }
 
        connection_count++;
 
        data->timeout_tag = 0;
-       uri = data->ctx->priv->uri;
-       data->sock = soup_socket_client_new (uri->host, uri->port,
-                                            uri->protocol == SOUP_PROTOCOL_HTTPS,
-                                            soup_context_connect_cb, data);
+       data->conn = soup_connection_new (data->ctx->priv->uri,
+                                         soup_context_connect_cb, data);
        return TRUE;
 }
 
@@ -519,9 +514,9 @@ soup_context_cancel_connect (SoupConnectId tag)
 
        if (data->timeout_tag)
                g_source_remove (data->timeout_tag);
-       else if (data->sock) {
+       else if (data->conn) {
                connection_count--;
-               g_object_unref (data->sock);
+               g_object_unref (data->conn);
        }
 
        g_free (data);
diff --git a/libsoup/soup-message-client-io.c b/libsoup/soup-message-client-io.c
new file mode 100644 (file)
index 0000000..7e00260
--- /dev/null
@@ -0,0 +1,208 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-message-client-io.c: client-side request/response
+ *
+ * Copyright (C) 2000-2003, Ximian, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "soup-message-private.h"
+#include "soup-auth.h"
+#include "soup-context.h"
+#include "soup-headers.h"
+#include "soup-misc.h"
+#include "soup-private.h"
+
+static SoupKnownErrorCode
+parse_response_headers_cb (SoupMessage *req,
+                          char *headers, guint headers_len,
+                          SoupTransferEncoding *encoding,
+                          guint *content_len,
+                          gpointer user_data)
+{
+       const char *length, *enc;
+       SoupHttpVersion version;
+       GHashTable *resp_hdrs;
+       SoupMethodId meth_id;
+
+       if (!soup_headers_parse_response (headers, headers_len,
+                                         req->response_headers,
+                                         &version,
+                                         &req->errorcode,
+                                         (char **) &req->errorphrase))
+               return SOUP_ERROR_MALFORMED;
+
+       meth_id   = soup_method_get_id (req->method);
+       resp_hdrs = req->response_headers;
+
+       req->errorclass = soup_error_get_class (req->errorcode);
+
+       /* 
+        * Special case zero body handling for:
+        *   - HEAD requests (where content-length must be ignored) 
+        *   - CONNECT requests (no body expected) 
+        *   - No Content (204) responses (no message-body allowed)
+        *   - Reset Content (205) responses (no entity allowed)
+        *   - Not Modified (304) responses (no message-body allowed)
+        *   - 1xx Informational responses (where no body is allowed)
+        */
+       if (meth_id == SOUP_METHOD_ID_HEAD ||
+           meth_id == SOUP_METHOD_ID_CONNECT ||
+           req->errorcode  == SOUP_ERROR_NO_CONTENT || 
+           req->errorcode  == SOUP_ERROR_RESET_CONTENT || 
+           req->errorcode  == SOUP_ERROR_NOT_MODIFIED || 
+           req->errorclass == SOUP_ERROR_CLASS_INFORMATIONAL) {
+               *encoding = SOUP_TRANSFER_CONTENT_LENGTH;
+               *content_len = 0;
+               return SOUP_ERROR_OK;
+       }
+
+       /* 
+        * Handle Chunked encoding.  Prefer Chunked over a Content-Length to
+        * support broken Traffic-Server proxies that supply both.  
+        */
+       enc = soup_message_get_header (resp_hdrs, "Transfer-Encoding");
+       if (enc) {
+               if (g_strcasecmp (enc, "chunked") == 0) {
+                       *encoding = SOUP_TRANSFER_CHUNKED;
+                       return SOUP_ERROR_OK;
+               } else
+                       return SOUP_ERROR_MALFORMED;
+       }
+
+       /* 
+        * Handle Content-Length encoding 
+        */
+       length = soup_message_get_header (resp_hdrs, "Content-Length");
+       if (length) {
+               int len;
+
+               *encoding = SOUP_TRANSFER_CONTENT_LENGTH;
+               len = atoi (length);
+               if (len < 0)
+                       return SOUP_ERROR_MALFORMED;
+               else
+                       *content_len = len;
+       }
+
+       return SOUP_ERROR_OK;
+}
+
+void
+soup_message_read_response (SoupMessage            *msg,
+                           SoupMessageCallbackFn   read_headers_cb,
+                           SoupMessageReadChunkFn  read_chunk_cb,
+                           SoupMessageCallbackFn   read_body_cb,
+                           SoupMessageCallbackFn   read_error_cb,
+                           gpointer                user_data)
+{
+       soup_message_read (msg, &msg->response, parse_response_headers_cb,
+                          read_headers_cb, read_chunk_cb, read_body_cb,
+                          read_error_cb, user_data);
+}
+
+
+static void
+encode_http_auth (SoupMessage *msg, GString *header, gboolean proxy_auth)
+{
+       SoupAuth *auth;
+       SoupContext *ctx;
+       char *token;
+
+       ctx = proxy_auth ? soup_get_proxy () : msg->priv->context;
+
+       auth = soup_context_lookup_auth (ctx, msg);
+       if (!auth)
+               return;
+       if (!soup_auth_is_authenticated (auth) &&
+           !soup_context_authenticate_auth (ctx, auth))
+               return;
+
+       token = soup_auth_get_authorization (auth, msg);
+       if (token) {
+               g_string_sprintfa (header, "%s: %s\r\n",
+                                  proxy_auth ? 
+                                       "Proxy-Authorization" : 
+                                       "Authorization",
+                                  token);
+               g_free (token);
+       }
+}
+
+static void 
+add_header (gpointer name, gpointer value, gpointer data)
+{
+       GString *headers = data;
+
+       g_string_append_printf (headers, "%s: %s\r\n",
+                               (char *)name, (char *)value);
+}
+
+static void
+get_request_header_cb (SoupMessage *req, GString *header, gpointer user_data)
+{
+       const SoupUri *uri = soup_message_get_uri (req);
+       char *uri_string;
+       gboolean proxy = GPOINTER_TO_UINT (user_data);
+
+       if (!strcmp (req->method, "CONNECT")) {
+               /* CONNECT URI is hostname:port for tunnel destination */
+               uri_string = g_strdup_printf ("%s:%d", uri->host, uri->port);
+       } else {
+               /* Proxy expects full URI to destination. Otherwise
+                * just the path.
+                */
+               uri_string = soup_uri_to_string (uri, !proxy);
+       }
+
+       if (req->priv->http_version == SOUP_HTTP_1_0) {
+               g_string_append_printf (header, "%s %s HTTP/1.0\r\n",
+                                       req->method, uri_string);
+       } else {
+               g_string_append_printf (header, "%s %s HTTP/1.1\r\n",
+                                       req->method, uri_string);
+               if (soup_uri_uses_default_port (uri)) {
+                       g_string_append_printf (header, "Host: %s\r\n",
+                                               uri->host);
+               } else {
+                       g_string_append_printf (header, "Host: %s:%d\r\n",
+                                               uri->host, uri->port);
+               }
+       }
+       g_free (uri_string);
+
+       if (req->request.length > 0) {
+               if (!soup_message_get_header (req->request_headers,
+                                             "Content-Type")) {
+                       g_string_append (header, "Content-Type: text/xml; "
+                                        "charset=utf-8\r\n");
+               }
+               g_string_append_printf (header, "Content-Length: %d\r\n",
+                                       req->request.length);
+       }
+
+       encode_http_auth (req, header, FALSE);
+       if (proxy)
+               encode_http_auth (req, header, TRUE);
+
+       soup_message_foreach_header (req->request_headers, add_header, header);
+       g_string_append (header, "\r\n");
+}
+
+void
+soup_message_write_request (SoupMessage *req, gboolean is_via_proxy,
+                           SoupMessageCallbackFn write_done_cb,
+                           SoupMessageCallbackFn write_error_cb,
+                           gpointer user_data)
+{
+       soup_message_write_simple (req, &req->request,
+                                  get_request_header_cb,
+                                  GUINT_TO_POINTER (is_via_proxy),
+                                  write_done_cb, write_error_cb, user_data);
+}
index cf4057c..292294a 100644 (file)
@@ -458,7 +458,7 @@ typedef struct {
 
        SoupMessageGetHeadersFn get_header_cb;
        SoupMessageGetChunkFn   get_chunk_cb;
-       gpointer                user_data;
+       gpointer                get_user_data;
 
        guint wrote_body_id;
        guint error_id;
@@ -566,7 +566,8 @@ do_write (SoupSocket *sock, SoupMessage *msg)
                case SOUP_MESSAGE_STATUS_WRITING_HEADERS:
                        if (w->get_header_cb) {
                                SOUP_MESSAGE_WRITE_PREPARE_FOR_CALLBACK;
-                               w->get_header_cb (msg, w->buf, w->user_data);
+                               w->get_header_cb (msg, w->buf,
+                                                 w->get_user_data);
                                SOUP_MESSAGE_WRITE_RETURN_IF_CANCELLED;
 
                                w->get_header_cb = NULL;
@@ -606,7 +607,7 @@ do_write (SoupSocket *sock, SoupMessage *msg)
                                SOUP_MESSAGE_WRITE_PREPARE_FOR_CALLBACK;
                                got_chunk = w->get_chunk_cb (msg,
                                                             &w->chunk,
-                                                            w->user_data);
+                                                            w->get_user_data);
                                SOUP_MESSAGE_WRITE_RETURN_IF_CANCELLED;
 
                                if (!got_chunk) {
@@ -697,6 +698,7 @@ create_writer (SoupMessage             *msg,
               SoupTransferEncoding     encoding,
               SoupMessageGetHeadersFn  get_header_cb,
               SoupMessageGetChunkFn    get_chunk_cb,
+              gpointer                 get_user_data,
               SoupMessageCallbackFn    wrote_body_cb,
               SoupMessageCallbackFn    error_cb,
               gpointer                 user_data)
@@ -708,7 +710,7 @@ create_writer (SoupMessage             *msg,
        w->buf           = g_string_new (NULL);
        w->get_header_cb = get_header_cb;
        w->get_chunk_cb  = get_chunk_cb;
-       w->user_data     = user_data;
+       w->get_user_data = get_user_data;
 
        w->wrote_body_id = g_signal_connect (msg, "wrote_body",
                                             G_CALLBACK (wrote_body_cb),
@@ -736,6 +738,7 @@ void
 soup_message_write_simple (SoupMessage             *msg,
                           const SoupDataBuffer    *body,
                           SoupMessageGetHeadersFn  get_header_cb,
+                          gpointer                 get_user_data,
                           SoupMessageCallbackFn    wrote_body_cb,
                           SoupMessageCallbackFn    error_cb,
                           gpointer                 user_data)
@@ -743,8 +746,8 @@ soup_message_write_simple (SoupMessage             *msg,
        SoupMessageWriteState *w;
 
        w = create_writer (msg, SOUP_TRANSFER_CONTENT_LENGTH,
-                          get_header_cb, NULL, wrote_body_cb,
-                          error_cb, user_data);
+                          get_header_cb, NULL, get_user_data,
+                          wrote_body_cb, error_cb, user_data);
 
        w->body = body;
 }
@@ -754,13 +757,15 @@ soup_message_write (SoupMessage             *msg,
                    SoupTransferEncoding     encoding,
                    SoupMessageGetHeadersFn  get_header_cb,
                    SoupMessageGetChunkFn    get_chunk_cb,
+                   gpointer                 get_user_data,
                    SoupMessageCallbackFn    wrote_body_cb,
                    SoupMessageCallbackFn    error_cb,
                    gpointer                 user_data)
 {
        SoupMessageWriteState *w;
 
-       w = create_writer (msg, encoding, get_header_cb, get_chunk_cb,
+       w = create_writer (msg, encoding,
+                          get_header_cb, get_chunk_cb, get_user_data,
                           wrote_body_cb, error_cb, user_data);
 }
 
index 322a40e..f28e91d 100644 (file)
@@ -132,12 +132,14 @@ void soup_message_write         (SoupMessage                 *msg,
                                 SoupTransferEncoding         encoding,
                                 SoupMessageGetHeadersFn      get_header_cb,
                                 SoupMessageGetChunkFn        get_chunk_cb,
+                                gpointer                     get_user_data,
                                 SoupCallbackFn               write_done_cb,
                                 SoupCallbackFn               error_cb,
                                 gpointer                     user_data);
 void soup_message_write_simple  (SoupMessage                 *msg,
                                 const SoupDataBuffer        *body,
                                 SoupMessageGetHeadersFn      get_header_cb,
+                                gpointer                     get_user_data,
                                 SoupCallbackFn               write_done_cb,
                                 SoupCallbackFn               error_cb,
                                 gpointer                     user_data);
@@ -147,4 +149,19 @@ void soup_message_write_pause   (SoupMessage                 *msg);
 void soup_message_write_unpause (SoupMessage                 *msg);
 
 
+/* Higher-level API */
+void soup_message_write_request  (SoupMessage            *msg,
+                                 gboolean                is_via_proxy,
+                                 SoupMessageCallbackFn   write_done_cb,
+                                 SoupMessageCallbackFn   write_error_cb,
+                                 gpointer                user_data);
+void soup_message_read_response  (SoupMessage            *msg,
+                                 SoupMessageCallbackFn   read_headers_cb,
+                                 SoupMessageReadChunkFn  read_chunk_cb,
+                                 SoupMessageCallbackFn   read_body_cb,
+                                 SoupMessageCallbackFn   read_error_cb,
+                                 gpointer                user_data);
+
+
+
 #endif /* SOUP_MESSAGE_PRIVATE_H */
index da924e2..b41652c 100644 (file)
@@ -109,81 +109,6 @@ soup_queue_error_cb (SoupMessage *req, gpointer user_data)
        }
 }
 
-static SoupKnownErrorCode
-soup_queue_parse_headers_cb (SoupMessage *req,
-                            char *headers, guint headers_len,
-                            SoupTransferEncoding *encoding,
-                            guint *content_len,
-                            gpointer user_data)
-{
-       const char *length, *enc;
-       SoupHttpVersion version;
-       GHashTable *resp_hdrs;
-       SoupMethodId meth_id;
-
-       if (!soup_headers_parse_response (headers, headers_len,
-                                         req->response_headers,
-                                         &version,
-                                         &req->errorcode,
-                                         (char **) &req->errorphrase))
-               return SOUP_ERROR_MALFORMED;
-
-       meth_id   = soup_method_get_id (req->method);
-       resp_hdrs = req->response_headers;
-
-       req->errorclass = soup_error_get_class (req->errorcode);
-
-       /* 
-        * Special case zero body handling for:
-        *   - HEAD requests (where content-length must be ignored) 
-        *   - CONNECT requests (no body expected) 
-        *   - No Content (204) responses (no message-body allowed)
-        *   - Reset Content (205) responses (no entity allowed)
-        *   - Not Modified (304) responses (no message-body allowed)
-        *   - 1xx Informational responses (where no body is allowed)
-        */
-       if (meth_id == SOUP_METHOD_ID_HEAD ||
-           meth_id == SOUP_METHOD_ID_CONNECT ||
-           req->errorcode  == SOUP_ERROR_NO_CONTENT || 
-           req->errorcode  == SOUP_ERROR_RESET_CONTENT || 
-           req->errorcode  == SOUP_ERROR_NOT_MODIFIED || 
-           req->errorclass == SOUP_ERROR_CLASS_INFORMATIONAL) {
-               *encoding = SOUP_TRANSFER_CONTENT_LENGTH;
-               *content_len = 0;
-               return SOUP_ERROR_OK;
-       }
-
-       /* 
-        * Handle Chunked encoding.  Prefer Chunked over a Content-Length to
-        * support broken Traffic-Server proxies that supply both.  
-        */
-       enc = soup_message_get_header (resp_hdrs, "Transfer-Encoding");
-       if (enc) {
-               if (g_strcasecmp (enc, "chunked") == 0) {
-                       *encoding = SOUP_TRANSFER_CHUNKED;
-                       return SOUP_ERROR_OK;
-               } else
-                       return SOUP_ERROR_MALFORMED;
-       }
-
-       /* 
-        * Handle Content-Length encoding 
-        */
-       length = soup_message_get_header (resp_hdrs, "Content-Length");
-       if (length) {
-               int len;
-
-               *encoding = SOUP_TRANSFER_CONTENT_LENGTH;
-               len = atoi (length);
-               if (len < 0)
-                       return SOUP_ERROR_MALFORMED;
-               else
-                       *content_len = len;
-       }
-
-       return SOUP_ERROR_OK;
-}
-
 static void
 soup_queue_read_headers_cb (SoupMessage *req, gpointer user_data)
 {
@@ -211,194 +136,26 @@ soup_queue_read_done_cb (SoupMessage *req, gpointer user_data)
                soup_message_disconnect (req);
 
        if (req->errorclass == SOUP_ERROR_CLASS_INFORMATIONAL) {
-               soup_message_read (req, &req->response,
-                                  soup_queue_parse_headers_cb,
-                                  soup_queue_read_headers_cb,
-                                  soup_queue_read_chunk_cb,
-                                  soup_queue_read_done_cb,
-                                  soup_queue_error_cb,
-                                  NULL);
+               soup_message_read_response (req, 
+                                           soup_queue_read_headers_cb,
+                                           soup_queue_read_chunk_cb,
+                                           soup_queue_read_done_cb,
+                                           soup_queue_error_cb,
+                                           NULL);
        } else
                req->priv->status = SOUP_MESSAGE_STATUS_FINISHED;
 
        soup_message_run_handlers (req, SOUP_HANDLER_POST_BODY);
 }
 
-static void
-soup_encode_http_auth (SoupMessage *msg, GString *header, gboolean proxy_auth)
-{
-       SoupAuth *auth;
-       SoupContext *ctx;
-       char *token;
-
-       ctx = proxy_auth ? soup_get_proxy () : msg->priv->context;
-
-       auth = soup_context_lookup_auth (ctx, msg);
-       if (!auth)
-               return;
-       if (!soup_auth_is_authenticated (auth) &&
-           !soup_context_authenticate_auth (ctx, auth))
-               return;
-
-       token = soup_auth_get_authorization (auth, msg);
-       if (token) {
-               g_string_sprintfa (header, "%s: %s\r\n",
-                                  proxy_auth ? 
-                                       "Proxy-Authorization" : 
-                                       "Authorization",
-                                  token);
-               g_free (token);
-       }
-}
-
-struct SoupUsedHeaders {
-       gboolean host;
-       gboolean user_agent;
-       gboolean content_type;
-       gboolean connection;
-       gboolean proxy_auth;
-       gboolean auth;
-
-       GString *out;
-};
-
-static void 
-soup_check_used_headers (gchar  *key, 
-                        GSList *vals, 
-                        struct SoupUsedHeaders *hdrs)
-{
-       switch (toupper (key [0])) {
-       case 'H':
-               if (!g_strcasecmp (key+1, "ost")) 
-                       hdrs->host = TRUE;
-               break;
-       case 'U':
-               if (!g_strcasecmp (key+1, "ser-Agent")) 
-                       hdrs->user_agent = TRUE;
-               break;
-       case 'A':
-               if (!g_strcasecmp (key+1, "uthorization")) 
-                       hdrs->auth = TRUE;
-               break;
-       case 'P':
-               if (!g_strcasecmp (key+1, "roxy-Authorization")) 
-                       hdrs->proxy_auth = TRUE;
-               break;
-       case 'C':
-               if (!g_strcasecmp (key+1, "onnection")) 
-                       hdrs->connection = TRUE;
-               else if (!g_strcasecmp (key+1, "ontent-Type"))
-                       hdrs->content_type = TRUE;
-               else if (!g_strcasecmp (key+1, "ontent-Length")) {
-                       g_warning ("Content-Length set as custom request "
-                                  "header is not allowed.");
-                       return;
-               }
-               break;
-       }
-
-       while (vals) {
-               g_string_sprintfa (hdrs->out, 
-                                  "%s: %s\r\n", 
-                                  key, 
-                                  (gchar *) vals->data);
-               vals = vals->next;
-       }
-}
-
-static void
-soup_queue_get_request_header_cb (SoupMessage *req, GString *header,
-                                 gpointer user_data)
-{
-       char *uri;
-       SoupContext *proxy;
-       const SoupUri *suri;
-       struct SoupUsedHeaders hdrs = {
-               FALSE, 
-               FALSE, 
-               FALSE, 
-               FALSE, 
-               FALSE, 
-               FALSE, 
-               NULL
-       };
-
-       hdrs.out = header;
-       proxy = soup_get_proxy ();
-       suri = soup_message_get_uri (req);
-
-       if (!g_strcasecmp (req->method, "CONNECT")) {
-               /* CONNECT URI is hostname:port for tunnel destination */
-               uri = g_strdup_printf ("%s:%d", suri->host, suri->port);
-       } else {
-               /* Proxy expects full URI to destination. Otherwise
-                * just the path.
-                */
-               uri = soup_uri_to_string (suri, !proxy);
-       }
-
-       g_string_sprintfa (header,
-                          req->priv->http_version == SOUP_HTTP_1_1 ? 
-                                  "%s %s HTTP/1.1\r\n" : 
-                                  "%s %s HTTP/1.0\r\n",
-                          req->method,
-                          uri);
-       g_free (uri);
-
-       /*
-        * FIXME: Add a 411 "Length Required" response code handler here?
-        */
-       if (req->request.length > 0) {
-               g_string_sprintfa (header,
-                                  "Content-Length: %d\r\n",
-                                  req->request.length);
-       }
-
-       g_hash_table_foreach (req->request_headers, 
-                             (GHFunc) soup_check_used_headers,
-                             &hdrs);
-
-       /* 
-        * If we specify an absoluteURI in the request line, the Host header
-        * MUST be ignored by the proxy.  
-        */
-       g_string_sprintfa (header, 
-                          "%s%s%s%s%s%s%s",
-                          hdrs.host ? "" : "Host: ",
-                          hdrs.host ? "" : suri->host,
-                          hdrs.host ? "" : "\r\n",
-                          hdrs.content_type ? "" : "Content-Type: text/xml; ",
-                          hdrs.content_type ? "" : "charset=utf-8\r\n",
-                          hdrs.connection ? "" : "Connection: keep-alive\r\n",
-                          hdrs.user_agent ? 
-                                  "" : 
-                                  "User-Agent: Soup/" VERSION "\r\n");
-
-       /* 
-        * Proxy-Authorization from the proxy Uri 
-        */
-       if (!hdrs.proxy_auth && proxy && soup_context_get_uri (proxy)->user)
-               soup_encode_http_auth (req, header, TRUE);
-
-       /* 
-        * Authorization from the context Uri 
-        */
-       if (!hdrs.auth)
-               soup_encode_http_auth (req, header, FALSE);
-
-       g_string_append (header, "\r\n");
-}
-
 static void 
 soup_queue_write_done_cb (SoupMessage *req, gpointer user_data)
 {
-       soup_message_read (req, &req->response,
-                          soup_queue_parse_headers_cb,
-                          soup_queue_read_headers_cb,
-                          soup_queue_read_chunk_cb,
-                          soup_queue_read_done_cb,
-                          soup_queue_error_cb,
-                          NULL);
+       soup_message_read_response (req, soup_queue_read_headers_cb,
+                                   soup_queue_read_chunk_cb,
+                                   soup_queue_read_done_cb,
+                                   soup_queue_error_cb,
+                                   NULL);
 }
 
 static void
@@ -433,11 +190,10 @@ start_request (SoupContext *ctx, SoupMessage *req)
                return;
        }
 
-       soup_message_write_simple (req, &req->request,
-                                  soup_queue_get_request_header_cb,
-                                  soup_queue_write_done_cb,
-                                  soup_queue_error_cb,
-                                  NULL);
+       soup_message_write_request (req, soup_get_proxy () != NULL,
+                                   soup_queue_write_done_cb,
+                                   soup_queue_error_cb,
+                                   NULL);
 }
 
 static void
index de35f99..53d2dbc 100644 (file)
@@ -257,9 +257,8 @@ issue_bad_request (SoupMessage *msg)
        soup_server_message_finish (SOUP_SERVER_MESSAGE (msg));
 
        soup_message_write_simple (msg, &msg->response,
-                                  get_response_header_cb,
-                                  write_done_cb, error_cb,
-                                  NULL);
+                                  get_response_header_cb, NULL,
+                                  write_done_cb, error_cb, NULL);
 }
 
 static SoupKnownErrorCode
@@ -469,13 +468,12 @@ read_done_cb (SoupMessage *req, gpointer user_data)
        encoding = soup_server_message_get_encoding (smsg);
        if (encoding == SOUP_TRANSFER_CONTENT_LENGTH) {
                soup_message_write_simple (req, &req->response,
-                                          get_response_header_cb,
-                                          write_done_cb, error_cb,
-                                          NULL);
+                                          get_response_header_cb, NULL,
+                                          write_done_cb, error_cb, NULL);
                soup_server_message_start (smsg);
        } else {
                soup_message_write (req, encoding,
-                                   get_response_header_cb, get_chunk_cb,
+                                   get_response_header_cb, get_chunk_cb, NULL,
                                    write_done_cb, error_cb, NULL);
        }
 
index c934942..f3bcb4a 100644 (file)
@@ -480,3 +480,9 @@ soup_uri_decode (char *part)
                        *d++ = *s;
        } while (*s++);
 }
+
+gboolean
+soup_uri_uses_default_port (const SoupUri *uri)
+{
+       return uri->port == soup_protocol_default_port (uri->protocol);
+}
index e768342..2f560d1 100644 (file)
@@ -31,27 +31,29 @@ typedef struct {
        char         *fragment;
 } SoupUri;
 
-SoupUri *soup_uri_new_with_base (const SoupUri *base,
-                                const char    *uri_string);
-SoupUri *soup_uri_new           (const char    *uri_string);
+SoupUri  *soup_uri_new_with_base     (const SoupUri *base,
+                                     const char    *uri_string);
+SoupUri  *soup_uri_new               (const char    *uri_string);
 
-char    *soup_uri_to_string     (const SoupUri *uri, 
-                                gboolean       just_path);
+char     *soup_uri_to_string         (const SoupUri *uri, 
+                                     gboolean       just_path);
 
-SoupUri *soup_uri_copy          (const SoupUri *uri);
+SoupUri  *soup_uri_copy              (const SoupUri *uri);
 
-gboolean soup_uri_equal         (const SoupUri *uri1, 
-                                const SoupUri *uri2);
+gboolean  soup_uri_equal             (const SoupUri *uri1, 
+                                     const SoupUri *uri2);
 
-void     soup_uri_free          (SoupUri       *uri);
+void      soup_uri_free              (SoupUri       *uri);
 
-void     soup_uri_set_auth      (SoupUri       *uri, 
-                                const char    *user, 
-                                const char    *passwd, 
-                                const char    *authmech);
+void      soup_uri_set_auth          (SoupUri       *uri, 
+                                     const char    *user, 
+                                     const char    *passwd, 
+                                     const char    *authmech);
 
-char    *soup_uri_encode        (const char    *part,
-                                const char    *escape_extra);
-void     soup_uri_decode        (char          *part);
+char     *soup_uri_encode            (const char    *part,
+                                     const char    *escape_extra);
+void      soup_uri_decode            (char          *part);
+
+gboolean  soup_uri_uses_default_port (const SoupUri *uri);
 
 #endif /*SOUP_URI_H*/