Commit c6804497 authored by Micha Mueller's avatar Micha Mueller
Browse files

Further refactoring of HttpsServer

parent a2bc693a
......@@ -23,41 +23,45 @@ void HttpsServer::requestHandler::operator()(server::request const &request, ser
LOG(info) << "HttpsServer: " << ip << ":" << port << " connected";
//set appropriate default value to connection status
connection->set_status(server::connection::internal_server_error);
std::ostringstream data;
boost::network::uri::uri uri("https://" + request.destination);
server::string_type method = request.method;
server::string_type path = uri.path();
server::string_type query = uri.query();
std::string response = "";
std::string auth_value = "";
std::vector<std::string> pathStrs;
std::vector<std::pair<std::string, std::string>> queries;
//first check if request is supported at all
if (method != "GET" && method != "PUT") {
LOG(info) << "HttpsServer: Unsupported " << method << " request was made";
LOG(warning) << "HttpsServer: Unsupported " << method << " request was made";
connection->set_status(server::connection::not_supported);
} else {
LOG(info) << "HttpsServer: " << method << " request of " << request.destination << " was made";
std::string response = "";
boost::network::uri::uri uri("https://" + request.destination);
server::string_type path = uri.path();
server::string_type query = uri.query();
goto error;
}
//do some string processing
std::vector<std::string> pathStrs;
std::vector<std::pair<std::string, std::string>> queries;
LOG(info) << "HttpsServer: " << method << " request of " << request.destination << " was made";
//split path into its hierarchical parts
if (path.size() >= 2) {
if (path[0] == '/') {
path.erase(0,1);
}
if (path[path.size() -1] == '/') {
path.erase(path.size() -1);
}
boost::split(pathStrs, path, boost::is_any_of("/"), boost::token_compress_off);
//do some string processing
//split path into its hierarchical parts
if (path.size() >= 2) {
if (path[0] == '/') {
path.erase(0,1);
}
if (path[path.size() -1] == '/') {
path.erase(path.size() -1);
}
//split query part into the individual queries
boost::split(pathStrs, path, boost::is_any_of("/"), boost::token_compress_off);
}
//split query part into the individual queries (key-value pairs)
{
std::vector<std::string> queryStrs;
boost::split(queryStrs, query, boost::is_any_of(";"), boost::token_compress_on);
for(auto& key : queryStrs) {
......@@ -69,127 +73,174 @@ void HttpsServer::requestHandler::operator()(server::request const &request, ser
queries.push_back(std::make_pair(key, value));
}
}
//finished string processing
}
//finished string processing
std::string auth_value = "";
for (auto& p : queries) {
if (p.first == "authkey") {
auth_value = p.second;
break;
}
//authkey is required in every case
for (auto& p : queries) {
if (p.first == "authkey") {
auth_value = p.second;
break;
}
}
if (pathStrs.size() < 2 || auth_value == "") {
LOG(error) << "Received malformed request";
connection->set_status(server::connection::bad_request);
if (pathStrs.size() < 1) {
LOG(warning) << "Received malformed request: No first path part";
connection->set_status(server::connection::bad_request);
goto error;
}
//select code depending on request
if (method == "GET") {
//first check permission
if (!_httpsServer.check_authkey(auth_value, permission::GETReq)) {
LOG(warning) << "Provided authentication token has insufficient permissions";
connection->set_status(server::connection::unauthorized);
goto error;
}
if (pathStrs[0] == "help") {
response = "dcdbpusher RESTful API cheatsheet:\n"
" -GET: /help This help message\n"
" /plugins List of currently loaded plugins (Discovery)\n"
" /[plugin]/sensors\n"
" List of currently running sensors which belong\n"
" to the specified plugin (Discovery)\n"
" /[plugin]/[sensor]/avg?interval=[timeInSec]\n"
" Average of last sensor readings from the last\n"
" [interval] seconds or of all cached readings\n"
" if no interval is given\n"
" -PUT: /[plugin]/[start|stop]\n"
" Start/stop the sensors of the plugin\n"
"\n"
"All resources have to be prepended by host:port and need at\n"
"least the query ?authkey=[token] at the end. Multiple queries\n"
"need to be separated by semicolons(';')\n";
} else if (pathStrs[0] == "plugins") {
//TODO make (JSON) list of plugins
} else {
std::string plugin = pathStrs[0];
//select code depending on request
/* ##################################################### GET ##################################################### */
if (method == "GET") {
//do some prior checks
if (pathStrs.size() < 2) {
LOG(warning) << "Received malformed request: No second path part";
connection->set_status(server::connection::bad_request);
goto error;
}
if (pathStrs[1] == "sensors") {
//TODO make (JSON) list of sensors
} else {
//do some prior checks
if (pathStrs.size() < 3 || pathStrs[2] != "avg") {
response = "GET requests should be of the form \"host:port/[plugin]/[sensor]/[action]?authkey=[token]\"\n"
"Where [plugin] names a plugin, [sensor] a sensor within this plugin, [action] is 'avg' and [token] is your authentication key";
if (pathStrs.size() < 3) {
LOG(warning) << "Received malformed request: No third path part";
connection->set_status(server::connection::bad_request);
} else {
std::string sensor = pathStrs[1];
std::string action = pathStrs[2];
uint64_t time = 0;
for (auto& p : queries) {
if (p.first == "interval") {
time = getTimestamp();
time -= S_TO_NS(std::stoul(p.second));
}
}
goto error;
}
//process actual request
//check if query and action are valid values
//check if authkey is valid
if (_httpsServer.check_authkey(auth_value, permission::GETReq)) {
response = "Plugin not found!";
connection->set_status(server::connection::not_found);
for(auto& p : _httpsServer._plugins) {
if (p.id == plugin) {
response = "Sensor not found!";
for(auto s : p.configurator->getSensors()) {
if (s->getName() == sensor) {
uint64_t avg = 0;
const reading_t * const cache = s->getCache();
unsigned size = s->getCacheSize();
unsigned count = 0;
for(unsigned i = 0; i < size; i++) {
if (cache[i].timestamp > time) {
avg += cache[i].value;
count++;
}
}
if (count > 0)
avg /= count;
response = plugin + "::" + sensor + " Average of last " + std::to_string(count) + " values is " + std::to_string(avg);
connection->set_status(server::connection::ok);
}
}
}
}
} else {
response = "Invalid authentication key!";
connection->set_status(server::connection::unauthorized);
if (pathStrs[2] != "avg") {
LOG(warning) << "Unknown action " << pathStrs[2] << " requested";
connection->set_status(server::connection::not_supported);
goto error;
}
std::string sensor = pathStrs[1];
std::string action = pathStrs[2];
uint64_t time = 0;
for (auto& p : queries) {
if (p.first == "interval") {
time = getTimestamp();
time -= S_TO_NS(std::stoul(p.second));
}
}
/* ##################################################### PUT ##################################################### */
} else if (method == "PUT") {
std::string action = pathStrs[1];
//process actual request
//check if query and action are valid values
if ((action == "start") || (action == "stop")) {
//check if authkey is valid
if (_httpsServer.check_authkey(auth_value, permission::PUTReq)) {
response = "Plugin not found!";
connection->set_status(server::connection::not_found);
for(auto& p : _httpsServer._plugins) {
if (p.id == plugin) {
if (action == "start") {
for(auto s : p.configurator->getSensors()) {
s->startPolling();
response = "Plugin not found!";
connection->set_status(server::connection::not_found);
for(auto& p : _httpsServer._plugins) {
if (p.id == pathStrs[0]) {
response = "Sensor not found!";
for(auto s : p.configurator->getSensors()) {
if (s->getName() == sensor) {
uint64_t avg = 0;
const reading_t * const cache = s->getCache();
unsigned size = s->getCacheSize();
unsigned count = 0;
for(unsigned i = 0; i < size; i++) {
if (cache[i].timestamp > time) {
avg += cache[i].value;
count++;
}
response = "Plugin " + plugin + ": Sensors started";
connection->set_status(server::connection::ok);
} else if (action == "stop") {
for(auto s : p.configurator->getSensors()) {
s->stopPolling();
}
response = "Plugin " + plugin + ": Sensors stopped";
connection->set_status(server::connection::ok);
}
if (count > 0)
avg /= count;
response = pathStrs[0] + "::" + sensor + " Average of last " + std::to_string(count) + " values is " + std::to_string(avg);
connection->set_status(server::connection::ok);
break;
}
}
} else {
response = "Invalid authentication key!";
connection->set_status(server::connection::unauthorized);
}
}
}
}
} else if (method == "PUT") {
} else {
response = "PUT requests should be of the form \"host:port/[plugin]/[action]?authkey=[token]\"\n"
"Where [plugin] names a plugin, [action] is either \"start\" or \"stop\" and [token] is your authentication key";
connection->set_status(server::connection::bad_request);
//first check permission
if (!_httpsServer.check_authkey(auth_value, permission::PUTReq)) {
LOG(warning) << "Provided authentication token has insufficient permissions";
connection->set_status(server::connection::unauthorized);
goto error;
}
if (pathStrs.size() < 2) {
LOG(warning) << "Received malformed request: No second path part";
connection->set_status(server::connection::bad_request);
goto error;
}
//check if query and action are valid values
if (pathStrs[1] != "start" && pathStrs[1] != "stop") {
LOG(warning) << "Unknown action " << pathStrs[1] << " requested";
connection->set_status(server::connection::not_supported);
goto error;
}
std::string action = pathStrs[1];
//process actual request
response = "Plugin not found!";
connection->set_status(server::connection::not_found);
for(auto& p : _httpsServer._plugins) {
if (p.id == pathStrs[0]) {
if (action == "start") {
for(auto s : p.configurator->getSensors()) {
s->startPolling();
}
response = "Plugin " + pathStrs[0] + ": Sensors started";
connection->set_status(server::connection::ok);
} else if (action == "stop") {
for(auto s : p.configurator->getSensors()) {
s->stopPolling();
}
response = "Plugin " + pathStrs[0] + ": Sensors stopped";
connection->set_status(server::connection::ok);
}
break;
}
LOG(info) << "HttpsServer: Responding: " << response;
data << response << std::endl;
}
}
//send response
LOG(info) << "HttpsServer: Responding: " << response;
data << response << std::endl;
//jump right here if an error was encountered.
//An empty response will be send while the connections status should be set to an appropriate error state
error:
server::response_header headers[] = { {"Connection", "close"}, {"Content-Type", "text/plain"} };
connection->set_headers(boost::make_iterator_range(headers, headers + 2));
connection->write(data.str());
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment