/************************************************************************
MeOS - Orienteering Software
Copyright (C) 2009-2017 Melin Software HB
This program 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 3 of the License, or
(at your option) any later version.
This program 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 more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
Melin Software HB - software@melin.nu - www.melin.nu
Eksoppsvägen 16, SE-75646 UPPSALA, Sweden
************************************************************************/
#include "stdafx.h"
#include "oEvent.h"
#include "xmlparser.h"
#include
#include "restbed/restbed"
#include "meosexception.h"
#include "restserver.h"
#include "infoserver.h"
#include
using namespace restbed;
vector< shared_ptr > RestServer::startedServers;
shared_ptr RestServer::construct() {
shared_ptr obj(new RestServer());
startedServers.push_back(obj);
return obj;
}
void RestServer::remove(shared_ptr server) {
// xxx std::remove(startedServers.begin(), startedServers.end(), server);
}
RestServer::RestServer() : hasAnyRequest(false) {
}
RestServer::~RestServer() {
stop();
}
class MeOSResource : public restbed::Resource {
RestServer *server;
public:
MeOSResource(RestServer *server) : server(server) {}
~MeOSResource() {
}
RestServer &getServer() const { return *server; }
};
static void method_handler(const shared_ptr< restbed::Session > session) {
RestServer &server = dynamic_cast(*session->get_resource()).getServer();
server.handleRequest(session);
}
void RestServer::handleRequest(const shared_ptr &session) {
const auto request = session->get_request();
size_t content_length = request->get_header("Content-Length", 0);
chrono::time_point start, end;
start = chrono::system_clock::now();
auto param = request->get_query_parameters();
auto answer = RestServer::addRequest(param);
{
unique_lock mlock(lock);
if (!waitForCompletion.wait_for(mlock, 10s, [answer] {return answer->isCompleted(); })) {
answer->answer = "Error (MeOS): Internal timeout";
}
end = chrono::system_clock::now();
chrono::duration elapsed_seconds = end - start;
responseTimes.push_back(int(1000 * elapsed_seconds.count()));
}
// lock.unlock();
session->fetch(content_length, [request, answer](const shared_ptr< Session > session, const Bytes & body)
{
//fprintf(stdout, "%.*s\n", (int)body.size(), body.data());
/*while (!answer->state) {
std::this_thread::yield();
}*/
session->close(restbed::OK, answer->answer, { { "Content-Length", itos(answer->answer.length()) },{ "Connection", "close" } });
});
}
void RestServer::startThread(int port) {
auto settings = make_shared();
settings->set_port(port);
auto resource = make_shared(this);
resource->set_path("/meos");
resource->set_method_handler("GET", method_handler);
restService->publish(resource);
restService->start(settings);
}
void RestServer::startService(int port) {
if (service)
throw meosException("Server started");
restService.reset(new restbed::Service());
service = make_shared(&RestServer::startThread, this, port);
}
void RestServer::stop() {
if (restService)
restService->stop();
if (service && service->joinable())
service->join();
restService.reset();
service.reset();
}
void RestServer::computeRequested(oEvent &ref) {
for (auto &server : startedServers) {
server->compute(ref);
}
}
void RestServer::compute(oEvent &ref) {
auto rq = getRequest();
if (!rq)
return;
if (rq->parameters.empty()) {
rq->answer = ""
"MeOS Information Service"
""
""
"MeOS
"
""
"";
}
else if (rq->parameters.count("get") > 0) {
string what = rq->parameters.find("get")->second;
getData(ref, what, rq->parameters, rq->answer);
}
else {
rq->answer = "Error (MeOS): Unknown request";
}
{
lock_guard lg(lock);
rq->state = true;
}
waitForCompletion.notify_all();
}
void RestServer::getData(oEvent &oe, const string &what, const multimap ¶m, string &answer) {
xmlbuffer out;
out.setComplete(true);
if (what == "competition") {
InfoCompetition cmp(0);
cmp.synchronize(oe);
cmp.serialize(out, false);
}
else if (what == "class") {
vector cls;
oe.getClasses(cls, true);
for (auto c : cls) {
InfoClass iCls(c->getId());
set ctrl;
iCls.synchronize(*c, ctrl);
iCls.serialize(out, false);
}
}
else if (what == "competitor") {
vector r;
set selection;
if (param.count("id") > 0)
getSelection(param.find("id")->second, selection);
oe.getRunners(selection.size() == 1 ? *selection.begin() : 0, -1, r, true);
for (auto c : r) {
InfoCompetitor iR(c->getId());
iR.synchronize(false, *c);
iR.serialize(out, false);
}
}
if (out.size() > 0) {
xmlparser mem;
mem.openMemoryOutput(false);
mem.startTag("MOPComplete", "xmlns", "http://www.melin.nu/mop");
out.commit(mem, 100000);
mem.endTag();
mem.getMemoryOutput(answer);
}
else {
answer = "Error (MeOS): Unknown command '" + what + "'";
}
}
void RestServer::getSelection(const string ¶m, set &sel) {
vector sw;
split(param, ";,", sw);
for (auto &s : sw) {
int id = atoi(s.c_str());
if (id > 0)
sel.insert(id);
}
}
shared_ptr RestServer::getRequest() {
shared_ptr res;
if (hasAnyRequest) {
lock_guard lg(lock);
if (!requests.empty()) {
res = requests.front();
requests.pop_front();
}
if (requests.empty())
hasAnyRequest = false;
}
return res;
}
shared_ptr RestServer::addRequest(multimap ¶m) {
auto rq = make_shared();
rq->parameters.swap(param);
lock_guard lg(lock);
requests.push_back(rq);
hasAnyRequest = true;
return rq;
}
void RestServer::getStatistics(Statistics &s) {
lock_guard lg(lock);
s.numRequests = responseTimes.size();
s.maxResponseTime = 0;
s.averageResponseTime = 0;
for (int t : responseTimes) {
s.maxResponseTime = max(s.maxResponseTime, t);
s.averageResponseTime += t;
}
if (s.numRequests > 0) {
s.averageResponseTime /= s.numRequests;
}
}