|
|
|
#include "RestServer.hpp"
|
|
|
|
|
|
|
|
#include <boost/property_tree/ptree.hpp>
|
|
|
|
#include <boost/property_tree/json_parser.hpp>
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
using namespace chrono;
|
|
|
|
using namespace boost::property_tree;
|
|
|
|
|
|
|
|
static string response201 = "HTTP/1.1 201 Created\r\nContent-Length: 7\r\n\r\nCreated";
|
|
|
|
static string response202 = "HTTP/1.1 202 Accepted\r\nContent-Length: 8\r\n\r\nAccepted";
|
|
|
|
static string response400 = "HTTP/1.1 400 Bad Request\r\nContent-Length: 11\r\n\r\nbad request";
|
|
|
|
|
|
|
|
inline void ResponseStr(HttpServer::Response &response, string content, string type = "", string code = "200 OK")
|
|
|
|
{
|
|
|
|
response << "HTTP/1.1 " << code << "\r\n";
|
|
|
|
if (!type.empty())
|
|
|
|
response << "Content-Type: " << type <<"\r\n";
|
|
|
|
response << "Content-Length: " << content.length() << "\r\n\r\n" << content;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void ptreeToServer(boost::property_tree::ptree &pt, MasterServer::SServer &server)
|
|
|
|
{
|
|
|
|
server.SetName(pt.get<string>("hostname").c_str());
|
|
|
|
server.SetGameMode(pt.get<string>("modname").c_str());
|
|
|
|
server.SetVersion(pt.get<string>("version").c_str());
|
|
|
|
server.SetPassword(pt.get<bool>("passw"));
|
|
|
|
//server.query_port = pt.get<unsigned short>("query_port");
|
|
|
|
server.SetPlayers(pt.get<unsigned>("players"));
|
|
|
|
server.SetMaxPlayers(pt.get<unsigned>("max_players"));
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void queryToStringStream(stringstream &ss, string addr, MasterServer::SServer &query)
|
|
|
|
{
|
|
|
|
ss <<"\"" << addr << "\":{";
|
|
|
|
ss << "\"modname\": \"" << query.GetGameMode() << "\"" << ", ";
|
|
|
|
ss << "\"passw\": " << (query.GetPassword() ? "true" : "false") << ", ";
|
|
|
|
ss << "\"hostname\": \"" << query.GetName() << "\"" << ", ";
|
|
|
|
ss << "\"query_port\": " << 0 << ", ";
|
|
|
|
ss << "\"last_update\": " << duration_cast<seconds>(steady_clock::now() - query.lastUpdate).count() << ", ";
|
|
|
|
ss << "\"players\": " << query.GetPlayers() << ", ";
|
|
|
|
ss << "\"version\": \"" << query.GetVersion() << "\"" << ", ";
|
|
|
|
ss << "\"max_players\": " << query.GetMaxPlayers();
|
|
|
|
ss << "}";
|
|
|
|
}
|
|
|
|
|
|
|
|
RestServer::RestServer(unsigned short port, MasterServer::ServerMap *pMap) : serverMap(pMap)
|
|
|
|
{
|
|
|
|
httpServer.config.port = port;
|
|
|
|
}
|
|
|
|
|
|
|
|
void RestServer::start()
|
|
|
|
{
|
|
|
|
static const string ValidIpAddressRegex = "(?:[0-9]{1,3}\\.){3}[0-9]{1,3}";
|
|
|
|
static const string ValidPortRegex = "(?:[0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$";
|
|
|
|
static const string ServersRegex = "^/api/servers(?:/(" + ValidIpAddressRegex + "\\:" + ValidPortRegex + "))?";
|
|
|
|
|
|
|
|
httpServer.resource[ServersRegex]["GET"] = [this](auto response, auto request) {
|
|
|
|
if (request->path_match[1].length() > 0)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
stringstream ss;
|
|
|
|
ss << "{";
|
|
|
|
auto addr = request->path_match[1].str();
|
|
|
|
auto port = (unsigned short)stoi(&(addr[addr.find(':')+1]));
|
|
|
|
queryToStringStream(ss, "server", serverMap->at(RakNet::SystemAddress(addr.c_str(), port)));
|
|
|
|
ss << "}";
|
|
|
|
ResponseStr(*response, ss.str(), "application/json");
|
|
|
|
}
|
|
|
|
catch(out_of_range e)
|
|
|
|
{
|
|
|
|
*response << response400;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
static string str;
|
|
|
|
|
|
|
|
//if (updatedCache)
|
|
|
|
{
|
|
|
|
stringstream ss;
|
|
|
|
ss << "{";
|
|
|
|
ss << "\"list servers\":{";
|
|
|
|
for (auto query = serverMap->begin(); query != serverMap->end(); query++)
|
|
|
|
{
|
|
|
|
queryToStringStream(ss, query->first.ToString(true, ':'), query->second);
|
|
|
|
if (next(query) != serverMap->end())
|
|
|
|
ss << ", ";
|
|
|
|
}
|
|
|
|
ss << "}}";
|
|
|
|
ResponseStr(*response, ss.str(), "application/json");
|
|
|
|
updatedCache = false;
|
|
|
|
}
|
|
|
|
*response << str;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
//Add query for < 0.6 servers
|
|
|
|
httpServer.resource[ServersRegex]["POST"] = [this](auto response, auto request) {
|
|
|
|
try
|
|
|
|
{
|
|
|
|
ptree pt;
|
|
|
|
read_json(request->content, pt);
|
|
|
|
|
|
|
|
MasterServer::SServer server;
|
|
|
|
ptreeToServer(pt, server);
|
|
|
|
|
|
|
|
unsigned short port = pt.get<unsigned short>("port");
|
|
|
|
server.lastUpdate = steady_clock::now();
|
|
|
|
serverMap->insert({RakNet::SystemAddress(request->remote_endpoint_address.c_str(), port), server});
|
|
|
|
updatedCache = true;
|
|
|
|
|
|
|
|
*response << response201;
|
|
|
|
}
|
|
|
|
catch (exception& e)
|
|
|
|
{
|
|
|
|
cout << e.what() << endl;
|
|
|
|
*response << response400;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
//Update query for < 0.6 servers
|
|
|
|
httpServer.resource[ServersRegex]["PUT"] = [this](auto response, auto request) {
|
|
|
|
auto addr = request->path_match[1].str();
|
|
|
|
auto port = (unsigned short)stoi(&(addr[addr.find(':')+1]));
|
|
|
|
|
|
|
|
auto query = serverMap->find(RakNet::SystemAddress(request->remote_endpoint_address.c_str(), port));
|
|
|
|
|
|
|
|
if (query == serverMap->end())
|
|
|
|
{
|
|
|
|
cout << request->remote_endpoint_address + ": Trying to update a non-existent server or without permissions." << endl;
|
|
|
|
*response << response400;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (request->content.size() != 0)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
ptree pt;
|
|
|
|
read_json(request->content, pt);
|
|
|
|
|
|
|
|
ptreeToServer(pt, query->second);
|
|
|
|
|
|
|
|
updatedCache = true;
|
|
|
|
}
|
|
|
|
catch(exception &e)
|
|
|
|
{
|
|
|
|
cout << e.what() << endl;
|
|
|
|
*response << response400;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
query->second.lastUpdate = steady_clock::now();
|
|
|
|
|
|
|
|
*response << response202;
|
|
|
|
};
|
|
|
|
|
|
|
|
httpServer.resource["/api/servers/info"]["GET"] = [this](auto response, auto /*request*/) {
|
|
|
|
stringstream ss;
|
|
|
|
ss << '{';
|
|
|
|
ss << "\"servers\": " << serverMap->size();
|
|
|
|
unsigned int players = 0;
|
|
|
|
for (auto s : *serverMap)
|
|
|
|
players += s.second.GetPlayers();
|
|
|
|
ss << ", \"players\": " << players;
|
|
|
|
ss << "}";
|
|
|
|
|
|
|
|
ResponseStr(*response, ss.str(), "application/json");
|
|
|
|
};
|
|
|
|
|
|
|
|
httpServer.default_resource["GET"]=[](auto response, auto /*request*/) {
|
|
|
|
*response << response400;
|
|
|
|
};
|
|
|
|
|
|
|
|
httpServer.start();
|
|
|
|
}
|
|
|
|
|
|
|
|
void RestServer::cacheUpdated()
|
|
|
|
{
|
|
|
|
updatedCache = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void RestServer::stop()
|
|
|
|
{
|
|
|
|
httpServer.stop();
|
|
|
|
}
|