/*
This file is part of Ingen.
Copyright 2007-2012 David Robillard
Ingen is free software: you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free
Software Foundation, either version 3 of the License, or 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 Affero General Public License for details.
You should have received a copy of the GNU Affero General Public License
along with Ingen. If not, see .
*/
#include
#include
#include
#include
#include
#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 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 = 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 = 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: .\n")
.append("\n<> rdfs:seeAlso ;")
.append("\n rdfs:seeAlso ;")
.append("\n rdfs:seeAlso .");
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 = 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 = 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