[tracker/wip/carlosg/remote-module-reduction: 2/6] libtracker-sparql: Add HTTP module soup2/soup3 implementation
- From: Carlos Garnacho <carlosg src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [tracker/wip/carlosg/remote-module-reduction: 2/6] libtracker-sparql: Add HTTP module soup2/soup3 implementation
- Date: Sun, 24 Apr 2022 15:25:48 +0000 (UTC)
commit 381c754fe7fa763caa7afa090e3247ebae259e07
Author: Carlos Garnacho <carlosg gnome org>
Date: Sat Apr 23 13:49:44 2022 +0200
libtracker-sparql: Add HTTP module soup2/soup3 implementation
These modules implement TrackerHttpClient and TrackerHttpServer using
the 2 relevant major libsoup versions. These modules are built and
installed, but dormant and unused.
src/libtracker-sparql/meson.build | 4 +-
src/libtracker-sparql/remote/meson.build | 34 ++
src/libtracker-sparql/remote/tracker-http-module.c | 564 +++++++++++++++++++++
src/libtracker-sparql/remote/tracker-http-module.h | 39 ++
4 files changed, 639 insertions(+), 2 deletions(-)
---
diff --git a/src/libtracker-sparql/meson.build b/src/libtracker-sparql/meson.build
index bb0aee5a5..001c9fba7 100644
--- a/src/libtracker-sparql/meson.build
+++ b/src/libtracker-sparql/meson.build
@@ -1,3 +1,5 @@
+libtracker_sparql_modules = []
+
subdir('core')
subdir('bus')
subdir('direct')
@@ -165,8 +167,6 @@ libtracker_sparql_remote_vala_sources = files(
'remote/tracker-remote.vala',
)
-libtracker_sparql_modules = []
-
if libsoup2.found()
libtracker_remote_soup2 = shared_module('tracker-remote-soup2',
libtracker_sparql_remote_c_sources, libtracker_sparql_remote_vala_sources,
diff --git a/src/libtracker-sparql/remote/meson.build b/src/libtracker-sparql/remote/meson.build
index 5cc639795..ca137b85e 100644
--- a/src/libtracker-sparql/remote/meson.build
+++ b/src/libtracker-sparql/remote/meson.build
@@ -1,3 +1,37 @@
remote_files = files(
'tracker-http.c',
)
+
+module_sources = files('tracker-http-module.c')
+
+if libsoup2.found()
+ libtracker_http_soup2 = shared_module('tracker-http-soup2',
+ module_sources,
+ dependencies: [libsoup2],
+ c_args: tracker_c_args + [
+ '-include', 'config.h',
+ '-include', '../tracker-enums-private.h',
+ '-DMODULE',
+ ],
+ install: true,
+ install_dir: tracker_internal_libs_dir,
+ name_suffix: 'so',
+ )
+ libtracker_sparql_modules += libtracker_http_soup2
+endif
+
+if libsoup3.found()
+ libtracker_http_soup3 = shared_module('tracker-http-soup3',
+ module_sources,
+ dependencies: [libsoup3],
+ c_args: tracker_c_args + [
+ '-include', 'config.h',
+ '-include', '../tracker-enums-private.h',
+ '-DMODULE',
+ ],
+ install: true,
+ install_dir: tracker_internal_libs_dir,
+ name_suffix: 'so',
+ )
+ libtracker_sparql_modules += libtracker_http_soup3
+endif
diff --git a/src/libtracker-sparql/remote/tracker-http-module.c
b/src/libtracker-sparql/remote/tracker-http-module.c
new file mode 100644
index 000000000..421005001
--- /dev/null
+++ b/src/libtracker-sparql/remote/tracker-http-module.c
@@ -0,0 +1,564 @@
+/*
+ * Copyright (C) 2022, Red Hat Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * Author: Carlos Garnacho <carlosg gnome org>
+ */
+
+#include <libsoup/soup.h>
+
+#include "tracker-http-module.h"
+
+GType
+tracker_http_client_get_type (void)
+{
+ return g_type_from_name ("TrackerHttpClient");
+}
+
+GType
+tracker_http_server_get_type (void)
+{
+ return g_type_from_name ("TrackerHttpServer");
+}
+
+static const gchar *mimetypes[] = {
+ "application/sparql-results+json",
+ "application/sparql-results+xml",
+ "text/turtle",
+ "application/trig",
+};
+
+G_STATIC_ASSERT (G_N_ELEMENTS (mimetypes) == TRACKER_N_SERIALIZER_FORMATS);
+
+#define USER_AGENT "Tracker " PACKAGE_VERSION " (https://gitlab.gnome.org/GNOME/tracker/issues/)"
+
+/* Server */
+struct _TrackerHttpRequest
+{
+ TrackerHttpServer *server;
+#if SOUP_CHECK_VERSION (2, 99, 2)
+ SoupServerMessage *message;
+#else
+ SoupMessage *message;
+#endif
+ GTask *task;
+ GInputStream *istream;
+};
+
+struct _TrackerHttpServerSoup
+{
+ TrackerHttpServer parent_instance;
+ SoupServer *server;
+ GCancellable *cancellable;
+};
+
+static void tracker_http_server_soup_initable_iface_init (GInitableIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (TrackerHttpServerSoup,
+ tracker_http_server_soup,
+ TRACKER_TYPE_HTTP_SERVER,
+ G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
+ tracker_http_server_soup_initable_iface_init))
+
+
+static guint
+get_supported_formats (TrackerHttpRequest *request)
+{
+ SoupMessageHeaders *request_headers;
+ TrackerSerializerFormat i;
+ guint formats = 0;
+
+#if SOUP_CHECK_VERSION (2, 99, 2)
+ request_headers = soup_server_message_get_request_headers (request->message);
+#else
+ request_headers = request->message->request_headers;
+#endif
+
+ for (i = 0; i < TRACKER_N_SERIALIZER_FORMATS; i++) {
+ if (soup_message_headers_header_contains (request_headers, "Accept",
+ mimetypes[i]))
+ formats |= 1 << i;
+ }
+
+ return formats;
+}
+
+static void
+set_message_format (TrackerHttpRequest *request,
+ TrackerSerializerFormat format)
+{
+ SoupMessageHeaders *response_headers;
+
+#if SOUP_CHECK_VERSION (2, 99, 2)
+ response_headers = soup_server_message_get_response_headers (request->message);
+#else
+ response_headers = request->message->response_headers;
+#endif
+ soup_message_headers_set_content_type (response_headers, mimetypes[format], NULL);
+}
+
+#if SOUP_CHECK_VERSION (2, 99, 2)
+static void
+server_callback (SoupServer *server,
+ SoupServerMessage *message,
+ const char *path,
+ GHashTable *query,
+ gpointer user_data)
+#else
+static void
+server_callback (SoupServer *server,
+ SoupMessage *message,
+ const char *path,
+ GHashTable *query,
+ SoupClientContext *client,
+ gpointer user_data)
+#endif
+{
+ TrackerHttpServer *http_server = user_data;
+ GSocketAddress *remote_address;
+ TrackerHttpRequest *request;
+
+#if SOUP_CHECK_VERSION (2, 99, 2)
+ remote_address = soup_server_message_get_remote_address (message);
+#else
+ remote_address = soup_client_context_get_remote_address (client);
+#endif
+
+ request = g_new0 (TrackerHttpRequest, 1);
+ request->server = http_server;
+ request->message = message;
+ soup_server_pause_message (server, message);
+
+ g_signal_emit_by_name (http_server, "request",
+ remote_address,
+ path,
+ query,
+ get_supported_formats (request),
+ request);
+}
+
+static gboolean
+tracker_http_server_initable_init (GInitable *initable,
+ GCancellable *cancellable,
+ GError **error)
+{
+ TrackerHttpServerSoup *server = TRACKER_HTTP_SERVER_SOUP (initable);
+ GTlsCertificate *certificate;
+ guint port;
+
+ g_object_get (initable,
+ "http-certificate", &certificate,
+ "http-port", &port,
+ NULL);
+
+ server->server =
+ soup_server_new ("tls-certificate", certificate,
+ "server-header", USER_AGENT,
+ NULL);
+ soup_server_add_handler (server->server,
+ "/sparql",
+ server_callback,
+ initable,
+ NULL);
+ g_clear_object (&certificate);
+
+ return soup_server_listen_all (server->server,
+ port,
+ 0, error);
+}
+
+static void
+tracker_http_server_soup_initable_iface_init (GInitableIface *iface)
+{
+ iface->init = tracker_http_server_initable_init;
+}
+
+static void
+tracker_http_server_soup_error (TrackerHttpServer *server,
+ TrackerHttpRequest *request,
+ gint code,
+ const gchar *message)
+{
+ TrackerHttpServerSoup *server_soup =
+ TRACKER_HTTP_SERVER_SOUP (server);
+
+ g_assert (request->server == server);
+
+#if SOUP_CHECK_VERSION (2, 99, 2)
+ soup_server_message_set_status (request->message, code, message);
+#else
+ soup_message_set_status_full (request->message, code, message);
+#endif
+ soup_server_unpause_message (server_soup->server, request->message);
+ g_free (request);
+}
+
+static void
+handle_write_in_thread (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ TrackerHttpRequest *request = task_data;
+ gchar buffer[1000];
+ SoupMessageBody *message_body;
+ GError *error = NULL;
+ gssize count;
+
+#if SOUP_CHECK_VERSION (2, 99, 2)
+ message_body = soup_server_message_get_response_body (request->message);
+#else
+ message_body = request->message->response_body;
+#endif
+
+ for (;;) {
+ count = g_input_stream_read (request->istream,
+ buffer, sizeof (buffer),
+ cancellable, &error);
+ if (count < 0) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ break;
+ }
+
+ soup_message_body_append (message_body,
+ SOUP_MEMORY_COPY,
+ buffer, count);
+
+ if ((gsize) count < sizeof (buffer)) {
+ break;
+ }
+ }
+
+ g_input_stream_close (request->istream, cancellable, NULL);
+ soup_message_body_complete (message_body);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+write_finished_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ TrackerHttpRequest *request = user_data;
+ TrackerHttpServerSoup *server =
+ TRACKER_HTTP_SERVER_SOUP (request->server);
+ GError *error = NULL;
+
+ if (!g_task_propagate_boolean (G_TASK (result), &error)) {
+ tracker_http_server_soup_error (request->server,
+ request,
+ 500,
+ error->message);
+ g_clear_error (&error);
+ } else {
+#if SOUP_CHECK_VERSION (2, 99, 2)
+ soup_server_message_set_status (request->message, 200, NULL);
+#else
+ soup_message_set_status (request->message, 200);
+#endif
+ soup_server_unpause_message (server->server, request->message);
+ g_free (request);
+ }
+}
+
+static void
+tracker_http_server_soup_response (TrackerHttpServer *server,
+ TrackerHttpRequest *request,
+ TrackerSerializerFormat format,
+ GInputStream *content)
+{
+ TrackerHttpServerSoup *server_soup =
+ TRACKER_HTTP_SERVER_SOUP (server);
+
+ g_assert (request->server == server);
+
+ set_message_format (request, format);
+
+ request->istream = content;
+ request->task = g_task_new (server, server_soup->cancellable,
+ write_finished_cb, request);
+
+ g_task_set_task_data (request->task, request, NULL);
+ g_task_run_in_thread (request->task, handle_write_in_thread);
+}
+
+static void
+tracker_http_server_soup_finalize (GObject *object)
+{
+ TrackerHttpServerSoup *server =
+ TRACKER_HTTP_SERVER_SOUP (object);
+
+ g_cancellable_cancel (server->cancellable);
+ g_object_unref (server->cancellable);
+
+ g_clear_object (&server->server);
+
+ G_OBJECT_CLASS (tracker_http_server_soup_parent_class)->finalize (object);
+}
+
+static void
+tracker_http_server_soup_class_init (TrackerHttpServerSoupClass *klass)
+{
+ TrackerHttpServerClass *server_class = TRACKER_HTTP_SERVER_CLASS (klass);
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = tracker_http_server_soup_finalize;
+
+ server_class->response = tracker_http_server_soup_response;
+ server_class->error = tracker_http_server_soup_error;
+}
+
+static void
+tracker_http_server_soup_init (TrackerHttpServerSoup *server)
+{
+ server->cancellable = g_cancellable_new ();
+}
+
+/* Client */
+struct _TrackerHttpClientSoup
+{
+ TrackerHttpClient parent_instance;
+ SoupSession *session;
+};
+
+G_DEFINE_TYPE (TrackerHttpClientSoup, tracker_http_client_soup,
+ TRACKER_TYPE_HTTP_CLIENT)
+
+static gboolean
+get_content_type_format (SoupMessage *message,
+ TrackerSerializerFormat *format,
+ GError **error)
+{
+ SoupMessageHeaders *response_headers;
+ gint status_code;
+ const gchar *content_type;
+ TrackerSerializerFormat i;
+
+#if SOUP_CHECK_VERSION (2, 99, 2)
+ status_code = soup_message_get_status (message);
+ response_headers = soup_message_get_response_headers (message);
+#else
+ status_code = message->status_code;
+ response_headers = message->response_headers;
+#endif
+
+ if (status_code != SOUP_STATUS_OK) {
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_FAILED,
+ "Unhandled status code %d",
+ status_code);
+ return FALSE;
+ }
+
+ content_type = soup_message_headers_get_content_type (response_headers, NULL);
+
+ for (i = 0; i < TRACKER_N_SERIALIZER_FORMATS; i++) {
+ if (g_strcmp0 (content_type, mimetypes[i]) == 0) {
+ *format = i;
+ return TRUE;
+ }
+ }
+
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_FAILED,
+ "Unhandled content type '%s'",
+ soup_message_headers_get_content_type (response_headers, NULL));
+ return FALSE;
+}
+
+static void
+send_message_cb (GObject *source,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GTask *task = user_data;
+ GInputStream *stream;
+ GError *error = NULL;
+ SoupMessage *message;
+
+ stream = soup_session_send_finish (SOUP_SESSION (source), res, &error);
+ message = g_task_get_task_data (task);
+
+ if (stream) {
+ TrackerSerializerFormat format;
+
+ if (!get_content_type_format (message, &format, &error)) {
+ g_task_return_error (task, error);
+ } else {
+ g_task_set_task_data (task, GUINT_TO_POINTER (format), NULL);
+ g_task_return_pointer (task, stream, g_object_unref);
+ }
+ } else {
+ g_task_return_error (task, error);
+ }
+
+ g_object_unref (task);
+}
+
+static void
+add_accepted_formats (SoupMessageHeaders *headers,
+ guint formats)
+{
+ TrackerSerializerFormat i;
+
+ for (i = 0; i < TRACKER_N_SERIALIZER_FORMATS; i++) {
+ if ((formats & (1 << i)) == 0)
+ continue;
+
+ soup_message_headers_append (headers, "Accept", mimetypes[i]);
+ }
+}
+
+static SoupMessage *
+create_message (const gchar *uri,
+ const gchar *query,
+ guint formats)
+{
+ SoupMessage *message;
+ SoupMessageHeaders *headers;
+ gchar *full_uri, *query_escaped;
+
+ query_escaped = g_uri_escape_string (query, NULL, FALSE);
+ full_uri = g_strconcat (uri, "?query=", query_escaped, NULL);
+ g_free (query_escaped);
+
+ message = soup_message_new ("GET", full_uri);
+ g_free (full_uri);
+
+#if SOUP_CHECK_VERSION (2, 99, 2)
+ headers = soup_message_get_request_headers (message);
+#else
+ headers = message->request_headers;
+#endif
+
+ soup_message_headers_append (headers, "User-Agent", USER_AGENT);
+ add_accepted_formats (headers, formats);
+
+ return message;
+}
+
+static void
+tracker_http_client_soup_send_message_async (TrackerHttpClient *client,
+ const gchar *uri,
+ const gchar *query,
+ guint formats,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ TrackerHttpClientSoup *client_soup = TRACKER_HTTP_CLIENT_SOUP (client);
+ SoupMessage *message;
+ GTask *task;
+
+ task = g_task_new (client, cancellable, callback, user_data);
+
+ message = create_message (uri, query, formats);
+ g_task_set_task_data (task, message, g_object_unref);
+
+#if SOUP_CHECK_VERSION (2, 99, 2)
+ soup_session_send_async (client_soup->session,
+ message,
+ G_PRIORITY_DEFAULT,
+ cancellable,
+ send_message_cb,
+ task);
+#else
+ soup_session_send_async (client_soup->session,
+ message,
+ cancellable,
+ send_message_cb,
+ task);
+#endif
+}
+
+static GInputStream *
+tracker_http_client_soup_send_message_finish (TrackerHttpClient *client,
+ GAsyncResult *res,
+ TrackerSerializerFormat *format,
+ GError **error)
+{
+ if (format)
+ *format = GPOINTER_TO_UINT (g_task_get_task_data (G_TASK (res)));
+
+ return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static GInputStream *
+tracker_http_client_soup_send_message (TrackerHttpClient *client,
+ const gchar *uri,
+ const gchar *query,
+ guint formats,
+ GCancellable *cancellable,
+ TrackerSerializerFormat *format,
+ GError **error)
+{
+ TrackerHttpClientSoup *client_soup = TRACKER_HTTP_CLIENT_SOUP (client);
+ SoupMessage *message;
+ GInputStream *stream;
+
+ message = create_message (uri, query, formats);
+
+#if SOUP_CHECK_VERSION (2, 99, 2)
+ stream = soup_session_send (client_soup->session,
+ message,
+ cancellable,
+ error);
+#else
+ stream = soup_session_send (client_soup->session,
+ message,
+ cancellable,
+ error);
+#endif
+ if (!stream)
+ return NULL;
+
+ if (!get_content_type_format (message, format, error)) {
+ g_clear_object (&stream);
+ return NULL;
+ }
+
+ return stream;
+}
+
+static void
+tracker_http_client_soup_class_init (TrackerHttpClientSoupClass *klass)
+{
+ TrackerHttpClientClass *client_class =
+ TRACKER_HTTP_CLIENT_CLASS (klass);
+
+ client_class->send_message_async = tracker_http_client_soup_send_message_async;
+ client_class->send_message_finish = tracker_http_client_soup_send_message_finish;
+ client_class->send_message = tracker_http_client_soup_send_message;
+}
+
+static void
+tracker_http_client_soup_init (TrackerHttpClientSoup *client)
+{
+ client->session = soup_session_new ();
+}
+
+void
+initialize_types (GType *client,
+ GType *server)
+{
+ *client = TRACKER_TYPE_HTTP_CLIENT_SOUP;
+ *server = TRACKER_TYPE_HTTP_SERVER_SOUP;
+}
diff --git a/src/libtracker-sparql/remote/tracker-http-module.h
b/src/libtracker-sparql/remote/tracker-http-module.h
new file mode 100644
index 000000000..c013a6f35
--- /dev/null
+++ b/src/libtracker-sparql/remote/tracker-http-module.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022, Red Hat Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * Author: Carlos Garnacho <carlosg gnome org>
+ */
+
+#ifndef TRACKER_HTTP_MODULE_H
+#define TRACKER_HTTP_MODULE_H
+
+#include "tracker-http.h"
+
+#define TRACKER_TYPE_HTTP_SERVER_SOUP (tracker_http_server_soup_get_type ())
+G_DECLARE_FINAL_TYPE (TrackerHttpServerSoup,
+ tracker_http_server_soup,
+ TRACKER, HTTP_SERVER_SOUP,
+ TrackerHttpServer)
+
+#define TRACKER_TYPE_HTTP_CLIENT_SOUP (tracker_http_client_soup_get_type ())
+G_DECLARE_FINAL_TYPE (TrackerHttpClientSoup,
+ tracker_http_client_soup,
+ TRACKER, HTTP_CLIENT_SOUP,
+ TrackerHttpClient)
+
+#endif /* TRACKER_HTTP_MODULE_H */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]