[glib] Bug 624546 – Modification of GDBusMessage in filter function
- From: David Zeuthen <davidz src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib] Bug 624546 – Modification of GDBusMessage in filter function
- Date: Sun, 22 Aug 2010 15:22:14 +0000 (UTC)
commit 3ff9894826215790fdd6c8b53584f94a7172c39f
Author: David Zeuthen <davidz redhat com>
Date: Thu Aug 5 20:37:27 2010 -0400
Bug 624546 â?? Modification of GDBusMessage in filter function
Allow modifying a GDBusMessage in a filter function and also add tests
for this. This breaks API but leaves ABI (almost) intact - at least
dconf's GSettings backend (the only big user I know of) will keep
working.
https://bugzilla.gnome.org/show_bug.cgi?id=624546
Signed-off-by: David Zeuthen <davidz redhat com>
docs/reference/gio/gio-sections.txt | 1 +
gio/gdbusconnection.c | 85 +++++++++++++++-----
gio/gdbusconnection.h | 18 +++--
gio/gdbusprivate.c | 59 +++++++++++---
gio/gdbusprivate.h | 6 +-
gio/gio.symbols | 1 +
gio/gioenums.h | 25 ++++++
gio/tests/gdbus-connection.c | 147 ++++++++++++++++++++++++++++++++++-
gio/tests/gdbus-peer.c | 4 +-
9 files changed, 298 insertions(+), 48 deletions(-)
---
diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt
index 19db3db..930f6cd 100644
--- a/docs/reference/gio/gio-sections.txt
+++ b/docs/reference/gio/gio-sections.txt
@@ -2425,6 +2425,7 @@ g_dbus_connection_send_message
g_dbus_connection_send_message_with_reply
g_dbus_connection_send_message_with_reply_finish
g_dbus_connection_send_message_with_reply_sync
+GDBusMessageFilterResult
GDBusMessageFilterFunction
g_dbus_connection_add_filter
g_dbus_connection_remove_filter
diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c
index 6a37203..c018787 100644
--- a/gio/gdbusconnection.c
+++ b/gio/gdbusconnection.c
@@ -1944,6 +1944,7 @@ on_worker_message_received (GDBusWorker *worker,
GDBusConnection *connection = G_DBUS_CONNECTION (user_data);
FilterCallback *filters;
gboolean consumed_by_filter;
+ gboolean altered_by_filter;
guint num_filters;
guint n;
@@ -1965,17 +1966,39 @@ on_worker_message_received (GDBusWorker *worker,
/* the call the filters in order (without holding the lock) */
consumed_by_filter = FALSE;
+ altered_by_filter = FALSE;
for (n = 0; n < num_filters; n++)
{
- consumed_by_filter = filters[n].func (connection,
- message,
- TRUE,
- filters[n].user_data);
+ GDBusMessageFilterResult result;
+ result = filters[n].func (connection,
+ message,
+ TRUE,
+ filters[n].user_data);
+ switch (result)
+ {
+ case G_DBUS_MESSAGE_FILTER_RESULT_NO_EFFECT:
+ /* do nothing */
+ break;
+
+ default:
+ g_warning ("Treating unknown value %d for GDBusMessageFilterResult from filter "
+ "function on incoming message as MESSAGE_CONSUMED.", result);
+ /* explicit fallthrough */
+ case G_DBUS_MESSAGE_FILTER_RESULT_MESSAGE_CONSUMED:
+ consumed_by_filter = TRUE;
+ break;
+
+ case G_DBUS_MESSAGE_FILTER_RESULT_MESSAGE_ALTERED:
+ altered_by_filter = TRUE;
+ break;
+ }
if (consumed_by_filter)
break;
}
- /* Standard dispatch unless the filter ate the message */
+ /* Standard dispatch unless the filter ate the message - no need to
+ * do anything if the message was altered
+ */
if (!consumed_by_filter)
{
GDBusMessageType message_type;
@@ -2020,19 +2043,21 @@ on_worker_message_received (GDBusWorker *worker,
}
/* Called in worker's thread */
-static gboolean
+static GDBusMessageFilterResult
on_worker_message_about_to_be_sent (GDBusWorker *worker,
GDBusMessage *message,
gpointer user_data)
{
GDBusConnection *connection = G_DBUS_CONNECTION (user_data);
FilterCallback *filters;
- gboolean consumed_by_filter;
guint num_filters;
guint n;
+ GDBusMessageFilterResult ret;
//g_debug ("in on_worker_message_about_to_be_sent");
+ ret = G_DBUS_MESSAGE_FILTER_RESULT_NO_EFFECT;
+
g_object_ref (connection);
/* First collect the set of callback functions */
@@ -2047,22 +2072,39 @@ on_worker_message_about_to_be_sent (GDBusWorker *worker,
}
CONNECTION_UNLOCK (connection);
- /* the call the filters in order (without holding the lock) */
- consumed_by_filter = FALSE;
+ /* then call the filters in order (without holding the lock) */
for (n = 0; n < num_filters; n++)
{
- consumed_by_filter = filters[n].func (connection,
- message,
- FALSE,
- filters[n].user_data);
- if (consumed_by_filter)
- break;
+ GDBusMessageFilterResult result;
+ result = filters[n].func (connection,
+ message,
+ FALSE,
+ filters[n].user_data);
+ switch (result)
+ {
+ case G_DBUS_MESSAGE_FILTER_RESULT_NO_EFFECT:
+ /* do nothing, ret might already be _ALTERED */
+ break;
+
+ default:
+ g_warning ("Treating unknown value %d for GDBusMessageFilterResult from filter "
+ "function on outgoing message as MESSAGE_CONSUMED.", result);
+ /* explicit fallthrough */
+ case G_DBUS_MESSAGE_FILTER_RESULT_MESSAGE_CONSUMED:
+ ret = G_DBUS_MESSAGE_FILTER_RESULT_MESSAGE_CONSUMED;
+ goto out;
+
+ case G_DBUS_MESSAGE_FILTER_RESULT_MESSAGE_ALTERED:
+ ret = G_DBUS_MESSAGE_FILTER_RESULT_MESSAGE_ALTERED;
+ break;
+ }
}
+ out:
g_object_unref (connection);
g_free (filters);
- return consumed_by_filter;
+ return ret;
}
/* Called in worker's thread - we must not block */
@@ -2674,7 +2716,9 @@ static guint _global_filter_id = 1;
* are run in the order that they were added. The same handler can be
* added as a filter more than once, in which case it will be run more
* than once. Filters added during a filter callback won't be run on
- * the message being processed.
+ * the message being processed. Filter functions are allowed to modify
+ * and even drop messages - see the #GDBusMessageFilterResult
+ * enumeration for details.
*
* Note that filters are run in a dedicated message handling thread so
* they can't block and, generally, can't do anything but signal a
@@ -2683,10 +2727,9 @@ static guint _global_filter_id = 1;
* g_dbus_connection_signal_subscribe() or
* g_dbus_connection_call() instead.
*
- * If a filter consumes an incoming message (by returning %TRUE), the
- * message is not dispatched anywhere else - not even the standard
- * dispatch machinery (that API such as
- * g_dbus_connection_signal_subscribe() and
+ * If a filter consumes an incoming message the message is not
+ * dispatched anywhere else - not even the standard dispatch machinery
+ * (that API such as g_dbus_connection_signal_subscribe() and
* g_dbus_connection_send_message_with_reply() relies on) will see the
* message. Similary, if a filter consumes an outgoing message, the
* message will not be sent to the other peer.
diff --git a/gio/gdbusconnection.h b/gio/gdbusconnection.h
index d130ef1..92d477f 100644
--- a/gio/gdbusconnection.h
+++ b/gio/gdbusconnection.h
@@ -475,15 +475,21 @@ void g_dbus_connection_signal_unsubscribe (GDBusConnection
*
* Signature for function used in g_dbus_connection_add_filter().
*
- * Returns: %TRUE if the filter handled @message, %FALSE to let other
- * handlers run.
+ * If you modify an outgoing message, make sure to return
+ * %G_DBUS_MESSAGE_FILTER_RESULT_MESSAGE_ALTERED instead of
+ * %G_DBUS_MESSAGE_FILTER_RESULT_NO_EFFECT so the message can be
+ * re-serialized. If an error occurs during re-serialization, a
+ * warning will be printed on standard error.
+ *
+ * Returns: A value from the #GDBusMessageFilterResult enumeration
+ * describing what to do with @message.
*
* Since: 2.26
*/
-typedef gboolean (*GDBusMessageFilterFunction) (GDBusConnection *connection,
- GDBusMessage *message,
- gboolean incoming,
- gpointer user_data);
+typedef GDBusMessageFilterResult (*GDBusMessageFilterFunction) (GDBusConnection *connection,
+ GDBusMessage *message,
+ gboolean incoming,
+ gpointer user_data);
guint g_dbus_connection_add_filter (GDBusConnection *connection,
GDBusMessageFilterFunction filter_function,
diff --git a/gio/gdbusprivate.c b/gio/gdbusprivate.c
index 2e4a104..ccce66e 100644
--- a/gio/gdbusprivate.c
+++ b/gio/gdbusprivate.c
@@ -468,12 +468,12 @@ _g_dbus_worker_emit_message_received (GDBusWorker *worker,
worker->message_received_callback (worker, message, worker->user_data);
}
-static gboolean
+static GDBusMessageFilterResult
_g_dbus_worker_emit_message_about_to_be_sent (GDBusWorker *worker,
GDBusMessage *message)
{
- gboolean ret;
- ret = FALSE;
+ GDBusMessageFilterResult ret;
+ ret = G_DBUS_MESSAGE_FILTER_RESULT_NO_EFFECT;
if (!worker->stopped)
ret = worker->message_about_to_be_sent_callback (worker, message, worker->user_data);
return ret;
@@ -1241,23 +1241,56 @@ maybe_write_next_message (GDBusWorker *worker)
*/
if (data != NULL)
{
- gboolean message_was_dropped;
- message_was_dropped = _g_dbus_worker_emit_message_about_to_be_sent (worker, data->message);
- if (G_UNLIKELY (message_was_dropped))
+ GDBusMessageFilterResult filter_result;
+ guchar *new_blob;
+ gsize new_blob_size;
+ GError *error;
+
+ filter_result = _g_dbus_worker_emit_message_about_to_be_sent (worker, data->message);
+ switch (filter_result)
{
+ case G_DBUS_MESSAGE_FILTER_RESULT_NO_EFFECT:
+ /* do nothing */
+ break;
+
+ case G_DBUS_MESSAGE_FILTER_RESULT_MESSAGE_CONSUMED:
+ /* drop message */
g_mutex_lock (worker->write_lock);
worker->num_writes_pending -= 1;
g_mutex_unlock (worker->write_lock);
message_to_write_data_free (data);
goto write_next;
+
+ case G_DBUS_MESSAGE_FILTER_RESULT_MESSAGE_ALTERED:
+ /* reencode altered message */
+ error = NULL;
+ new_blob = g_dbus_message_to_blob (data->message,
+ &new_blob_size,
+ worker->capabilities,
+ &error);
+ if (new_blob == NULL)
+ {
+ /* if filter make the GDBusMessage unencodeable, just complain on stderr and send
+ * the old message instead
+ */
+ g_warning ("Error encoding GDBusMessage with serial %d altered by filter function: %s",
+ g_dbus_message_get_serial (data->message),
+ error->message);
+ g_error_free (error);
+ }
+ else
+ {
+ g_free (data->blob);
+ data->blob = (gchar *) new_blob;
+ data->blob_size = new_blob_size;
+ }
+ break;
}
- else
- {
- write_message_async (worker,
- data,
- write_message_cb,
- data);
- }
+
+ write_message_async (worker,
+ data,
+ write_message_cb,
+ data);
}
}
diff --git a/gio/gdbusprivate.h b/gio/gdbusprivate.h
index ae8d416..ec58c11 100644
--- a/gio/gdbusprivate.h
+++ b/gio/gdbusprivate.h
@@ -39,9 +39,9 @@ typedef void (*GDBusWorkerMessageReceivedCallback) (GDBusWorker *worker,
GDBusMessage *message,
gpointer user_data);
-typedef gboolean (*GDBusWorkerMessageAboutToBeSentCallback) (GDBusWorker *worker,
- GDBusMessage *message,
- gpointer user_data);
+typedef GDBusMessageFilterResult (*GDBusWorkerMessageAboutToBeSentCallback) (GDBusWorker *worker,
+ GDBusMessage *message,
+ gpointer user_data);
typedef void (*GDBusWorkerDisconnectedCallback) (GDBusWorker *worker,
gboolean remote_peer_vanished,
diff --git a/gio/gio.symbols b/gio/gio.symbols
index a458893..7993e8d 100644
--- a/gio/gio.symbols
+++ b/gio/gio.symbols
@@ -1040,6 +1040,7 @@ g_dbus_signal_flags_get_type G_GNUC_CONST
g_dbus_send_message_flags_get_type G_GNUC_CONST
g_credentials_type_get_type G_GNUC_CONST
g_dbus_message_byte_order_get_type G_GNUC_CONST
+g_dbus_message_filter_result_get_type G_GNUC_CONST
#endif
#endif
diff --git a/gio/gioenums.h b/gio/gioenums.h
index 967b061..482e7f2 100644
--- a/gio/gioenums.h
+++ b/gio/gioenums.h
@@ -1214,6 +1214,31 @@ typedef enum
G_DBUS_MESSAGE_BYTE_ORDER_LITTLE_ENDIAN = 'l'
} GDBusMessageByteOrder;
+/**
+ * GDBusMessageFilterResult:
+ * @G_DBUS_MESSAGE_FILTER_RESULT_NO_EFFECT: The filter function had
+ * no effect on the message - the message will be passed on to the
+ * next filter function and/or sent to the remote peer.
+ * @G_DBUS_MESSAGE_FILTER_RESULT_MESSAGE_CONSUMED: The message was
+ * consumed by the filter function and will be dropped - the message
+ * will not be passed to other filter functions and/or sent to the
+ * remote peer.
+ * @G_DBUS_MESSAGE_FILTER_RESULT_MESSAGE_ALTERED: The message was
+ * modified - the message will still be passed on to the next filter
+ * function and/or sent to the remote peer.
+ *
+ * Possible return values for #GDBusMessageFilterFunction when
+ * handling a #GDBusMessage.
+ *
+ * Since: 2.26
+ */
+typedef enum
+{
+ G_DBUS_MESSAGE_FILTER_RESULT_NO_EFFECT,
+ G_DBUS_MESSAGE_FILTER_RESULT_MESSAGE_CONSUMED,
+ G_DBUS_MESSAGE_FILTER_RESULT_MESSAGE_ALTERED
+} GDBusMessageFilterResult;
+
G_END_DECLS
#endif /* __GIO_ENUMS_H__ */
diff --git a/gio/tests/gdbus-connection.c b/gio/tests/gdbus-connection.c
index e06e1fd..5f0ff2b 100644
--- a/gio/tests/gdbus-connection.c
+++ b/gio/tests/gdbus-connection.c
@@ -62,13 +62,13 @@ static const GDBusInterfaceVTable boo_vtable =
NULL /* _set_property */
};
-static gboolean
+static GDBusMessageFilterResult
some_filter_func (GDBusConnection *connection,
GDBusMessage *message,
gboolean incoming,
gpointer user_data)
{
- return FALSE;
+ return G_DBUS_MESSAGE_FILTER_RESULT_NO_EFFECT;
}
static void
@@ -686,7 +686,7 @@ typedef struct
guint32 serial;
} FilterData;
-static gboolean
+static GDBusMessageFilterResult
filter_func (GDBusConnection *connection,
GDBusMessage *message,
gboolean incoming,
@@ -706,6 +706,75 @@ filter_func (GDBusConnection *connection,
data->num_outgoing += 1;
}
+ return G_DBUS_MESSAGE_FILTER_RESULT_NO_EFFECT;
+}
+
+typedef struct
+{
+ GDBusMessageFilterResult incoming;
+ GDBusMessageFilterResult outgoing;
+} FilterEffects;
+
+static GDBusMessageFilterResult
+other_filter_func (GDBusConnection *connection,
+ GDBusMessage *message,
+ gboolean incoming,
+ gpointer user_data)
+{
+ FilterEffects *effects = user_data;
+ GDBusMessageFilterResult ret;
+
+ if (incoming)
+ ret = effects->incoming;
+ else
+ ret = effects->outgoing;
+
+ if (ret == G_DBUS_MESSAGE_FILTER_RESULT_MESSAGE_ALTERED)
+ {
+ GVariant *body;
+ gchar *s;
+ gchar *s2;
+ body = g_dbus_message_get_body (message);
+ g_variant_get (body, "(s)", &s);
+ s2 = g_strdup_printf ("MOD: %s", s);
+ g_dbus_message_set_body (message, g_variant_new ("(s)", s2));
+ g_free (s2);
+ g_free (s);
+ }
+
+ return ret;
+}
+
+static void
+test_connection_filter_name_owner_changed_signal_handler (GDBusConnection *connection,
+ const gchar *sender_name,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *signal_name,
+ GVariant *parameters,
+ gpointer user_data)
+{
+ const gchar *name;
+ const gchar *old_owner;
+ const gchar *new_owner;
+
+ g_variant_get (parameters,
+ "(&s&s&s)",
+ &name,
+ &old_owner,
+ &new_owner);
+
+ if (g_strcmp0 (name, "com.example.TestService") == 0 && strlen (new_owner) > 0)
+ {
+ g_main_loop_quit (loop);
+ }
+}
+
+static gboolean
+test_connection_filter_on_timeout (gpointer user_data)
+{
+ g_printerr ("Timeout waiting 1000 msec on service\n");
+ g_assert_not_reached ();
return FALSE;
}
@@ -718,6 +787,11 @@ test_connection_filter (void)
GDBusMessage *r;
GError *error;
guint filter_id;
+ guint timeout_mainloop_id;
+ guint signal_handler_id;
+ FilterEffects effects;
+ GVariant *result;
+ const gchar *s;
memset (&data, '\0', sizeof (FilterData));
@@ -781,6 +855,73 @@ test_connection_filter (void)
g_assert_cmpint (data.num_handled, ==, 3);
g_assert_cmpint (data.num_outgoing, ==, 3);
+ /* this is safe; testserver will exit once the bus goes away */
+ g_assert (g_spawn_command_line_async (SRCDIR "/gdbus-testserver.py", NULL));
+ /* wait for service to be available */
+ signal_handler_id = g_dbus_connection_signal_subscribe (c,
+ "org.freedesktop.DBus", /* sender */
+ "org.freedesktop.DBus",
+ "NameOwnerChanged",
+ "/org/freedesktop/DBus",
+ NULL, /* arg0 */
+ G_DBUS_SIGNAL_FLAGS_NONE,
+ test_connection_filter_name_owner_changed_signal_handler,
+ NULL,
+ NULL);
+ g_assert_cmpint (signal_handler_id, !=, 0);
+ timeout_mainloop_id = g_timeout_add (1000, test_connection_filter_on_timeout, NULL);
+ g_main_loop_run (loop);
+ g_source_remove (timeout_mainloop_id);
+ g_dbus_connection_signal_unsubscribe (c, signal_handler_id);
+
+ /* now test all nine combinations... */
+
+ filter_id = g_dbus_connection_add_filter (c,
+ other_filter_func,
+ &effects,
+ NULL);
+ /* -- */
+ effects.incoming = G_DBUS_MESSAGE_FILTER_RESULT_NO_EFFECT;
+ effects.outgoing = G_DBUS_MESSAGE_FILTER_RESULT_NO_EFFECT;
+ error = NULL;
+ result = g_dbus_connection_call_sync (c,
+ "com.example.TestService", /* bus name */
+ "/com/example/TestObject", /* object path */
+ "com.example.Frob", /* interface name */
+ "HelloWorld", /* method name */
+ g_variant_new ("(s)", "Cat"), /* parameters */
+ G_VARIANT_TYPE ("(s)"), /* return type */
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ &error);
+ g_assert_no_error (error);
+ g_variant_get (result, "(&s)", &s);
+ g_assert_cmpstr (s, ==, "You greeted me with 'Cat'. Thanks!");
+ g_variant_unref (result);
+ /* -- */
+ effects.incoming = G_DBUS_MESSAGE_FILTER_RESULT_MESSAGE_ALTERED;
+ effects.outgoing = G_DBUS_MESSAGE_FILTER_RESULT_MESSAGE_ALTERED;
+ error = NULL;
+ result = g_dbus_connection_call_sync (c,
+ "com.example.TestService", /* bus name */
+ "/com/example/TestObject", /* object path */
+ "com.example.Frob", /* interface name */
+ "HelloWorld", /* method name */
+ g_variant_new ("(s)", "Cat"), /* parameters */
+ G_VARIANT_TYPE ("(s)"), /* return type */
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ &error);
+ g_assert_no_error (error);
+ g_variant_get (result, "(&s)", &s);
+ g_assert_cmpstr (s, ==, "MOD: You greeted me with 'MOD: Cat'. Thanks!");
+ g_variant_unref (result);
+
+
+ g_dbus_connection_remove_filter (c, filter_id);
+
_g_object_wait_for_single_ref (c);
g_object_unref (c);
g_object_unref (m);
diff --git a/gio/tests/gdbus-peer.c b/gio/tests/gdbus-peer.c
index e400d3b..7c3eecd 100644
--- a/gio/tests/gdbus-peer.c
+++ b/gio/tests/gdbus-peer.c
@@ -1250,7 +1250,7 @@ test_credentials (void)
#define OVERFLOW_NUM_SIGNALS 5000
#define OVERFLOW_TIMEOUT_SEC 10
-static gboolean
+static GDBusMessageFilterResult
overflow_filter_func (GDBusConnection *connection,
GDBusMessage *message,
gboolean incoming,
@@ -1258,7 +1258,7 @@ overflow_filter_func (GDBusConnection *connection,
{
volatile gint *counter = user_data;
*counter += 1;
- return FALSE; /* don't drop the message */
+ return G_DBUS_MESSAGE_FILTER_RESULT_NO_EFFECT;
}
static gboolean
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]