New. An interface for objects that want to act on every message passing
authorDan Winship <danw@src.gnome.org>
Fri, 19 Dec 2003 20:54:38 +0000 (20:54 +0000)
committerDan Winship <danw@src.gnome.org>
Fri, 19 Dec 2003 20:54:38 +0000 (20:54 +0000)
* libsoup/soup-message-filter.c: New. An interface for objects
that want to act on every message passing through a session.
(Initially being used for authentication, but could also be used
for cache handling, cookie management, etc.)

* libsoup/soup-connection.c (class_init, etc): Add a message
filter property.
(send_request): If the connection has a message filter set, run
it on the message before sending it.
(soup_connection_connect_async, etc): When setting up a tunnel, if
we get back a 407 and the session tries to requeue the message,
either re-send it, or return SOUP_STATUS_TRY_AGAIN (depending on
whether or not the proxy closed the connection).
(soup_connection_connect_sync): Likewise
(send_request, request_done): Ref/unref the connection

* libsoup/soup-session.c (soup_session_get_type): Implement the
SoupMessageFilter interface.
(soup_session_get_connection): Use the session as the connection's
message filter
(soup_session_add_filter, soup_session_remove_filter): Add/remove
filters from the session
(setup_message): do auth handling, and call each of the session's
filters' setup_message methods as well.
(soup_session_send_message_via): No longer needed.
(connect_result): Handle SOUP_STATUS_TRY_AGAIN.

* libsoup/soup-session-async.c (run_queue): Use
soup_connection_send_request, since soup_session_send_message_via
is gone now.

* libsoup/soup-session-sync.c (send_message): Likewise

* libsoup/soup-message.c (soup_message_is_keepalive): A successful
response to a CONNECT is always keepalive, even if it's HTTP/1.0
with no Connection header.

* libsoup/soup-status.h: add SOUP_STATUS_TRY_AGAIN

* libsoup/soup-types.h: Add SoupMessageFilter, and macros for
gobject interface types.

* tests/get.c (main): Add a -p flag to specify a proxy

* tests/simple-proxy.c: Fix #includes

16 files changed:
ChangeLog
libsoup/Makefile.am
libsoup/soup-connection.c
libsoup/soup-connection.h
libsoup/soup-message-filter.c [new file with mode: 0644]
libsoup/soup-message-filter.h [new file with mode: 0644]
libsoup/soup-message.c
libsoup/soup-session-async.c
libsoup/soup-session-sync.c
libsoup/soup-session.c
libsoup/soup-session.h
libsoup/soup-status.c
libsoup/soup-status.h
libsoup/soup-types.h
tests/get.c
tests/simple-proxy.c

index 594aced..9e91986 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,51 @@
+2003-12-19  Dan Winship  <danw@ximian.com>
+
+       * libsoup/soup-message-filter.c: New. An interface for objects
+       that want to act on every message passing through a session.
+       (Initially being used for authentication, but could also be used
+       for cache handling, cookie management, etc.)
+
+       * libsoup/soup-connection.c (class_init, etc): Add a message
+       filter property.
+       (send_request): If the connection has a message filter set, run
+       it on the message before sending it.
+       (soup_connection_connect_async, etc): When setting up a tunnel, if
+       we get back a 407 and the session tries to requeue the message,
+       either re-send it, or return SOUP_STATUS_TRY_AGAIN (depending on
+       whether or not the proxy closed the connection).
+       (soup_connection_connect_sync): Likewise
+       (send_request, request_done): Ref/unref the connection
+
+       * libsoup/soup-session.c (soup_session_get_type): Implement the
+       SoupMessageFilter interface.
+       (soup_session_get_connection): Use the session as the connection's
+       message filter
+       (soup_session_add_filter, soup_session_remove_filter): Add/remove
+       filters from the session
+       (setup_message): do auth handling, and call each of the session's
+       filters' setup_message methods as well.
+       (soup_session_send_message_via): No longer needed.
+       (connect_result): Handle SOUP_STATUS_TRY_AGAIN.
+
+       * libsoup/soup-session-async.c (run_queue): Use
+       soup_connection_send_request, since soup_session_send_message_via
+       is gone now.
+
+       * libsoup/soup-session-sync.c (send_message): Likewise
+
+       * libsoup/soup-message.c (soup_message_is_keepalive): A successful
+       response to a CONNECT is always keepalive, even if it's HTTP/1.0
+       with no Connection header.
+
+       * libsoup/soup-status.h: add SOUP_STATUS_TRY_AGAIN
+
+       * libsoup/soup-types.h: Add SoupMessageFilter, and macros for
+       gobject interface types.
+
+       * tests/get.c (main): Add a -p flag to specify a proxy
+
+       * tests/simple-proxy.c: Fix #includes
+
 2003-12-18  Dan Winship  <danw@ximian.com>
 
        * libsoup/soup-connection.c (soup_connection_disconnect): Actually
index 458b9a8..342aee1 100644 (file)
@@ -32,6 +32,7 @@ libsoupinclude_HEADERS =      \
        soup-connection.h       \
        soup-headers.h          \
        soup-message.h          \
+       soup-message-filter.h   \
        soup-message-queue.h    \
        soup-method.h           \
        soup-misc.h             \
@@ -78,6 +79,7 @@ libsoup_2_2_la_SOURCES =              \
        soup-md5-utils.c                \
        soup-message.c                  \
        soup-message-client-io.c        \
+       soup-message-filter.c           \
        soup-message-handlers.c         \
        soup-message-io.c               \
        soup-message-private.h          \
index 7c1535a..5e3d3bd 100644 (file)
@@ -25,6 +25,7 @@
 #include "soup-connection.h"
 #include "soup-marshal.h"
 #include "soup-message.h"
+#include "soup-message-filter.h"
 #include "soup-misc.h"
 #include "soup-socket.h"
 #include "soup-ssl.h"
@@ -42,6 +43,8 @@ struct SoupConnectionPrivate {
        SoupUri     *proxy_uri, *origin_uri, *conn_uri;
        gpointer     ssl_creds;
 
+       SoupMessageFilter *filter;
+
        SoupMessage *cur_req;
        time_t       last_used;
        gboolean     in_use;
@@ -66,6 +69,7 @@ enum {
   PROP_ORIGIN_URI,
   PROP_PROXY_URI,
   PROP_SSL_CREDS,
+  PROP_MESSAGE_FILTER,
 
   LAST_PROP
 };
@@ -96,6 +100,9 @@ finalize (GObject *object)
        if (conn->priv->origin_uri)
                soup_uri_free (conn->priv->origin_uri);
 
+       if (conn->priv->filter)
+               g_object_unref (conn->priv->filter);
+
        g_free (conn->priv);
 
        G_OBJECT_CLASS (parent_class)->finalize (object);
@@ -194,6 +201,12 @@ class_init (GObjectClass *object_class)
                                      "SSL credentials",
                                      "Opaque SSL credentials for this connection",
                                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+       g_object_class_install_property (
+               object_class, PROP_MESSAGE_FILTER,
+               g_param_spec_pointer (SOUP_CONNECTION_MESSAGE_FILTER,
+                                     "Message filter",
+                                     "Message filter object for this connection",
+                                     G_PARAM_READWRITE));
 }
 
 SOUP_MAKE_TYPE (soup_connection, SoupConnection, class_init, init, PARENT_TYPE)
@@ -255,6 +268,9 @@ set_property (GObject *object, guint prop_id,
        case PROP_SSL_CREDS:
                conn->priv->ssl_creds = g_value_get_pointer (value);
                break;
+       case PROP_MESSAGE_FILTER:
+               conn->priv->filter = g_object_ref (g_value_get_pointer (value));
+               break;
        default:
                break;
        }
@@ -279,12 +295,38 @@ get_property (GObject *object, guint prop_id,
        case PROP_SSL_CREDS:
                g_value_set_pointer (value, conn->priv->ssl_creds);
                break;
+       case PROP_MESSAGE_FILTER:
+               g_value_set_pointer (value, g_object_ref (conn->priv->filter));
+               break;
        default:
                break;
        }
 }
 
 static void
+set_current_request (SoupConnection *conn, SoupMessage *req)
+{
+       g_return_if_fail (conn->priv->cur_req == NULL);
+
+       req->status = SOUP_MESSAGE_STATUS_RUNNING;
+       conn->priv->cur_req = req;
+       conn->priv->in_use = TRUE;
+       g_object_add_weak_pointer (G_OBJECT (req),
+                                  (gpointer *)conn->priv->cur_req);
+}
+
+static void
+clear_current_request (SoupConnection *conn)
+{
+       if (conn->priv->cur_req) {
+               g_object_remove_weak_pointer (G_OBJECT (conn->priv->cur_req),
+                                             (gpointer *)conn->priv->cur_req);
+               conn->priv->cur_req = NULL;
+       }
+       conn->priv->in_use = FALSE;
+}
+
+static void
 socket_disconnected (SoupSocket *sock, gpointer conn)
 {
        soup_connection_disconnect (conn);
@@ -310,6 +352,8 @@ tunnel_connect_finished (SoupMessage *msg, gpointer user_data)
        SoupConnection *conn = user_data;
        guint status = msg->status_code;
 
+       clear_current_request (conn);
+
        if (SOUP_STATUS_IS_SUCCESSFUL (status)) {
                if (!soup_socket_start_ssl (conn->priv->socket))
                        status = SOUP_STATUS_SSL_FAILED;
@@ -321,6 +365,37 @@ tunnel_connect_finished (SoupMessage *msg, gpointer user_data)
 }
 
 static void
+tunnel_connect_restarted (SoupMessage *msg, gpointer user_data)
+{
+       SoupConnection *conn = user_data;
+       guint status = msg->status_code;
+
+       /* We only allow one restart: if another one happens, treat
+        * it as "finished".
+        */
+       g_signal_handlers_disconnect_by_func (msg, tunnel_connect_restarted, conn);
+       g_signal_connect (msg, "restarted",
+                         G_CALLBACK (tunnel_connect_finished), conn);
+
+       if (status == SOUP_STATUS_PROXY_AUTHENTICATION_REQUIRED) {
+               /* Our parent session has handled the authentication
+                * and attempted to restart the message.
+                */
+               if (soup_message_is_keepalive (msg)) {
+                       /* Connection is still open, so just send the
+                        * message again.
+                        */
+                       soup_connection_send_request (conn, msg);
+               } else {
+                       /* Tell the session to try again. */
+                       soup_message_set_status (msg, SOUP_STATUS_TRY_AGAIN);
+                       soup_message_finished (msg);
+               }
+       } else
+               soup_message_finished (msg);
+}
+
+static void
 socket_connect_result (SoupSocket *sock, guint status, gpointer user_data)
 {
        SoupConnection *conn = user_data;
@@ -344,6 +419,8 @@ socket_connect_result (SoupSocket *sock, guint status, gpointer user_data)
                connect_msg = soup_message_new_from_uri (SOUP_METHOD_CONNECT,
                                                         conn->priv->origin_uri);
 
+               g_signal_connect (connect_msg, "restarted",
+                                 G_CALLBACK (tunnel_connect_restarted), conn);
                g_signal_connect (connect_msg, "finished",
                                  G_CALLBACK (tunnel_connect_finished), conn);
 
@@ -428,6 +505,17 @@ soup_connection_connect_sync (SoupConnection *conn)
                                                         conn->priv->origin_uri);
                soup_connection_send_request (conn, connect_msg);
                status = connect_msg->status_code;
+
+               if (status == SOUP_STATUS_PROXY_AUTHENTICATION_REQUIRED &&
+                   SOUP_MESSAGE_IS_STARTING (connect_msg)) {
+                       if (soup_message_is_keepalive (connect_msg)) {
+                               /* Try once more */
+                               soup_connection_send_request (conn, connect_msg);
+                               status = connect_msg->status_code;
+                       } else
+                               status = SOUP_STATUS_TRY_AGAIN;
+               }
+
                g_object_unref (connect_msg);
        }
 
@@ -509,33 +597,32 @@ request_done (SoupMessage *req, gpointer user_data)
 {
        SoupConnection *conn = user_data;
 
-       g_object_remove_weak_pointer (G_OBJECT (conn->priv->cur_req),
-                                     (gpointer *)conn->priv->cur_req);
-       conn->priv->cur_req = NULL;
+       clear_current_request (conn);
        conn->priv->last_used = time (NULL);
-       conn->priv->in_use = FALSE;
 
        if (!soup_message_is_keepalive (req))
                soup_connection_disconnect (conn);
 
        g_signal_handlers_disconnect_by_func (req, request_done, conn);
        g_signal_handlers_disconnect_by_func (req, request_restarted, conn);
+       g_object_unref (conn);
 }
 
 static void
 send_request (SoupConnection *conn, SoupMessage *req)
 {
+       g_object_ref (conn);
+
        if (req != conn->priv->cur_req) {
-               g_return_if_fail (conn->priv->cur_req == NULL);
-               conn->priv->cur_req = req;
-               conn->priv->in_use = TRUE;
-               g_object_add_weak_pointer (G_OBJECT (req),
-                                          (gpointer *)conn->priv->cur_req);
+               set_current_request (conn, req);
 
                g_signal_connect (req, "restarted",
                                  G_CALLBACK (request_restarted), conn);
                g_signal_connect (req, "finished",
                                  G_CALLBACK (request_done), conn);
+
+               if (conn->priv->filter)
+                       soup_message_filter_setup_message (conn->priv->filter, req);
        }
 
        soup_message_io_cancel (req);
index d25b6a9..5d08b08 100644 (file)
@@ -49,6 +49,7 @@ GType soup_connection_get_type (void);
 #define SOUP_CONNECTION_ORIGIN_URI      "origin-uri"
 #define SOUP_CONNECTION_PROXY_URI       "proxy-uri"
 #define SOUP_CONNECTION_SSL_CREDENTIALS "ssl-creds"
+#define SOUP_CONNECTION_MESSAGE_FILTER  "message-filter"
 
 SoupConnection *soup_connection_new            (const char       *propname1,
                                                ...);
diff --git a/libsoup/soup-message-filter.c b/libsoup/soup-message-filter.c
new file mode 100644 (file)
index 0000000..4b516c1
--- /dev/null
@@ -0,0 +1,30 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-filter-offset: 8 -*- */
+/*
+ * soup-message-filter.c: Interface for arbitrary message manipulation
+ *
+ * Copyright (C) 2003, Ximian, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "soup-message-filter.h"
+
+SOUP_MAKE_INTERFACE (soup_message_filter, SoupMessageFilter, NULL)
+
+/**
+ * soup_message_filter_setup_message:
+ * @filter: an object that implements the #SoupMessageFilter interface
+ * @msg: a #SoupMessage
+ *
+ * Performs some sort of processing on @msg in preparation for it
+ * being sent. This will generally involve some combination of adding
+ * headers, adding handlers, and connecting to signals.
+ **/
+void
+soup_message_filter_setup_message (SoupMessageFilter *filter,
+                                  SoupMessage       *msg)
+{
+       SOUP_MESSAGE_FILTER_GET_CLASS (filter)->setup_message (filter, msg);
+}
diff --git a/libsoup/soup-message-filter.h b/libsoup/soup-message-filter.h
new file mode 100644 (file)
index 0000000..a5471cf
--- /dev/null
@@ -0,0 +1,30 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2000-2003, Ximian, Inc.
+ */
+
+#ifndef SOUP_MESSAGE_FILTER_H
+#define SOUP_MESSAGE_FILTER_H 1
+
+#include <libsoup/soup-types.h>
+
+#define SOUP_TYPE_MESSAGE_FILTER            (soup_message_filter_get_type ())
+#define SOUP_MESSAGE_FILTER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_MESSAGE_FILTER, SoupMessageFilter))
+#define SOUP_MESSAGE_FILTER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_MESSAGE_FILTER, SoupMessageFilterClass))
+#define SOUP_IS_MESSAGE_FILTER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_MESSAGE_FILTER))
+#define SOUP_IS_MESSAGE_FILTER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SOUP_TYPE_MESSAGE_FILTER))
+#define SOUP_MESSAGE_FILTER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_INTERFACE ((obj), SOUP_TYPE_MESSAGE_FILTER, SoupMessageFilterClass))
+
+typedef struct {
+       GTypeInterface parent;
+
+       /* methods */
+       void  (*setup_message) (SoupMessageFilter *filter, SoupMessage *msg);
+} SoupMessageFilterClass;
+
+GType soup_message_filter_get_type (void);
+
+void soup_message_filter_setup_message (SoupMessageFilter *filter,
+                                       SoupMessage       *msg);
+
+#endif /* SOUP_MESSAGE_FILTER_H */
index 6d85825..7fb4bfd 100644 (file)
@@ -602,6 +602,10 @@ soup_message_is_keepalive (SoupMessage *msg)
        c_conn = soup_message_get_header (msg->request_headers, "Connection");
        s_conn = soup_message_get_header (msg->response_headers, "Connection");
 
+       if (msg->status_code == SOUP_STATUS_OK &&
+           soup_method_get_id (msg->method) == SOUP_METHOD_ID_CONNECT)
+               return TRUE;
+
        if (msg->priv->http_version == SOUP_HTTP_1_0) {
                /* Only persistent if the client requested keepalive
                 * and the server agreed.
index 1a61d61..f6addaf 100644 (file)
@@ -133,7 +133,7 @@ run_queue (SoupSessionAsync *sa, gboolean try_pruning)
                        soup_connection_connect_async (conn, got_connection,
                                                       session);
                } else
-                       soup_session_send_message_via (session, msg, conn);
+                       soup_connection_send_request (conn, msg);
 
                started_any = TRUE;
        }
index 5c07804..6311073 100644 (file)
@@ -170,7 +170,7 @@ send_message (SoupSession *session, SoupMessage *msg)
                 * until either it's done, or the connection is closed.
                 */
                while (msg->status != SOUP_MESSAGE_STATUS_FINISHED && conn)
-                       soup_session_send_message_via (session, msg, conn);
+                       soup_connection_send_request (conn, msg);
 
                if (conn) {
                        g_object_remove_weak_pointer (G_OBJECT (conn),
index 65da464..6922740 100644 (file)
@@ -18,6 +18,7 @@
 #include "soup-connection.h"
 #include "soup-connection-ntlm.h"
 #include "soup-marshal.h"
+#include "soup-message-filter.h"
 #include "soup-message-queue.h"
 #include "soup-ssl.h"
 #include "soup-uri.h"
@@ -40,6 +41,8 @@ struct SoupSessionPrivate {
        char *ssl_ca_file;
        gpointer ssl_creds;
 
+       GSList *filters;
+
        GHashTable *hosts; /* SoupUri -> SoupSessionHost */
        GHashTable *conns; /* SoupConnection -> SoupSessionHost */
        guint num_conns;
@@ -57,6 +60,8 @@ static guint    host_uri_hash  (gconstpointer key);
 static gboolean host_uri_equal (gconstpointer v1, gconstpointer v2);
 static void     free_host      (SoupSessionHost *host, SoupSession *session);
 
+static void setup_message   (SoupMessageFilter *filter, SoupMessage *msg);
+
 static void queue_message   (SoupSession *session, SoupMessage *msg,
                             SoupMessageCallbackFn callback,
                             gpointer user_data);
@@ -128,10 +133,18 @@ static void
 dispose (GObject *object)
 {
        SoupSession *session = SOUP_SESSION (object);
+       GSList *f;
 
        soup_session_abort (session);
        cleanup_hosts (session);
 
+       if (session->priv->filters) {
+               for (f = session->priv->filters; f; f = f->next)
+                       g_object_unref (f->data);
+               g_slist_free (session->priv->filters);
+               session->priv->filters = NULL;
+       }
+
        G_OBJECT_CLASS (parent_class)->dispose (object);
 }
 
@@ -236,7 +249,14 @@ class_init (GObjectClass *object_class)
                                      G_PARAM_READWRITE));
 }
 
-SOUP_MAKE_TYPE (soup_session, SoupSession, class_init, init, PARENT_TYPE)
+static void
+filter_iface_init (SoupMessageFilterClass *filter_class)
+{
+       /* interface implementation */
+       filter_class->setup_message = setup_message;
+}
+
+SOUP_MAKE_TYPE_WITH_IFACE (soup_session, SoupSession, class_init, init, PARENT_TYPE, filter_iface_init, SOUP_TYPE_MESSAGE_FILTER)
 
 static gboolean
 safe_uri_equal (const SoupUri *a, const SoupUri *b)
@@ -324,6 +344,42 @@ get_property (GObject *object, guint prop_id,
 }
 
 
+/**
+ * soup_session_add_filter:
+ * @session: a #SoupSession
+ * @filter: an object implementing the #SoupMessageFilter interface
+ *
+ * Adds @filter to @session's list of message filters to be applied to
+ * all messages.
+ **/
+void
+soup_session_add_filter (SoupSession *session, SoupMessageFilter *filter)
+{
+       g_return_if_fail (SOUP_IS_SESSION (session));
+       g_return_if_fail (SOUP_IS_MESSAGE_FILTER (filter));
+
+       session->priv->filters = g_slist_prepend (session->priv->filters,
+                                                 filter);
+}
+
+/**
+ * soup_session_remove_filter:
+ * @session: a #SoupSession
+ * @filter: an object implementing the #SoupMessageFilter interface
+ *
+ * Removes @filter from @session's list of message filters
+ **/
+void
+soup_session_remove_filter (SoupSession *session, SoupMessageFilter *filter)
+{
+       g_return_if_fail (SOUP_IS_SESSION (session));
+       g_return_if_fail (SOUP_IS_MESSAGE_FILTER (filter));
+
+       session->priv->filters = g_slist_remove (session->priv->filters,
+                                                filter);
+}
+
+
 /* Hosts */
 static guint
 host_uri_hash (gconstpointer key)
@@ -728,16 +784,30 @@ add_auth (SoupSession *session, SoupMessage *msg, gboolean proxy)
        }
 }
 
-void
-soup_session_send_message_via (SoupSession *session, SoupMessage *msg,
-                              SoupConnection *conn)
+static void
+setup_message (SoupMessageFilter *filter, SoupMessage *msg)
 {
-       msg->status = SOUP_MESSAGE_STATUS_RUNNING;
+       SoupSession *session = SOUP_SESSION (filter);
+       GSList *f;
+
+       for (f = session->priv->filters; f; f = f->next) {
+               filter = f->data;
+               soup_message_filter_setup_message (filter, msg);
+       }
 
        add_auth (session, msg, FALSE);
-       if (session->priv->proxy_uri)
+       soup_message_add_status_code_handler (
+               msg, SOUP_STATUS_UNAUTHORIZED,
+               SOUP_HANDLER_POST_BODY,
+               authorize_handler, session);
+
+       if (session->priv->proxy_uri) {
                add_auth (session, msg, TRUE);
-       soup_connection_send_request (conn, msg);
+               soup_message_add_status_code_handler  (
+                       msg, SOUP_STATUS_PROXY_UNAUTHORIZED,
+                       SOUP_HANDLER_POST_BODY,
+                       authorize_handler, session);
+       }
 }
 
 static void
@@ -847,11 +917,24 @@ connect_result (SoupConnection *conn, guint status, gpointer user_data)
                return;
        }
 
-       /* It's hopeless. Cancel everything that was waiting for this host. */
+       /* There are two possibilities: either status is
+        * SOUP_STATUS_TRY_AGAIN, in which case the session implementation
+        * will create a new connection (and all we need to do here
+        * is downgrade the message from CONNECTING to QUEUED); or
+        * status is something else, probably CANT_CONNECT or
+        * CANT_RESOLVE or the like, in which case we need to cancel
+        * any messages waiting for this host, since they're out
+        * of luck.
+        */
        for (msg = soup_message_queue_first (session->queue, &iter); msg; msg = soup_message_queue_next (session->queue, &iter)) {
                if (get_host_for_message (session, msg) == host) {
-                       soup_message_set_status (msg, status);
-                       soup_session_cancel_message (session, msg);
+                       if (status == SOUP_STATUS_TRY_AGAIN) {
+                               if (msg->status == SOUP_MESSAGE_STATUS_CONNECTING)
+                                       msg->status = SOUP_MESSAGE_STATUS_QUEUED;
+                       } else {
+                               soup_message_set_status (msg, status);
+                               soup_session_cancel_message (session, msg);
+                       }
                }
        }
 }
@@ -932,6 +1015,7 @@ soup_session_get_connection (SoupSession *session, SoupMessage *msg,
                SOUP_CONNECTION_ORIGIN_URI, host->root_uri,
                SOUP_CONNECTION_PROXY_URI, session->priv->proxy_uri,
                SOUP_CONNECTION_SSL_CREDENTIALS, session->priv->ssl_creds,
+               SOUP_CONNECTION_MESSAGE_FILTER, session,
                NULL);
        g_signal_connect (conn, "connect_result",
                          G_CALLBACK (connect_result),
@@ -983,14 +1067,6 @@ queue_message (SoupSession *session, SoupMessage *msg,
        g_signal_connect_after (msg, "finished",
                                G_CALLBACK (message_finished), session);
 
-       soup_message_add_status_code_handler  (msg, SOUP_STATUS_UNAUTHORIZED,
-                                              SOUP_HANDLER_POST_BODY,
-                                              authorize_handler, session);
-       soup_message_add_status_code_handler  (msg,
-                                              SOUP_STATUS_PROXY_UNAUTHORIZED,
-                                              SOUP_HANDLER_POST_BODY,
-                                              authorize_handler, session);
-
        if (!(soup_message_get_flags (msg) & SOUP_MESSAGE_NO_REDIRECT)) {
                soup_message_add_status_class_handler (
                        msg, SOUP_STATUS_CLASS_REDIRECT,
index a8f9bf1..8e5be50 100644 (file)
@@ -58,6 +58,11 @@ GType soup_session_get_type (void);
 #define SOUP_SESSION_USE_NTLM           "use-ntlm"
 #define SOUP_SESSION_SSL_CA_FILE        "ssl-ca-file"
 
+void            soup_session_add_filter       (SoupSession           *session,
+                                              SoupMessageFilter     *filter);
+void            soup_session_remove_filter    (SoupSession           *session,
+                                              SoupMessageFilter     *filter);
+
 void            soup_session_queue_message    (SoupSession           *session,
                                               SoupMessage           *msg,
                                               SoupMessageCallbackFn  callback,
@@ -80,9 +85,5 @@ SoupConnection *soup_session_get_connection       (SoupSession    *session,
                                                   gboolean       *is_new);
 gboolean        soup_session_try_prune_connection (SoupSession    *session);
 
-void            soup_session_send_message_via     (SoupSession    *session,
-                                                  SoupMessage    *msg,
-                                                  SoupConnection *conn);
-
 
 #endif /* SOUP_SESSION_H */
index 1f60101..83b5bad 100644 (file)
@@ -22,6 +22,7 @@ struct {
        { SOUP_STATUS_SSL_FAILED,                 "SSL handshake failed" },
        { SOUP_STATUS_IO_ERROR,                   "Connection terminated unexpectedly" },
        { SOUP_STATUS_MALFORMED,                  "Message Corrupt" },
+       /* SOUP_STATUS_TRY_AGAIN should never be returned to the caller */
 
        /* Informational */
        { SOUP_STATUS_CONTINUE,                   "Continue" },
index fd8f984..d4d643b 100644 (file)
@@ -34,6 +34,7 @@ typedef enum {
        SOUP_STATUS_SSL_FAILED,
        SOUP_STATUS_IO_ERROR,
        SOUP_STATUS_MALFORMED,
+       SOUP_STATUS_TRY_AGAIN,
 
        /* HTTP Status Codes */
        SOUP_STATUS_CONTINUE                        = 100,
index ffdcbba..c7a97aa 100644 (file)
@@ -14,6 +14,7 @@
 typedef struct SoupAddress           SoupAddress;
 typedef struct SoupConnection        SoupConnection;
 typedef struct SoupMessage           SoupMessage;
+typedef struct SoupMessageFilter     SoupMessageFilter;
 typedef struct SoupServer            SoupServer;
 typedef union  SoupServerAuth        SoupServerAuth;
 typedef struct SoupServerAuthContext SoupServerAuthContext;
@@ -24,26 +25,80 @@ typedef struct SoupSessionSync       SoupSessionSync;
 typedef struct SoupSocket            SoupSocket;
 typedef struct SoupUri               SoupUri;
 
-#define SOUP_MAKE_TYPE(l,t,ci,i,parent) \
-GType l##_get_type(void)\
+#define SOUP_MAKE_TYPE(type_name,TypeName,class_init,init,parent) \
+GType type_name##_get_type(void)\
 {\
        static GType type = 0;                          \
        if (!type){                                     \
                static GTypeInfo const object_info = {  \
-                       sizeof (t##Class),              \
+                       sizeof (TypeName##Class),       \
                                                        \
                        (GBaseInitFunc) NULL,           \
                        (GBaseFinalizeFunc) NULL,       \
                                                        \
-                       (GClassInitFunc) ci,            \
+                       (GClassInitFunc) class_init,    \
                        (GClassFinalizeFunc) NULL,      \
                        NULL,   /* class_data */        \
                                                        \
-                       sizeof (t),                     \
+                       sizeof (TypeName),              \
                        0,      /* n_preallocs */       \
-                       (GInstanceInitFunc) i,          \
+                       (GInstanceInitFunc) init,       \
                };                                      \
-               type = g_type_register_static (parent, #t, &object_info, 0); \
+               type = g_type_register_static (parent, #TypeName, &object_info, 0); \
+       }                                               \
+       return type;                                    \
+}
+
+#define SOUP_MAKE_INTERFACE(type_name,TypeName,base_init) \
+GType type_name##_get_type(void)\
+{\
+       static GType type = 0;                          \
+       if (!type){                                     \
+               static GTypeInfo const object_info = {  \
+                       sizeof (TypeName##Class),       \
+                                                       \
+                       (GBaseInitFunc) base_init,      \
+                       (GBaseFinalizeFunc) NULL,       \
+                                                       \
+                       (GClassInitFunc) NULL,          \
+                       (GClassFinalizeFunc) NULL,      \
+                       NULL,   /* class_data */        \
+                                                       \
+                       0,                              \
+                       0,      /* n_preallocs */       \
+                       (GInstanceInitFunc) NULL,       \
+               };                                      \
+               type = g_type_register_static (G_TYPE_INTERFACE, #TypeName, &object_info, 0); \
+       }                                               \
+       return type;                                    \
+}
+
+#define SOUP_MAKE_TYPE_WITH_IFACE(type_name,TypeName,class_init,init,parent,iface_init,iparent) \
+GType type_name##_get_type(void)\
+{\
+       static GType type = 0;                          \
+       if (!type){                                     \
+               static GTypeInfo const object_info = {  \
+                       sizeof (TypeName##Class),       \
+                                                       \
+                       (GBaseInitFunc) NULL,           \
+                       (GBaseFinalizeFunc) NULL,       \
+                                                       \
+                       (GClassInitFunc) class_init,    \
+                       (GClassFinalizeFunc) NULL,      \
+                       NULL,   /* class_data */        \
+                                                       \
+                       sizeof (TypeName),              \
+                       0,      /* n_preallocs */       \
+                       (GInstanceInitFunc) init,       \
+               };                                      \
+               static GInterfaceInfo const iface_info = {      \
+                       (GInterfaceInitFunc) iface_init,        \
+                       NULL,                                   \
+                       NULL                                    \
+               };                                              \
+               type = g_type_register_static (parent, #TypeName, &object_info, 0);     \
+               g_type_add_interface_static (type, iparent, &iface_info);               \
        }                                               \
        return type;                                    \
 }
index 6956652..5c7e5d0 100644 (file)
@@ -205,17 +205,27 @@ int
 main (int argc, char **argv)
 {
        const char *cafile = NULL;
+       SoupUri *proxy = NULL;
        int opt;
 
        g_type_init ();
        g_thread_init (NULL);
 
-       while ((opt = getopt (argc, argv, "c:r")) != -1) {
+       while ((opt = getopt (argc, argv, "c:p:r")) != -1) {
                switch (opt) {
                case 'c':
                        cafile = optarg;
                        break;
 
+               case 'p':
+                       proxy = soup_uri_new (optarg);
+                       if (!proxy) {
+                               fprintf (stderr, "Could not parse %s as URI\n",
+                                        optarg);
+                               exit (1);
+                       }
+                       break;
+
                case 'r':
                        recurse = TRUE;
                        break;
@@ -239,6 +249,7 @@ main (int argc, char **argv)
 
        session = soup_session_async_new_with_options (
                SOUP_SESSION_SSL_CA_FILE, cafile,
+               SOUP_SESSION_PROXY_URI, proxy,
                NULL);
 
        if (recurse) {
index 85b647c..c4ae793 100644 (file)
@@ -18,7 +18,7 @@
 #include <libsoup/soup-message.h>
 #include <libsoup/soup-server.h>
 #include <libsoup/soup-server-message.h>
-#include <libsoup/soup-session.h>
+#include <libsoup/soup-session-async.h>
 
 /* WARNING: this is really really really not especially compliant with
  * RFC 2616. But it does work for basic stuff.