summaryrefslogtreecommitdiffstats
path: root/src/http
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2011-10-22 00:21:43 +0000
committerDavid Robillard <d@drobilla.net>2011-10-22 00:21:43 +0000
commit14ab4dcff7f8461dfed27b6352249b683c1f4bae (patch)
treef263ecca5c5c2a220bd00c69bba11a03ad266dc5 /src/http
parent91bd13d3767452ba0575d69447f23eed1c508603 (diff)
downloadingen-14ab4dcff7f8461dfed27b6352249b683c1f4bae.tar.gz
ingen-14ab4dcff7f8461dfed27b6352249b683c1f4bae.tar.bz2
ingen-14ab4dcff7f8461dfed27b6352249b683c1f4bae.zip
Move *all* OSC and HTTP stuff to their respective modules.
git-svn-id: http://svn.drobilla.net/lad/trunk/ingen@3578 a436a847-0d15-0410-975c-d299462d15a1
Diffstat (limited to 'src/http')
-rw-r--r--src/http/HTTPClientReceiver.cpp241
-rw-r--r--src/http/HTTPClientReceiver.hpp85
-rw-r--r--src/http/HTTPClientSender.hpp3
-rw-r--r--src/http/HTTPEngineSender.cpp194
-rw-r--r--src/http/HTTPEngineSender.hpp126
-rw-r--r--src/http/HTTPSender.cpp156
-rw-r--r--src/http/HTTPSender.hpp61
-rw-r--r--src/http/ingen_http_server.cpp (renamed from src/http/ingen_http.cpp)0
8 files changed, 865 insertions, 1 deletions
diff --git a/src/http/HTTPClientReceiver.cpp b/src/http/HTTPClientReceiver.cpp
new file mode 100644
index 00000000..0ec5aaa4
--- /dev/null
+++ b/src/http/HTTPClientReceiver.cpp
@@ -0,0 +1,241 @@
+/* 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 <assert.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/socket.h>
+
+#include <libsoup/soup.h>
+
+#include "ingen/shared/World.hpp"
+#include "raul/log.hpp"
+
+#include "HTTPClientReceiver.hpp"
+
+#define LOG(s) s << "[HTTPClientReceiver] "
+
+using namespace std;
+using namespace Raul;
+
+namespace Ingen {
+
+using namespace Serialisation;
+
+namespace Client {
+
+static SoupSession* client_session = NULL;
+static HTTPClientReceiver* client_receiver = NULL;
+
+HTTPClientReceiver::HTTPClientReceiver(
+ Shared::World* world,
+ const std::string& url,
+ SharedPtr<ClientInterface> target)
+ : _target(target)
+ , _world(world)
+ , _url(url)
+{
+ start(false);
+ client_receiver = this;
+}
+
+HTTPClientReceiver::~HTTPClientReceiver()
+{
+ stop();
+ if (client_receiver == this)
+ client_receiver = NULL;
+}
+
+HTTPClientReceiver::Listener::Listener(HTTPClientReceiver* receiver, const std::string& uri)
+ : _uri(uri)
+ , _receiver(receiver)
+{
+ const string port_str = uri.substr(uri.find_last_of(":")+1);
+ int port = atoi(port_str.c_str());
+
+ LOG(info) << "Client HTTP listen: " << uri << " (port " << port << ")" << endl;
+
+ struct sockaddr_in servaddr;
+
+ // Create listen address
+ memset(&servaddr, 0, sizeof(servaddr));
+ servaddr.sin_family = AF_INET;
+ servaddr.sin_port = htons(port);
+
+ // Create listen socket
+ if ((_sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ LOG(error) << "Error creating listening socket: %s" << strerror(errno) << endl;
+ _sock = -1;
+ return;
+ }
+
+ // Set remote address (FIXME: always localhost)
+ if (inet_aton("127.0.0.1", &servaddr.sin_addr) <= 0) {
+ LOG(error) << "Invalid remote IP address" << endl;
+ _sock = -1;
+ return;
+ }
+
+ // Connect to server
+ if (connect(_sock, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) {
+ LOG(error) << "Error calling connect: " << strerror(errno) << endl;
+ _sock = -1;
+ return;
+ }
+}
+
+HTTPClientReceiver::Listener::~Listener()
+{
+ close(_sock);
+}
+
+void
+HTTPClientReceiver::send(SoupMessage* msg)
+{
+ if (!client_session) {
+ LOG(debug) << "Starting session" << endl;
+ client_session = soup_session_sync_new();
+ }
+
+ assert(SOUP_IS_MESSAGE(msg));
+ soup_session_queue_message(client_session, msg, message_callback, client_receiver);
+}
+
+void
+HTTPClientReceiver::close_session()
+{
+ if (client_session) {
+ SoupSession* s = client_session;
+ client_session = NULL;
+ soup_session_abort(s);
+ }
+}
+
+void
+HTTPClientReceiver::update(const std::string& str)
+{
+ //LOG(info) << _world->parser()->parse_update(_world, _target.get(), str, _url);
+}
+
+void
+HTTPClientReceiver::Listener::_run()
+{
+ char in = '\0';
+ char last = '\0';
+ char llast = '\0';
+ string recv;
+
+ while (true) {
+ while (read(_sock, &in, 1) > 0 ) {
+ recv += in;
+ if (in == '\n' && last == '\n' && llast == '\n') {
+ if (!recv.empty()) {
+ _receiver->update(recv);
+ recv.clear();
+ last = '\0';
+ llast = '\0';
+ }
+ break;
+ }
+ llast = last;
+ last = in;
+ }
+ }
+
+ LOG(info) << "HTTP listener finished" << endl;
+}
+
+void
+HTTPClientReceiver::message_callback(SoupSession* session, SoupMessage* msg, void* ptr)
+{
+ if (ptr == NULL)
+ return;
+
+ HTTPClientReceiver* me = (HTTPClientReceiver*)ptr;
+ const string path = soup_message_get_uri(msg)->path;
+
+ if (msg->response_body->data == NULL) {
+ LOG(error) << "Empty client message" << endl;
+ return;
+ }
+
+ if (path == "/") {
+ me->_target->response_ok(0);
+
+ } else if (path == "/plugins") {
+ if (msg->response_body->data == NULL) {
+ LOG(error) << "Empty response" << endl;
+ } else {
+ Glib::Mutex::Lock lock(me->_mutex);
+ me->_target->response_ok(0);
+ me->_world->parser()->parse_string(me->_world, me->_target.get(),
+ Glib::ustring(msg->response_body->data), me->_url);
+ }
+
+ } else if (path.substr(0, 6) == "/patch") {
+ if (msg->response_body->data == NULL) {
+ LOG(error) << "Empty response" << endl;
+ } else {
+ Glib::Mutex::Lock lock(me->_mutex);
+ me->_target->response_ok(0);
+ me->_world->parser()->parse_string(
+ me->_world,
+ me->_target.get(),
+ Glib::ustring(msg->response_body->data),
+ path);
+ }
+
+ } else if (path == "/stream") {
+ if (msg->response_body->data == NULL) {
+ LOG(error) << "Empty response" << endl;
+ } else {
+ Glib::Mutex::Lock lock(me->_mutex);
+ string uri = string(soup_uri_to_string(soup_message_get_uri(msg), false));
+ uri = uri.substr(0, uri.find_last_of(":"));
+ uri += string(":") + msg->response_body->data;
+ LOG(info) << "Stream URI: " << uri << endl;
+ me->_listener = boost::shared_ptr<Listener>(new Listener(me, uri));
+ me->_listener->start();
+ }
+
+ } else {
+ LOG(error) << "Unknown message: " << path << endl;
+ me->update(msg->response_body->data);
+ }
+}
+
+void
+HTTPClientReceiver::start(bool dump)
+{
+ if (!_world->parser())
+ _world->load_module("serialisation");
+
+ SoupMessage* msg = soup_message_new("GET", (_url + "/stream").c_str());
+ assert(SOUP_IS_MESSAGE(msg));
+ soup_session_queue_message(client_session, msg, message_callback, this);
+}
+
+void
+HTTPClientReceiver::stop()
+{
+ //unregister_client();
+ close_session();
+}
+
+} // namespace Client
+} // namespace Ingen
+
diff --git a/src/http/HTTPClientReceiver.hpp b/src/http/HTTPClientReceiver.hpp
new file mode 100644
index 00000000..d7165c81
--- /dev/null
+++ b/src/http/HTTPClientReceiver.hpp
@@ -0,0 +1,85 @@
+/* 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_CLIENT_HTTPCLIENTRECEIVER_HPP
+#define INGEN_CLIENT_HTTPCLIENTRECEIVER_HPP
+
+#include <cstdlib>
+
+#include <boost/utility.hpp>
+#include <glibmm/thread.h>
+
+#include "ingen/ClientInterface.hpp"
+#include "ingen/serialisation/Parser.hpp"
+#include "raul/Deletable.hpp"
+#include "raul/SharedPtr.hpp"
+#include "raul/Thread.hpp"
+#include "sord/sordmm.hpp"
+
+typedef struct _SoupSession SoupSession;
+typedef struct _SoupMessage SoupMessage;
+
+namespace Ingen {
+namespace Client {
+
+class HTTPClientReceiver : public boost::noncopyable, public Raul::Deletable
+{
+public:
+ HTTPClientReceiver(Shared::World* world,
+ const std::string& url,
+ SharedPtr<ClientInterface> target);
+
+ ~HTTPClientReceiver();
+
+ static void send(SoupMessage* msg);
+ static void close_session();
+
+ std::string uri() const { return _url; }
+
+ void start(bool dump);
+ void stop();
+
+private:
+ static void message_callback(SoupSession* session, SoupMessage* msg, void* ptr);
+
+ void update(const std::string& str);
+
+ class Listener : public Raul::Thread {
+ public:
+ Listener(HTTPClientReceiver* receiver, const std::string& uri);
+ ~Listener();
+ void _run();
+ private:
+ std::string _uri;
+ int _sock;
+ HTTPClientReceiver* _receiver;
+ };
+
+ friend class Listener;
+
+ SharedPtr<Listener> _listener;
+ Glib::Mutex _mutex;
+ SharedPtr<ClientInterface> _target;
+ Shared::World* _world;
+ const std::string _url;
+ bool _quit_flag;
+};
+
+} // namespace Client
+} // namespace Ingen
+
+#endif // INGEN_CLIENT_HTTPCLIENTRECEIVER_HPP
diff --git a/src/http/HTTPClientSender.hpp b/src/http/HTTPClientSender.hpp
index 116f0601..761a60a6 100644
--- a/src/http/HTTPClientSender.hpp
+++ b/src/http/HTTPClientSender.hpp
@@ -24,7 +24,8 @@
#include "raul/Thread.hpp"
#include "ingen/ClientInterface.hpp"
-#include "../shared/HTTPSender.hpp"
+
+#include "HTTPSender.hpp"
namespace Ingen {
diff --git a/src/http/HTTPEngineSender.cpp b/src/http/HTTPEngineSender.cpp
new file mode 100644
index 00000000..43a3d44c
--- /dev/null
+++ b/src/http/HTTPEngineSender.cpp
@@ -0,0 +1,194 @@
+/* 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 <libsoup/soup.h>
+
+#include "raul/AtomRDF.hpp"
+#include "raul/log.hpp"
+#include "sord/sordmm.hpp"
+
+#include "ingen/shared/World.hpp"
+#include "HTTPEngineSender.hpp"
+#include "HTTPClientReceiver.hpp"
+
+#define LOG(s) s << "[HTTPEngineSender] "
+
+using namespace std;
+using namespace Raul;
+
+namespace Ingen {
+
+using namespace Shared;
+
+namespace Client {
+
+HTTPEngineSender::HTTPEngineSender(World* world,
+ const URI& engine_url,
+ SharedPtr<Raul::Deletable> receiver)
+ : _receiver(receiver)
+ , _world(*world->rdf_world())
+ , _engine_url(engine_url)
+ , _id(0)
+ , _enabled(true)
+{
+ _session = soup_session_sync_new();
+}
+
+HTTPEngineSender::~HTTPEngineSender()
+{
+ soup_session_abort(_session);
+}
+
+void
+HTTPEngineSender::attach(int32_t ping_id, bool block)
+{
+ LOG(debug) << "Attaching to " << _engine_url << endl;
+ SoupMessage* msg = soup_message_new ("GET", _engine_url.c_str());
+ HTTPClientReceiver::send(msg);
+}
+
+/* *** ServerInterface implementation below here *** */
+
+/** Register with the engine via HTTP.
+ *
+ * Note that this does not actually use 'key', since the engine creates
+ * it's own key for HTTP clients (namely the incoming URL), for NAT
+ * traversal. It is a parameter to remain compatible with ServerInterface.
+ */
+void
+HTTPEngineSender::register_client(ClientInterface* client)
+{
+ /*SoupMessage* msg = soup_message_new("GET", (_engine_url.str() + "/stream").c_str());
+ HTTPClientReceiver::send(msg);*/
+}
+
+void
+HTTPEngineSender::unregister_client(const URI& uri)
+{
+}
+
+// Object commands
+
+void
+HTTPEngineSender::put(const URI& uri,
+ const Resource::Properties& properties,
+ Resource::Graph ctx)
+{
+ const string path = (uri.substr(0, 6) == "path:/") ? uri.substr(6) : uri.str();
+ const string full_uri = _engine_url.str() + "/" + path;
+
+ Sord::Model model(_world);
+ for (Resource::Properties::const_iterator i = properties.begin(); i != properties.end(); ++i)
+ model.add_statement(Sord::URI(_world, path),
+ AtomRDF::atom_to_node(model, i->first),
+ AtomRDF::atom_to_node(model, i->second));
+
+ const string str = model.write_to_string("");
+ SoupMessage* msg = soup_message_new(SOUP_METHOD_PUT, full_uri.c_str());
+ assert(msg);
+ soup_message_set_request(msg, "application/x-turtle", SOUP_MEMORY_COPY, str.c_str(), str.length());
+ soup_session_send_message(_session, msg);
+}
+
+void
+HTTPEngineSender::delta(const Raul::URI& path,
+ const Resource::Properties& remove,
+ const Resource::Properties& add)
+{
+ LOG(warn) << "TODO: HTTP delta" << endl;
+}
+
+void
+HTTPEngineSender::move(const Path& old_path,
+ const Path& new_path)
+{
+ SoupMessage* msg = soup_message_new(SOUP_METHOD_MOVE,
+ (_engine_url.str() + old_path.str()).c_str());
+ soup_message_headers_append(msg->request_headers, "Destination",
+ (_engine_url.str() + new_path.str()).c_str());
+ soup_session_send_message(_session, msg);
+}
+
+void
+HTTPEngineSender::del(const URI& uri)
+{
+ const string path = (uri.substr(0, 6) == "path:/") ? uri.substr(6) : uri.str();
+ const string full_uri = _engine_url.str() + "/" + path;
+ SoupMessage* msg = soup_message_new(SOUP_METHOD_DELETE, full_uri.c_str());
+ soup_session_send_message(_session, msg);
+}
+
+void
+HTTPEngineSender::connect(const Path& src_port_path,
+ const Path& dst_port_path)
+{
+ LOG(warn) << "TODO: HTTP connect" << endl;
+}
+
+void
+HTTPEngineSender::disconnect(const URI& src,
+ const URI& dst)
+{
+ LOG(warn) << "TODO: HTTP disconnect" << endl;
+}
+
+void
+HTTPEngineSender::disconnect_all(const Path& parent_patch_path,
+ const Path& path)
+{
+ LOG(warn) << "TODO: HTTP disconnect_all" << endl;
+}
+
+void
+HTTPEngineSender::set_property(const URI& subject,
+ const URI& predicate,
+ const Atom& value)
+{
+ Resource::Properties prop;
+ prop.insert(make_pair(predicate, value));
+ put(subject, prop);
+}
+
+// Requests //
+
+void
+HTTPEngineSender::ping()
+{
+ LOG(debug) << "Ping " << _engine_url << endl;
+ get(_engine_url);
+}
+
+void
+HTTPEngineSender::get(const URI& uri)
+{
+ if (!Raul::Path::is_path(uri) && uri.scheme() != "http") {
+ LOG(warn) << "Ignoring GET of non-HTTP URI " << uri << endl;
+ return;
+ }
+
+ const std::string request_uri = (Raul::Path::is_path(uri))
+ ?_engine_url.str() + "/patch" + uri.substr(uri.find("/"))
+ : uri.str();
+ cout << "Get " << request_uri << endl;
+ LOG(debug) << "Get " << request_uri << endl;
+ SoupMessage* msg = soup_message_new("GET", request_uri.c_str());
+ HTTPClientReceiver::send(msg);
+}
+
+} // namespace Client
+} // namespace Ingen
+
diff --git a/src/http/HTTPEngineSender.hpp b/src/http/HTTPEngineSender.hpp
new file mode 100644
index 00000000..14439e39
--- /dev/null
+++ b/src/http/HTTPEngineSender.hpp
@@ -0,0 +1,126 @@
+/* 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_CLIENT_HTTPENGINESENDER_HPP
+#define INGEN_CLIENT_HTTPENGINESENDER_HPP
+
+#include <inttypes.h>
+
+#include <string>
+
+#include "raul/Deletable.hpp"
+#include "raul/Path.hpp"
+#include "sord/sordmm.hpp"
+
+#include "ingen/ServerInterface.hpp"
+
+typedef struct _SoupSession SoupSession;
+
+namespace Ingen {
+
+namespace Shared { class World; }
+
+namespace Client {
+
+class HTTPClientReceiver;
+
+/* HTTP (via libsoup) interface to the engine.
+ *
+ * Clients can use this opaquely as an ServerInterface to control the engine
+ * over HTTP (whether over a network or not).
+ *
+ * \ingroup IngenClient
+ */
+class HTTPEngineSender : public ServerInterface
+{
+public:
+ HTTPEngineSender(Shared::World* world,
+ const Raul::URI& engine_url,
+ SharedPtr<Raul::Deletable> receiver);
+
+ ~HTTPEngineSender();
+
+ Raul::URI uri() const { return _engine_url; }
+
+ inline int32_t next_id()
+ { int32_t ret = (_id == -1) ? -1 : _id++; return ret; }
+
+ void respond_to(ClientInterface* client, int32_t id) { _id = id; }
+ void disable_responses() { _id = -1; }
+
+ void attach(int32_t ping_id, bool block);
+
+ /* *** ServerInterface implementation below here *** */
+
+ void enable() { _enabled = true; }
+ void disable() { _enabled = false; }
+
+ void bundle_begin() {}
+ void bundle_end() {}
+
+ // Client registration
+ void register_client(ClientInterface* client);
+ void unregister_client(const Raul::URI& uri);
+
+ // Object commands
+
+ virtual void put(const Raul::URI& path,
+ const Resource::Properties& properties,
+ Resource::Graph ctx=Resource::DEFAULT);
+
+ 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,
+ const Raul::URI& predicate,
+ const Raul::Atom& value);
+
+ // Requests //
+ void ping();
+ void get(const Raul::URI& uri);
+
+protected:
+ SharedPtr<Raul::Deletable> _receiver;
+
+ SoupSession* _session;
+ Sord::World& _world;
+ const Raul::URI _engine_url;
+ int _client_port;
+ int32_t _id;
+ bool _enabled;
+};
+
+} // namespace Client
+} // namespace Ingen
+
+#endif // INGEN_CLIENT_HTTPENGINESENDER_HPP
+
diff --git a/src/http/HTTPSender.cpp b/src/http/HTTPSender.cpp
new file mode 100644
index 00000000..1a4d35da
--- /dev/null
+++ b/src/http/HTTPSender.cpp
@@ -0,0 +1,156 @@
+/* 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 <cassert>
+#include <cstdio>
+#include <cstring>
+#include <unistd.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <errno.h>
+#include "raul/log.hpp"
+#include "HTTPSender.hpp"
+
+using namespace std;
+using namespace Raul;
+
+namespace Ingen {
+namespace Shared {
+
+HTTPSender::HTTPSender()
+ : _listen_port(-1)
+ , _listen_sock(-1)
+ , _client_sock(-1)
+ , _send_state(Immediate)
+{
+ Thread::set_name("HTTPSender");
+
+ struct sockaddr_in addr;
+
+ // Create listen address
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+
+ // Create listen socket
+ if ((_listen_sock = socket(AF_INET, SOCK_STREAM, 0)) < 0 ) {
+ error << "Error creating listening socket (" << strerror(errno) << ")" << endl;
+ exit(EXIT_FAILURE);
+ }
+
+ // Bind our socket addresss to the listening socket
+ if (bind(_listen_sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
+ error << "Error calling bind (%s)\n" << strerror(errno) << ")" << endl;
+ _listen_sock = -1;
+ }
+
+ // Find port number
+ socklen_t length = sizeof(addr);
+ if (getsockname(_listen_sock, (struct sockaddr*)&addr, &length) == -1) {
+ error << "Error calling getsockname (" << strerror(errno) << ")" << endl;
+ _listen_sock = -1;
+ return;
+ }
+
+ if (listen(_listen_sock, 1) < 0 ) {
+ error << "Error calling listen (" << strerror(errno) << ")" << endl;
+ _listen_sock = -1;
+ return;
+ }
+
+ _listen_port = ntohs(addr.sin_port);
+ info << "Opening event stream on TCP port " << _listen_port << endl;
+ start();
+}
+
+HTTPSender::~HTTPSender()
+{
+ stop();
+ if (_listen_sock != -1)
+ close(_listen_sock);
+ if (_client_sock != -1)
+ close(_client_sock);
+}
+
+void
+HTTPSender::_run()
+{
+ if (_listen_sock == -1) {
+ error << "Unable to open socket, exiting sender thread" << endl;
+ return;
+ }
+
+ // Accept connection
+ if ((_client_sock = accept(_listen_sock, NULL, NULL) ) < 0) {
+ error << "Error calling accept: " << strerror(errno) << endl;
+ return;
+ }
+
+ // Hold connection open and write when signalled
+ while (true) {
+ _mutex.lock();
+ _signal.wait(_mutex);
+
+ write(_client_sock, _transfer.c_str(), _transfer.length());
+ write(_client_sock, "\n\n\n", 3);
+
+ _signal.broadcast();
+ _mutex.unlock();
+ }
+
+ close(_listen_sock);
+ _listen_sock = -1;
+}
+
+void
+HTTPSender::bundle_begin()
+{
+ _mutex.lock();
+ _send_state = SendingBundle;
+ _transfer = "";
+}
+
+void
+HTTPSender::bundle_end()
+{
+ assert(_send_state == SendingBundle);
+ _signal.broadcast();
+ _signal.wait(_mutex);
+ _send_state = Immediate;
+ _mutex.unlock();
+}
+
+void
+HTTPSender::send_chunk(const std::string& buf)
+{
+ if (_send_state == Immediate) {
+ _mutex.lock();
+ _transfer = buf;
+ _signal.broadcast();
+ _signal.wait(_mutex);
+ _mutex.unlock();
+ } else {
+ _transfer.append(buf);
+ }
+}
+
+} // namespace Shared
+} // namespace Ingen
diff --git a/src/http/HTTPSender.hpp b/src/http/HTTPSender.hpp
new file mode 100644
index 00000000..a1a3ea2a
--- /dev/null
+++ b/src/http/HTTPSender.hpp
@@ -0,0 +1,61 @@
+/* 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_SHARED_HTTPSENDER_HPP
+#define INGEN_SHARED_HTTPSENDER_HPP
+
+#include <string>
+
+#include <glibmm/thread.h>
+
+#include "raul/Thread.hpp"
+
+namespace Ingen {
+namespace Shared {
+
+class HTTPSender : public Raul::Thread {
+public:
+ HTTPSender();
+ virtual ~HTTPSender();
+
+ void bundle_begin();
+ void bundle_end();
+
+ int listen_port() const { return _listen_port; }
+
+protected:
+ void _run();
+
+ void send_chunk(const std::string& buf);
+
+ enum SendState { Immediate, SendingBundle };
+
+ Glib::Mutex _mutex;
+ Glib::Cond _signal;
+
+ int _listen_port;
+ int _listen_sock;
+ int _client_sock;
+ SendState _send_state;
+ std::string _transfer;
+};
+
+} // namespace Shared
+} // namespace Ingen
+
+#endif // INGEN_SHARED_HTTPSENDER_HPP
+
diff --git a/src/http/ingen_http.cpp b/src/http/ingen_http_server.cpp
index 0a08f59b..0a08f59b 100644
--- a/src/http/ingen_http.cpp
+++ b/src/http/ingen_http_server.cpp