// // Created by koncord on 13.05.17. // #include "RestServer.hpp" #include #include 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("hostname").c_str()); server.SetGameMode(pt.get("modname").c_str()); server.SetVersion(pt.get("version").c_str()); server.SetPassword(pt.get("passw")); //server.query_port = pt.get("query_port"); server.SetPlayers(pt.get("players")); server.SetMaxPlayers(pt.get("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(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("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(); }