From e81e61c7612793a1fa20812c2a329726dd29a2c8 Mon Sep 17 00:00:00 2001 From: Dan Winship Date: Fri, 19 Dec 2003 20:54:38 +0000 Subject: [PATCH] New. An interface for objects that want to act on every message passing * 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 --- ChangeLog | 48 ++++++++++++++++++ libsoup/Makefile.am | 2 + libsoup/soup-connection.c | 105 +++++++++++++++++++++++++++++++++++---- libsoup/soup-connection.h | 1 + libsoup/soup-message-filter.c | 30 +++++++++++ libsoup/soup-message-filter.h | 30 +++++++++++ libsoup/soup-message.c | 4 ++ libsoup/soup-session-async.c | 2 +- libsoup/soup-session-sync.c | 2 +- libsoup/soup-session.c | 112 +++++++++++++++++++++++++++++++++++------- libsoup/soup-session.h | 9 ++-- libsoup/soup-status.c | 1 + libsoup/soup-status.h | 1 + libsoup/soup-types.h | 69 +++++++++++++++++++++++--- tests/get.c | 13 ++++- tests/simple-proxy.c | 2 +- 16 files changed, 389 insertions(+), 42 deletions(-) create mode 100644 libsoup/soup-message-filter.c create mode 100644 libsoup/soup-message-filter.h diff --git a/ChangeLog b/ChangeLog index 594aced..9e91986 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,51 @@ +2003-12-19 Dan Winship + + * 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 * libsoup/soup-connection.c (soup_connection_disconnect): Actually diff --git a/libsoup/Makefile.am b/libsoup/Makefile.am index 458b9a8..342aee1 100644 --- a/libsoup/Makefile.am +++ b/libsoup/Makefile.am @@ -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 \ diff --git a/libsoup/soup-connection.c b/libsoup/soup-connection.c index 7c1535a..5e3d3bd 100644 --- a/libsoup/soup-connection.c +++ b/libsoup/soup-connection.c @@ -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); diff --git a/libsoup/soup-connection.h b/libsoup/soup-connection.h index d25b6a9..5d08b08 100644 --- a/libsoup/soup-connection.h +++ b/libsoup/soup-connection.h @@ -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 index 0000000..4b516c1 --- /dev/null +++ b/libsoup/soup-message-filter.c @@ -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 +#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 index 0000000..a5471cf --- /dev/null +++ b/libsoup/soup-message-filter.h @@ -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 + +#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 */ diff --git a/libsoup/soup-message.c b/libsoup/soup-message.c index 6d85825..7fb4bfd 100644 --- a/libsoup/soup-message.c +++ b/libsoup/soup-message.c @@ -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. diff --git a/libsoup/soup-session-async.c b/libsoup/soup-session-async.c index 1a61d61..f6addaf 100644 --- a/libsoup/soup-session-async.c +++ b/libsoup/soup-session-async.c @@ -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; } diff --git a/libsoup/soup-session-sync.c b/libsoup/soup-session-sync.c index 5c07804..6311073 100644 --- a/libsoup/soup-session-sync.c +++ b/libsoup/soup-session-sync.c @@ -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), diff --git a/libsoup/soup-session.c b/libsoup/soup-session.c index 65da464..6922740 100644 --- a/libsoup/soup-session.c +++ b/libsoup/soup-session.c @@ -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, diff --git a/libsoup/soup-session.h b/libsoup/soup-session.h index a8f9bf1..8e5be50 100644 --- a/libsoup/soup-session.h +++ b/libsoup/soup-session.h @@ -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 */ diff --git a/libsoup/soup-status.c b/libsoup/soup-status.c index 1f60101..83b5bad 100644 --- a/libsoup/soup-status.c +++ b/libsoup/soup-status.c @@ -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" }, diff --git a/libsoup/soup-status.h b/libsoup/soup-status.h index fd8f984..d4d643b 100644 --- a/libsoup/soup-status.h +++ b/libsoup/soup-status.h @@ -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, diff --git a/libsoup/soup-types.h b/libsoup/soup-types.h index ffdcbba..c7a97aa 100644 --- a/libsoup/soup-types.h +++ b/libsoup/soup-types.h @@ -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; \ } diff --git a/tests/get.c b/tests/get.c index 6956652..5c7e5d0 100644 --- a/tests/get.c +++ b/tests/get.c @@ -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) { diff --git a/tests/simple-proxy.c b/tests/simple-proxy.c index 85b647c..c4ae793 100644 --- a/tests/simple-proxy.c +++ b/tests/simple-proxy.c @@ -18,7 +18,7 @@ #include #include #include -#include +#include /* WARNING: this is really really really not especially compliant with * RFC 2616. But it does work for basic stuff. -- 2.7.4