[tracker/wip/carlosg/http-endpoint: 7/12] libtracker-sparql: Add HTTP endpoint implementation




commit 8be9514eb886fd3ed55ed74dc86b5c147cbb6425
Author: Carlos Garnacho <carlosg gnome org>
Date:   Mon Dec 7 13:44:45 2020 +0100

    libtracker-sparql: Add HTTP endpoint implementation
    
    Implement partially the server-side bits of
    https://www.w3.org/TR/2013/REC-sparql11-protocol-20130321/, or at
    least those bits that we handle in our remote connection side.
    Most notably, we only handle select queries ATM, this means no
    modifications, no authentication concerns, etc.
    
    This implements the necessary bits to have TrackerEndpoint and
    tracker_sparql_connection_remote_new() understand each other.

 .../libtracker-sparql-sections.txt                 |  12 +-
 .../libtracker-sparql/libtracker-sparql.types      |   1 +
 src/libtracker-sparql/meson.build                  |   4 +-
 src/libtracker-sparql/tracker-endpoint-http.c      | 380 +++++++++++++++++++++
 src/libtracker-sparql/tracker-endpoint-http.h      |  57 ++++
 src/libtracker-sparql/tracker-endpoint.c           |  15 +-
 src/libtracker-sparql/tracker-private.h            |   6 +
 src/libtracker-sparql/tracker-sparql.h             |   1 +
 8 files changed, 467 insertions(+), 9 deletions(-)
---
diff --git a/docs/reference/libtracker-sparql/libtracker-sparql-sections.txt 
b/docs/reference/libtracker-sparql/libtracker-sparql-sections.txt
index 93d73f2b4..1b4f7d845 100644
--- a/docs/reference/libtracker-sparql/libtracker-sparql-sections.txt
+++ b/docs/reference/libtracker-sparql/libtracker-sparql-sections.txt
@@ -252,9 +252,11 @@ TrackerNotifierClass
 <FILE>tracker-endpoint</FILE>
 <TITLE>TrackerEndpoint</TITLE>
 TrackerEndpoint
+tracker_endpoint_get_sparql_connection
 TrackerEndpointDBus
 tracker_endpoint_dbus_new
-tracker_endpoint_get_sparql_connection
+TrackerEndpointHttp
+tracker_endpoint_http_new
 <SUBSECTION Standard>
 TRACKER_TYPE_ENDPOINT
 TRACKER_TYPE_ENDPOINT_DBUS
@@ -263,9 +265,17 @@ TRACKER_ENDPOINT_DBUS_CLASS
 TRACKER_ENDPOINT_DBUS_GET_CLASS
 TRACKER_IS_ENDPOINT_DBUS
 TRACKER_IS_ENDPOINT_DBUS_CLASS
+TRACKER_TYPE_ENDPOINT_HTTP
+TRACKER_ENDPOINT_HTTP
+TRACKER_ENDPOINT_HTTP_CLASS
+TRACKER_ENDPOINT_HTTP_GET_CLASS
+TRACKER_IS_ENDPOINT_HTTP
+TRACKER_IS_ENDPOINT_HTTP_CLASS
 TrackerEndpointClass
 TrackerEndpointDBusClass
+TrackerEndpointHttpClass
 tracker_endpoint_dbus_get_type
+tracker_endpoint_http_get_type
 </SECTION>
 
 <SECTION>
diff --git a/docs/reference/libtracker-sparql/libtracker-sparql.types 
b/docs/reference/libtracker-sparql/libtracker-sparql.types
index 4a94c21cc..6b77ba0e7 100644
--- a/docs/reference/libtracker-sparql/libtracker-sparql.types
+++ b/docs/reference/libtracker-sparql/libtracker-sparql.types
@@ -2,6 +2,7 @@
 
 tracker_endpoint_get_type
 tracker_endpoint_dbus_get_type
+tracker_endpoint_http_get_type
 tracker_namespace_manager_get_type
 tracker_notifier_get_type
 tracker_notifier_event_get_type
diff --git a/src/libtracker-sparql/meson.build b/src/libtracker-sparql/meson.build
index 0e6064e67..570f4b83b 100644
--- a/src/libtracker-sparql/meson.build
+++ b/src/libtracker-sparql/meson.build
@@ -20,6 +20,7 @@ libtracker_sparql_c_sources = files(
     'tracker-cursor.c',
     'tracker-endpoint.c',
     'tracker-endpoint-dbus.c',
+    'tracker-endpoint-http.c',
     'tracker-error.c',
     'tracker-namespace-manager.c',
     'tracker-notifier.c',
@@ -39,6 +40,7 @@ libtracker_sparql_c_public_headers = files(
     'tracker-cursor.h',
     'tracker-endpoint.h',
     'tracker-endpoint-dbus.h',
+    'tracker-endpoint-http.h',
     'tracker-error.h',
     'tracker-namespace-manager.h',
     'tracker-notifier.h',
@@ -52,7 +54,7 @@ libtracker_sparql_c_public_headers = files(
 libtracker_sparql_intermediate = static_library('tracker-sparql-intermediate',
     enum_types,
     libtracker_sparql_c_sources,
-    dependencies: [tracker_common_dep, json_glib, libxml2],
+    dependencies: [tracker_common_dep, json_glib, libxml2, libsoup],
     gnu_symbol_visibility: 'hidden',
 )
 
diff --git a/src/libtracker-sparql/tracker-endpoint-http.c b/src/libtracker-sparql/tracker-endpoint-http.c
new file mode 100644
index 000000000..c2b643c3a
--- /dev/null
+++ b/src/libtracker-sparql/tracker-endpoint-http.c
@@ -0,0 +1,380 @@
+/*
+ * Copyright (C) 2020, 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 "config.h"
+
+#include "tracker-endpoint-http.h"
+#include "tracker-serializer.h"
+#include "tracker-private.h"
+
+#include <libsoup/soup.h>
+
+#define SERVER_HEADER "Tracker " PACKAGE_VERSION " (https://gitlab.gnome.org/GNOME/tracker/issues/)"
+
+typedef struct _TrackerEndpointHttp TrackerEndpointHttp;
+
+struct _TrackerEndpointHttp {
+       TrackerEndpoint parent_instance;
+       SoupServer *server;
+       GTlsCertificate *certificate;
+       guint port;
+       GCancellable *cancellable;
+};
+
+typedef struct {
+       TrackerEndpoint *endpoint;
+       SoupMessage *message;
+       GInputStream *istream;
+       GTask *task;
+       TrackerSerializerFormat format;
+} Request;
+
+enum {
+       PROP_0,
+       PROP_HTTP_PORT,
+       PROP_HTTP_CERTIFICATE,
+       N_PROPS
+};
+
+#define XML_TYPE "application/sparql-results+xml"
+#define JSON_TYPE "application/sparql-results+json"
+
+static GParamSpec *props[N_PROPS];
+
+static void tracker_endpoint_http_initable_iface_init (GInitableIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (TrackerEndpointHttp, tracker_endpoint_http, TRACKER_TYPE_ENDPOINT,
+                         G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, tracker_endpoint_http_initable_iface_init))
+
+static void
+request_free (Request *request)
+{
+       g_clear_object (&request->istream);
+       g_free (request);
+}
+
+static void
+handle_request_in_thread (GTask        *task,
+                          gpointer      source_object,
+                          gpointer      task_data,
+                          GCancellable *cancellable)
+{
+       Request *request = task_data;
+       gchar *buffer[1000];
+       gboolean finished = FALSE;
+       SoupMessageBody *message_body;
+       GError *error = NULL;
+       gssize count;
+
+       g_object_get (request->message,
+                     "response-body", &message_body,
+                     NULL);
+
+       while (!finished) {
+               count = g_input_stream_read (request->istream,
+                                            buffer, sizeof (buffer),
+                                            cancellable, &error);
+               if (count == -1) {
+                       g_task_return_error (task, error);
+                       break;
+               } else if (count < sizeof (buffer)) {
+                       finished = TRUE;
+               }
+
+               soup_message_body_append (message_body,
+                                         SOUP_MEMORY_COPY,
+                                         buffer, count);
+       }
+
+       g_input_stream_close (request->istream, cancellable, NULL);
+       soup_message_body_complete (message_body);
+       g_task_return_boolean (task, TRUE);
+}
+
+static void
+request_finished_cb (GObject      *object,
+                     GAsyncResult *result,
+                     gpointer      user_data)
+{
+       Request *request = user_data;
+       TrackerEndpointHttp *endpoint_http;
+       GError *error = NULL;
+
+       endpoint_http = TRACKER_ENDPOINT_HTTP (request->endpoint);
+
+       if (!g_task_propagate_boolean (G_TASK (result), &error)) {
+               soup_message_set_status_full (request->message, 500,
+                                             error ? error->message :
+                                             "No error message");
+               g_clear_error (&error);
+       } else {
+               soup_message_set_status (request->message, 200);
+       }
+
+       soup_server_unpause_message (endpoint_http->server, request->message);
+       request_free (request);
+}
+
+static void
+query_async_cb (GObject      *object,
+                GAsyncResult *result,
+                gpointer      user_data)
+{
+       TrackerEndpointHttp *endpoint_http;
+       TrackerSparqlCursor *cursor;
+       Request *request = user_data;
+       GError *error = NULL;
+
+       endpoint_http = TRACKER_ENDPOINT_HTTP (request->endpoint);
+       cursor = tracker_sparql_connection_query_finish (TRACKER_SPARQL_CONNECTION (object),
+                                                        result, &error);
+       if (error) {
+               soup_message_set_status_full (request->message, 500, error->message);
+               soup_server_unpause_message (endpoint_http->server, request->message);
+               request_free (request);
+               return;
+       }
+
+       request->istream = tracker_serializer_new (cursor, request->format);
+       request->task = g_task_new (endpoint_http, endpoint_http->cancellable,
+                                   request_finished_cb, request);
+       g_task_set_task_data (request->task, request, NULL);
+
+       g_task_run_in_thread (request->task, handle_request_in_thread);
+}
+
+static gboolean
+pick_format (SoupMessage             *message,
+             TrackerSerializerFormat *format)
+{
+       SoupMessageHeaders *request_headers, *response_headers;
+
+       g_object_get (message,
+                     "request-headers", &request_headers,
+                     "response-headers", &response_headers,
+                     NULL);
+
+       if (soup_message_headers_header_contains (request_headers, "Accept", JSON_TYPE)) {
+               soup_message_headers_set_content_type (response_headers, JSON_TYPE, NULL);
+               *format = TRACKER_SERIALIZER_FORMAT_JSON;
+               return TRUE;
+       } else if (soup_message_headers_header_contains (request_headers, "Accept", XML_TYPE)) {
+               soup_message_headers_set_content_type (response_headers, XML_TYPE, NULL);
+               *format = TRACKER_SERIALIZER_FORMAT_XML;
+               return TRUE;
+       } else {
+               return FALSE;
+       }
+
+       return FALSE;
+}
+
+static void
+server_callback (SoupServer        *server,
+                 SoupMessage       *message,
+                 const char        *path,
+                 GHashTable        *query,
+                 SoupClientContext *client,
+                 gpointer           user_data)
+{
+       TrackerEndpoint *endpoint = user_data;
+       TrackerSparqlConnection *conn;
+       TrackerSerializerFormat format;
+       const gchar *sparql;
+       Request *request;
+
+       sparql = g_hash_table_lookup (query, "query");
+       if (!sparql) {
+               soup_message_set_status_full (message, 500, "No query given");
+               return;
+       }
+
+       if (!pick_format (message, &format)) {
+               soup_message_set_status_full (message, 500, "No recognized accepted formats");
+               return;
+       }
+
+       request = g_new0 (Request, 1);
+       request->endpoint = endpoint;
+       request->message = message;
+       request->format = format;
+
+       conn = tracker_endpoint_get_sparql_connection (endpoint);
+       tracker_sparql_connection_query_async (conn,
+                                              sparql,
+                                              NULL,
+                                              query_async_cb,
+                                              request);
+
+       soup_server_pause_message (server, message);
+}
+
+static gboolean
+tracker_endpoint_http_initable_init (GInitable     *initable,
+                                     GCancellable  *cancellable,
+                                     GError       **error)
+{
+       TrackerEndpoint *endpoint = TRACKER_ENDPOINT (initable);
+       TrackerEndpointHttp *endpoint_http = TRACKER_ENDPOINT_HTTP (endpoint);
+
+       endpoint_http->server =
+               soup_server_new ("tls-certificate", endpoint_http->certificate,
+                                "server-header", SERVER_HEADER,
+                                NULL);
+       soup_server_add_handler (endpoint_http->server,
+                                "/sparql",
+                                server_callback,
+                                initable,
+                                NULL);
+
+       return soup_server_listen_all (endpoint_http->server,
+                                      endpoint_http->port,
+                                      0, error);
+}
+
+static void
+tracker_endpoint_http_initable_iface_init (GInitableIface *iface)
+{
+       iface->init = tracker_endpoint_http_initable_init;
+}
+
+static void
+tracker_endpoint_http_finalize (GObject *object)
+{
+       TrackerEndpointHttp *endpoint_http = TRACKER_ENDPOINT_HTTP (object);
+
+       g_cancellable_cancel (endpoint_http->cancellable);
+
+       g_clear_object (&endpoint_http->cancellable);
+
+       g_clear_object (&endpoint_http->server);
+
+       G_OBJECT_CLASS (tracker_endpoint_http_parent_class)->finalize (object);
+}
+
+static void
+tracker_endpoint_http_set_property (GObject      *object,
+                                    guint         prop_id,
+                                    const GValue *value,
+                                    GParamSpec   *pspec)
+{
+       TrackerEndpointHttp *endpoint_http = TRACKER_ENDPOINT_HTTP (object);
+
+       switch (prop_id) {
+       case PROP_HTTP_PORT:
+               endpoint_http->port = g_value_get_uint (value);
+               break;
+       case PROP_HTTP_CERTIFICATE:
+               endpoint_http->certificate = g_value_dup_object (value);
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+               break;
+       }
+}
+
+static void
+tracker_endpoint_http_get_property (GObject    *object,
+                                    guint       prop_id,
+                                    GValue     *value,
+                                    GParamSpec *pspec)
+{
+       TrackerEndpointHttp *endpoint_http = TRACKER_ENDPOINT_HTTP (object);
+
+       switch (prop_id) {
+       case PROP_HTTP_PORT:
+               g_value_set_uint (value, endpoint_http->port);
+               break;
+       case PROP_HTTP_CERTIFICATE:
+               g_value_set_object (value, endpoint_http->certificate);
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+               break;
+       }
+}
+
+static void
+tracker_endpoint_http_class_init (TrackerEndpointHttpClass *klass)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+       object_class->finalize = tracker_endpoint_http_finalize;
+       object_class->set_property = tracker_endpoint_http_set_property;
+       object_class->get_property = tracker_endpoint_http_get_property;
+
+       props[PROP_HTTP_PORT] =
+               g_param_spec_uint ("http-port",
+                                  "HTTP Port",
+                                  "HTTP Port",
+                                  0, G_MAXUINT,
+                                  8080,
+                                  G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+       props[PROP_HTTP_CERTIFICATE] =
+               g_param_spec_object ("http-certificate",
+                                    "HTTP certificate",
+                                    "HTTP certificate",
+                                    G_TYPE_TLS_CERTIFICATE,
+                                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+
+       g_object_class_install_properties (object_class, N_PROPS, props);
+}
+
+static void
+tracker_endpoint_http_init (TrackerEndpointHttp *endpoint)
+{
+       endpoint->cancellable = g_cancellable_new ();
+}
+
+/**
+ * tracker_endpoint_http_new:
+ * @sparql_connection: a #TrackerSparqlConnection
+ * @port: HTTP port to listen to
+ * @certificate: (nullable): certificate to use for encription, or %NULL
+ * @cancellable: (nullable): a #GCancellable, or %NULL
+ * @error: pointer to a #GError
+ *
+ * Sets up a Tracker endpoint to listen via HTTP, in the given @port.
+ * If @certificate is not %NULL, HTTPS may be used to connect to the
+ * endpoint.
+ *
+ * Returns: (transfer full): a #TrackerEndpointDBus object.
+ *
+ * Since: 3.1
+ **/
+TrackerEndpointHttp *
+tracker_endpoint_http_new (TrackerSparqlConnection  *sparql_connection,
+                           guint                     port,
+                           GTlsCertificate          *certificate,
+                           GCancellable             *cancellable,
+                           GError                  **error)
+{
+       g_return_val_if_fail (TRACKER_IS_SPARQL_CONNECTION (sparql_connection), NULL);
+       g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), NULL);
+       g_return_val_if_fail (!certificate || G_IS_TLS_CERTIFICATE (certificate), NULL);
+       g_return_val_if_fail (!error || !*error, NULL);
+
+       return g_initable_new (TRACKER_TYPE_ENDPOINT_HTTP, cancellable, error,
+                              "http-port", port,
+                              "sparql-connection", sparql_connection,
+                              "http-certificate", certificate,
+                              NULL);
+}
diff --git a/src/libtracker-sparql/tracker-endpoint-http.h b/src/libtracker-sparql/tracker-endpoint-http.h
new file mode 100644
index 000000000..40ef38591
--- /dev/null
+++ b/src/libtracker-sparql/tracker-endpoint-http.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2020, 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_ENDPOINT_HTTP_H
+#define TRACKER_ENDPOINT_HTTP_H
+
+#if !defined (__LIBTRACKER_SPARQL_INSIDE__) && !defined (TRACKER_COMPILATION)
+#error "only <libtracker-sparql/tracker-sparql.h> must be included directly."
+#endif
+
+#include <libtracker-sparql/tracker-endpoint.h>
+#include <libtracker-sparql/tracker-version.h>
+
+#define TRACKER_TYPE_ENDPOINT_HTTP         (tracker_endpoint_http_get_type())
+#define TRACKER_ENDPOINT_HTTP(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), TRACKER_TYPE_ENDPOINT_HTTP, 
TrackerEndpointHttp))
+#define TRACKER_ENDPOINT_HTTP_CLASS(c)     (G_TYPE_CHECK_CLASS_CAST ((c), TRACKER_TYPE_ENDPOINT_HTTP, 
TrackerEndpointHttpClass))
+#define TRACKER_IS_ENDPOINT_HTTP(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), TRACKER_TYPE_ENDPOINT_HTTP))
+#define TRACKER_IS_ENDPOINT_HTTP_CLASS(c)  (G_TYPE_CHECK_CLASS_TYPE ((c),  TRACKER_TYPE_ENDPOINT_HTTP))
+#define TRACKER_ENDPOINT_HTTP_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), TRACKER_TYPE_ENDPOINT_HTTP, 
TrackerEndpointHttpClass))
+
+/**
+ * TrackerEndpointHttp:
+ *
+ * The <structname>TrackerEndpointHttp</structname> object represents a public
+ * connection to a #TrackerSparqlConnection on a HTTP port.
+ */
+typedef struct _TrackerEndpointHttp TrackerEndpointHttp;
+
+TRACKER_AVAILABLE_IN_3_1
+GType tracker_endpoint_http_get_type (void) G_GNUC_CONST;
+
+TRACKER_AVAILABLE_IN_3_1
+TrackerEndpointHttp * tracker_endpoint_http_new (TrackerSparqlConnection  *sparql_connection,
+                                                 guint                     port,
+                                                 GTlsCertificate          *certificate,
+                                                 GCancellable             *cancellable,
+                                                 GError                  **error);
+
+#endif /* TRACKER_ENDPOINT_HTTP_H */
diff --git a/src/libtracker-sparql/tracker-endpoint.c b/src/libtracker-sparql/tracker-endpoint.c
index ddf537af5..6392dbb37 100644
--- a/src/libtracker-sparql/tracker-endpoint.c
+++ b/src/libtracker-sparql/tracker-endpoint.c
@@ -40,20 +40,21 @@ G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (TrackerEndpoint, tracker_endpoint, G_TYPE_O
 
 /**
  * SECTION: tracker-endpoint
- * @short_description: Expose a database to other processes
+ * @short_description: Expose a database outside the process
  * @title: TrackerEndpoint
  * @stability: Stable
  * @include: tracker-endpoint.h
  *
  * <para>
- * #TrackerEndpoint allows sharing data with other processes on the system,
- * using a Tracker-specific D-Bus API.
+ * #TrackerEndpoint allows sharing data, either with other processes on the
+ * system via a Tracker-specific D-Bus API, or remote peers via the HTTP
+ * SPARQL protocol.
  * </para>
  * <para>
- * When it is shared in this way, processes can connect to your database using
- * tracker_sparql_connection_bus_new() and can also fetch data directly from
- * SPARQL queries using the <userinput>SELECT { SERVICE ... }</userinput>
- * syntax.
+ * When it is shared in this way, other peers can connect to your database using
+ * tracker_sparql_connection_bus_new() or tracker_sparql_connection_remote_new(),
+ * and can also fetch data directly from SPARQL queries using the
+ * <userinput>SELECT { SERVICE ... }</userinput> syntax.
  * </para>
  */
 
diff --git a/src/libtracker-sparql/tracker-private.h b/src/libtracker-sparql/tracker-private.h
index 40d9828ca..da93ac862 100644
--- a/src/libtracker-sparql/tracker-private.h
+++ b/src/libtracker-sparql/tracker-private.h
@@ -180,6 +180,12 @@ struct _TrackerEndpointDBusClass {
        gchar * (* add_prologue) (TrackerEndpointDBus *endpoint_dbus);
 };
 
+typedef struct _TrackerEndpointHttpClass TrackerEndpointHttpClass;
+
+struct _TrackerEndpointHttpClass {
+       struct _TrackerEndpointClass parent_class;
+};
+
 typedef struct _TrackerResourceClass TrackerResourceClass;
 
 struct _TrackerResourceClass
diff --git a/src/libtracker-sparql/tracker-sparql.h b/src/libtracker-sparql/tracker-sparql.h
index cb6309a0d..3b9aa2ffd 100644
--- a/src/libtracker-sparql/tracker-sparql.h
+++ b/src/libtracker-sparql/tracker-sparql.h
@@ -30,6 +30,7 @@
 #include <libtracker-sparql/tracker-cursor.h>
 #include <libtracker-sparql/tracker-endpoint.h>
 #include <libtracker-sparql/tracker-endpoint-dbus.h>
+#include <libtracker-sparql/tracker-endpoint-http.h>
 #include <libtracker-sparql/tracker-version.h>
 #include <libtracker-sparql/tracker-ontologies.h>
 #include <libtracker-sparql/tracker-resource.h>


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]