summaryrefslogtreecommitdiffstats
path: root/src/http
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2011-10-21 23:44:02 +0000
committerDavid Robillard <d@drobilla.net>2011-10-21 23:44:02 +0000
commitc7b05953949dbc80eee22348270166c2a6704a95 (patch)
treed0ac1b757831bd5b4944d008101e68054e9f1c37 /src/http
parent5a413b8cd31006c8836bcc7409f7055c890f8a68 (diff)
downloadingen-c7b05953949dbc80eee22348270166c2a6704a95.tar.gz
ingen-c7b05953949dbc80eee22348270166c2a6704a95.tar.bz2
ingen-c7b05953949dbc80eee22348270166c2a6704a95.zip
Move engine side OSC and HTTP stuff to separate modules.
git-svn-id: http://svn.drobilla.net/lad/trunk/ingen@3576 a436a847-0d15-0410-975c-d299462d15a1
Diffstat (limited to 'src/http')
-rw-r--r--src/http/HTTPClientSender.cpp160
-rw-r--r--src/http/HTTPClientSender.hpp109
-rw-r--r--src/http/HTTPEngineReceiver.cpp228
-rw-r--r--src/http/HTTPEngineReceiver.hpp76
-rw-r--r--src/http/ingen_http.cpp54
5 files changed, 627 insertions, 0 deletions
diff --git a/src/http/HTTPClientSender.cpp b/src/http/HTTPClientSender.cpp
new file mode 100644
index 00000000..564954ae
--- /dev/null
+++ b/src/http/HTTPClientSender.cpp
@@ -0,0 +1,160 @@
+/* This file is part of Ingen.
+ * Copyright 2008-2011 David Robillard <http://drobilla.net>
+ *
+ * Ingen is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Ingen 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 General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <string>
+
+#include <libsoup/soup.h>
+
+#include "ingen/serialisation/Serialiser.hpp"
+#include "ingen/shared/World.hpp"
+#include "raul/Atom.hpp"
+#include "raul/AtomRDF.hpp"
+#include "raul/log.hpp"
+
+#include "../server/Engine.hpp"
+
+#include "HTTPClientSender.hpp"
+
+using namespace std;
+using namespace Raul;
+
+namespace Ingen {
+namespace Server {
+
+void
+HTTPClientSender::response_ok(int32_t id)
+{
+}
+
+void
+HTTPClientSender::response_error(int32_t id, const std::string& msg)
+{
+ warn << "HTTP Error " << id << " (" << msg << ")" << endl;
+}
+
+void
+HTTPClientSender::error(const std::string& msg)
+{
+ warn << "HTTP send error " << msg << endl;
+}
+
+void
+HTTPClientSender::put(const URI& uri,
+ const Resource::Properties& properties,
+ Resource::Graph ctx)
+{
+ const std::string request_uri = (Raul::Path::is_path(uri))
+ ? _url + "/patch" + uri.substr(uri.find("/"))
+ : uri.str();
+
+
+ Sord::Model model(*_engine.world()->rdf_world());
+ for (Resource::Properties::const_iterator i = properties.begin();
+ i != properties.end(); ++i)
+ model.add_statement(
+ Sord::URI(*_engine.world()->rdf_world(), request_uri),
+ AtomRDF::atom_to_node(model, i->first.str()),
+ AtomRDF::atom_to_node(model, i->second));
+
+ const string str = model.write_to_string("", SERD_TURTLE);
+ send_chunk(str);
+}
+
+void
+HTTPClientSender::delta(const URI& uri,
+ const Resource::Properties& remove,
+ const Resource::Properties& add)
+{
+}
+
+void
+HTTPClientSender::del(const URI& uri)
+{
+ send_chunk(string("<").append(uri.str()).append("> a <http://www.w3.org/2002/07/owl#Nothing> ."));
+}
+
+void
+HTTPClientSender::connect(const Path& src_path, const Path& dst_path)
+{
+ const string msg = string(
+ "@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n"
+ "@prefix ingen: <http://drobilla.net/ns/ingen#> .\n").append(
+ "<> ingen:connection [\n"
+ "\tingen:destination <").append(dst_path.str()).append("> ;\n"
+ "\tingen:source <").append(src_path.str()).append(">\n] .\n");
+ send_chunk(msg);
+}
+
+void
+HTTPClientSender::disconnect(const URI& src,
+ const URI& dst)
+{
+}
+
+void
+HTTPClientSender::disconnect_all(const Raul::Path& parent_patch_path,
+ const Raul::Path& path)
+{
+}
+
+void
+HTTPClientSender::set_property(const URI& subject, const URI& key, const Atom& value)
+{
+#if 0
+ Sord::Node node = AtomRDF::atom_to_node(*_engine.world()->rdf_world(), value);
+ const string msg = string(
+ "@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n"
+ "@prefix ingen: <http://drobilla.net/ns/ingen#> .\n"
+ "@prefix ingenui: <http://drobilla.net/ns/ingenuity#> .\n").append(
+ subject.str()).append("> ingen:property [\n"
+ "rdf:predicate ").append(key.str()).append(" ;\n"
+ "rdf:value ").append(node.to_string()).append("\n] .\n");
+ send_chunk(msg);
+#endif
+}
+
+void
+HTTPClientSender::activity(const Path& path, const Raul::Atom& value)
+{
+ if (value.type() == Atom::BOOL) {
+ const string msg = string(
+ "@prefix ingen: <http://drobilla.net/ns/ingen#> .\n\n<").append(
+ path.str()).append("> ingen:activity true .\n");
+ send_chunk(msg);
+ } else if (value.type() == Atom::FLOAT) {
+ const string msg = string(
+ "@prefix ingen: <http://drobilla.net/ns/ingen#> .\n\n<").append(
+ path.str()).append("> ingen:activity ").append(
+ value.get_bool() ? "true" : "false").append(" .\n");
+ send_chunk(msg);
+ } else {
+ warn << "Unknown activity type at " << path << endl;
+ }
+}
+
+void
+HTTPClientSender::move(const Path& old_path, const Path& new_path)
+{
+ string msg = string(
+ "@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n"
+ "@prefix ingen: <http://drobilla.net/ns/ingen#> .\n\n<").append(
+ old_path.str()).append("> rdf:subject <").append(new_path.str()).append("> .\n");
+ send_chunk(msg);
+}
+
+} // namespace Server
+} // namespace Ingen
diff --git a/src/http/HTTPClientSender.hpp b/src/http/HTTPClientSender.hpp
new file mode 100644
index 00000000..116f0601
--- /dev/null
+++ b/src/http/HTTPClientSender.hpp
@@ -0,0 +1,109 @@
+/* This file is part of Ingen.
+ * Copyright 2008-2011 David Robillard <http://drobilla.net>
+ *
+ * Ingen is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Ingen 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 General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef INGEN_ENGINE_HTTPCLIENTSENDER_HPP
+#define INGEN_ENGINE_HTTPCLIENTSENDER_HPP
+
+#include <cassert>
+#include <string>
+
+#include "raul/Thread.hpp"
+
+#include "ingen/ClientInterface.hpp"
+#include "../shared/HTTPSender.hpp"
+
+namespace Ingen {
+
+class ServerInterface;
+
+namespace Server {
+
+class Engine;
+
+/** Implements ClientInterface for HTTP clients.
+ * Sends changes as RDF deltas over an HTTP stream
+ * (a single message with chunked encoding response).
+ *
+ * \ingroup engine
+ */
+class HTTPClientSender
+ : public ClientInterface
+ , public Ingen::Shared::HTTPSender
+{
+public:
+ explicit HTTPClientSender(Engine& engine)
+ : _engine(engine)
+ , _enabled(true)
+ {}
+
+ bool enabled() const { return _enabled; }
+
+ void enable() { _enabled = true; }
+ void disable() { _enabled = false; }
+
+ void bundle_begin() { HTTPSender::bundle_begin(); }
+ void bundle_end() { HTTPSender::bundle_end(); }
+
+ Raul::URI uri() const { return "http://example.org/"; }
+
+ /* *** ClientInterface Implementation Below *** */
+
+ void response_ok(int32_t id);
+ void response_error(int32_t id, const std::string& msg);
+
+ void error(const std::string& msg);
+
+ virtual void put(const Raul::URI& path,
+ const Resource::Properties& properties,
+ Resource::Graph ctx);
+
+ virtual void delta(const Raul::URI& path,
+ const Resource::Properties& remove,
+ const Resource::Properties& add);
+
+ virtual void del(const Raul::URI& uri);
+
+ virtual void move(const Raul::Path& old_path,
+ const Raul::Path& new_path);
+
+ virtual void connect(const Raul::Path& src_port_path,
+ const Raul::Path& dst_port_path);
+
+ virtual void disconnect(const Raul::URI& src,
+ const Raul::URI& dst);
+
+ virtual void disconnect_all(const Raul::Path& parent_patch_path,
+ const Raul::Path& path);
+
+ virtual void set_property(const Raul::URI& subject_path,
+ const Raul::URI& predicate,
+ const Raul::Atom& value);
+
+ virtual void activity(const Raul::Path& path,
+ const Raul::Atom& value);
+
+private:
+ Engine& _engine;
+ std::string _url;
+ bool _enabled;
+};
+
+} // namespace Server
+} // namespace Ingen
+
+#endif // INGEN_ENGINE_HTTPCLIENTSENDER_HPP
+
diff --git a/src/http/HTTPEngineReceiver.cpp b/src/http/HTTPEngineReceiver.cpp
new file mode 100644
index 00000000..2f39ee9c
--- /dev/null
+++ b/src/http/HTTPEngineReceiver.cpp
@@ -0,0 +1,228 @@
+/* This file is part of Ingen.
+ * Copyright 2007-2011 David Robillard <http://drobilla.net>
+ *
+ * Ingen is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Ingen 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 General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <cstdio>
+#include <cstdlib>
+#include <string>
+
+#include <boost/format.hpp>
+
+#include <libsoup/soup.h>
+
+#include "raul/SharedPtr.hpp"
+#include "raul/log.hpp"
+
+#include "ingen/ClientInterface.hpp"
+#include "ingen/ServerInterface.hpp"
+#include "ingen/serialisation/Parser.hpp"
+#include "ingen/serialisation/Serialiser.hpp"
+#include "ingen/shared/Module.hpp"
+#include "ingen/shared/Store.hpp"
+
+#include "../server/ClientBroadcaster.hpp"
+#include "../server/Engine.hpp"
+
+#include "HTTPClientSender.hpp"
+#include "HTTPEngineReceiver.hpp"
+
+#define LOG(s) s << "[HTTPEngineReceiver] "
+
+using namespace std;
+using namespace Raul;
+
+namespace Ingen {
+
+using namespace Serialisation;
+
+namespace Server {
+
+HTTPEngineReceiver::HTTPEngineReceiver(Engine& engine,
+ SharedPtr<ServerInterface> interface,
+ uint16_t port)
+ : _engine(engine)
+ , _interface(interface)
+ , _server(soup_server_new(SOUP_SERVER_PORT, port, NULL))
+{
+ _receive_thread = new ReceiveThread(*this);
+
+ soup_server_add_handler(_server, NULL, message_callback, this, NULL);
+
+ LOG(info) << "Started HTTP server on port " << soup_server_get_port(_server) << endl;
+
+ if (!engine.world()->parser() || !engine.world()->serialiser())
+ engine.world()->load_module("serialisation");
+
+ _receive_thread->set_name("HTTPEngineReceiver Listener");
+ _receive_thread->start();
+}
+
+HTTPEngineReceiver::~HTTPEngineReceiver()
+{
+ _receive_thread->stop();
+ delete _receive_thread;
+
+ if (_server) {
+ soup_server_quit(_server);
+ _server = NULL;
+ }
+}
+
+void
+HTTPEngineReceiver::message_callback(SoupServer* server,
+ SoupMessage* msg,
+ const char* path_str,
+ GHashTable* query,
+ SoupClientContext* client,
+ void* data)
+{
+ HTTPEngineReceiver* me = (HTTPEngineReceiver*)data;
+ ServerInterface* interface = me->_interface.get();
+
+ using namespace Ingen::Shared;
+
+ SharedPtr<Store> store = me->_engine.world()->store();
+ if (!store) {
+ soup_message_set_status(msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ string path = path_str;
+ if (path[path.length() - 1] == '/') {
+ path = path.substr(0, path.length()-1);
+ }
+
+ SharedPtr<Serialiser> serialiser = me->_engine.world()->serialiser();
+
+ const string base_uri = "path:/";
+ const char* mime_type = "text/plain";
+
+ // Special GET paths
+ if (msg->method == SOUP_METHOD_GET) {
+ if (path == Path::root().str() || path.empty()) {
+ const string r = string("@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n")
+ .append("\n<> rdfs:seeAlso <plugins> ;")
+ .append("\n rdfs:seeAlso <stream> ;")
+ .append("\n rdfs:seeAlso <patch> .");
+ soup_message_set_status(msg, SOUP_STATUS_OK);
+ soup_message_set_response(msg, mime_type, SOUP_MEMORY_COPY, r.c_str(), r.length());
+ return;
+
+ } else if (msg->method == SOUP_METHOD_GET && path.substr(0, 8) == "/plugins") {
+ // FIXME: kludge
+ #if 0
+ interface->get("ingen:plugins");
+ me->_receive_thread->whip();
+
+ serialiser->start_to_string("/", base_uri);
+ for (NodeFactory::Plugins::const_iterator p = me->_engine.node_factory()->plugins().begin();
+ p != me->_engine.node_factory()->plugins().end(); ++p)
+ serialiser->serialise_plugin(*(Shared::Plugin*)p->second);
+ const string r = serialiser->finish();
+ soup_message_set_status(msg, SOUP_STATUS_OK);
+ soup_message_set_response(msg, mime_type, SOUP_MEMORY_COPY, r.c_str(), r.length());
+ #endif
+ return;
+
+ } else if (path.substr(0, 6) == "/patch") {
+ path = '/' + path.substr(6);
+ if (path.substr(0, 2) == "//")
+ path = path.substr(1);
+
+ } else if (path.substr(0, 7) == "/stream") {
+ HTTPClientSender* client = new HTTPClientSender(me->_engine);
+ interface->register_client(client);
+
+ // Respond with port number of stream for client
+ const int port = client->listen_port();
+ char buf[32];
+ snprintf(buf, sizeof(buf), "%d", port);
+ soup_message_set_status(msg, SOUP_STATUS_OK);
+ soup_message_set_response(msg, mime_type, SOUP_MEMORY_COPY, buf, strlen(buf));
+ return;
+ }
+ }
+
+ if (!Path::is_valid(path)) {
+ LOG(error) << "Bad HTTP path: " << path << endl;
+ soup_message_set_status(msg, SOUP_STATUS_BAD_REQUEST);
+ const string& err = (boost::format("Bad path: %1%") % path).str();
+ soup_message_set_response(msg, "text/plain", SOUP_MEMORY_COPY,
+ err.c_str(), err.length());
+ return;
+ }
+
+ if (msg->method == SOUP_METHOD_GET) {
+ Glib::RWLock::ReaderLock lock(store->lock());
+
+ // Find object
+ Store::const_iterator start = store->find(path);
+ if (start == store->end()) {
+ soup_message_set_status(msg, SOUP_STATUS_NOT_FOUND);
+ const string& err = (boost::format("No such object: %1%") % path).str();
+ soup_message_set_response(msg, "text/plain", SOUP_MEMORY_COPY,
+ err.c_str(), err.length());
+ return;
+ }
+
+ // Get serialiser
+ SharedPtr<Serialiser> serialiser = me->_engine.world()->serialiser();
+ if (!serialiser) {
+ soup_message_set_status(msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
+ soup_message_set_response(msg, "text/plain", SOUP_MEMORY_STATIC,
+ "No serialiser available\n", 24);
+ return;
+ }
+
+ // Serialise object
+ const string response = serialiser->to_string(start->second,
+ "http://localhost:16180/patch", GraphObject::Properties());
+
+ soup_message_set_status(msg, SOUP_STATUS_OK);
+ soup_message_set_response(msg, mime_type, SOUP_MEMORY_COPY,
+ response.c_str(), response.length());
+
+ } else if (msg->method == SOUP_METHOD_PUT) {
+ Glib::RWLock::WriterLock lock(store->lock());
+
+ // Get parser
+ SharedPtr<Parser> parser = me->_engine.world()->parser();
+ if (!parser) {
+ soup_message_set_status(msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ parser->parse_string(me->_engine.world(), interface, msg->request_body->data, base_uri);
+ soup_message_set_status(msg, SOUP_STATUS_OK);
+
+ } else if (msg->method == SOUP_METHOD_DELETE) {
+ interface->del(path);
+ soup_message_set_status(msg, SOUP_STATUS_OK);
+
+ } else {
+ soup_message_set_status(msg, SOUP_STATUS_NOT_IMPLEMENTED);
+ }
+}
+
+void
+HTTPEngineReceiver::ReceiveThread::_run()
+{
+ soup_server_run(_receiver._server);
+}
+
+} // namespace Server
+} // namespace Ingen
+
diff --git a/src/http/HTTPEngineReceiver.hpp b/src/http/HTTPEngineReceiver.hpp
new file mode 100644
index 00000000..e17efe05
--- /dev/null
+++ b/src/http/HTTPEngineReceiver.hpp
@@ -0,0 +1,76 @@
+/* This file is part of Ingen.
+ * Copyright 2008-2011 David Robillard <http://drobilla.net>
+ *
+ * Ingen is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Ingen 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 General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef INGEN_ENGINE_HTTPENGINERECEIVER_HPP
+#define INGEN_ENGINE_HTTPENGINERECEIVER_HPP
+
+#include <stdint.h>
+
+#include <string>
+
+#include "raul/Thread.hpp"
+
+typedef struct _SoupServer SoupServer;
+typedef struct _SoupMessage SoupMessage;
+typedef struct SoupClientContext SoupClientContext;
+
+namespace Ingen {
+
+class ServerInterface;
+
+namespace Server {
+
+class Engine;
+
+class HTTPEngineReceiver
+{
+public:
+ HTTPEngineReceiver(Engine& engine,
+ SharedPtr<ServerInterface> interface,
+ uint16_t port);
+
+ ~HTTPEngineReceiver();
+
+private:
+ struct ReceiveThread : public Raul::Thread {
+ explicit ReceiveThread(HTTPEngineReceiver& receiver)
+ : _receiver(receiver)
+ {}
+ virtual void _run();
+ private:
+ HTTPEngineReceiver& _receiver;
+ };
+
+ friend class ReceiveThread;
+
+ static void message_callback(SoupServer* server,
+ SoupMessage* msg,
+ const char* path,
+ GHashTable *query,
+ SoupClientContext* client,
+ void* data);
+
+ Engine& _engine;
+ SharedPtr<ServerInterface> _interface;
+ ReceiveThread* _receive_thread;
+ SoupServer* _server;
+};
+
+} // namespace Server
+} // namespace Ingen
+
+#endif // INGEN_ENGINE_HTTPENGINERECEIVER_HPP
diff --git a/src/http/ingen_http.cpp b/src/http/ingen_http.cpp
new file mode 100644
index 00000000..0a08f59b
--- /dev/null
+++ b/src/http/ingen_http.cpp
@@ -0,0 +1,54 @@
+/* This file is part of Ingen.
+ * Copyright 2007-2011 David Robillard <http://drobilla.net>
+ *
+ * Ingen is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * Ingen 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 General Public License for details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "ingen/shared/Module.hpp"
+#include "ingen/shared/World.hpp"
+
+#include "../server/Engine.hpp"
+#include "../server/ServerInterfaceImpl.hpp"
+
+#include "HTTPEngineReceiver.hpp"
+
+using namespace std;
+using namespace Ingen;
+
+struct IngenHTTPModule : public Ingen::Shared::Module {
+ void load(Ingen::Shared::World* world) {
+ Server::Engine* engine = (Server::Engine*)world->local_engine().get();
+ SharedPtr<Server::ServerInterfaceImpl> interface(
+ new Server::ServerInterfaceImpl(*engine));
+
+ receiver = SharedPtr<Server::HTTPEngineReceiver>(
+ new Server::HTTPEngineReceiver(
+ *engine,
+ interface,
+ world->conf()->option("engine-port").get_int32()));
+ engine->add_event_source(interface);
+ }
+
+ SharedPtr<Server::HTTPEngineReceiver> receiver;
+};
+
+extern "C" {
+
+Ingen::Shared::Module*
+ingen_module_load()
+{
+ return new IngenHTTPModule();
+}
+
+} // extern "C"