/************************************************************************ 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; } }