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

Merge branch 'rest_api'

The REST API should now be mature enough to be merged
parents c418fcfa ff9d73e2
......@@ -11,9 +11,10 @@ DISTFILES_HASHES = bacnet-stack-0.8.5.tgz|66b69111d91432fa67a7c6c1a653434d;freei
include $(DCDBCOREPATH)/common.mk
CXXFLAGS = -std=c++11 -DBOOST_DATE_TIME_POSIX_TIME_STD_CONFIG -O2 -g -Wall -Wno-unused-function -Wno-deprecated-declarations -Wno-unused-variable -DBOOST_LOG_DYN_LINK -I$(DCDBBASEPATH)/dcdb/include -I$(DCDBDEPLOYPATH)/include
LIBS = -L../deps/mosquitto_build/lib -L$(DCDBDEPLOYPATH)/lib/ -ldl -lmosquitto -lboost_system -lboost_thread -lboost_log_setup -lboost_log -lpthread -rdynamic
OBJS = src/dcdbpusher.o src/Configuration.o src/Sensor.o src/MQTTPusher.o
CXXFLAGS = -std=c++11 -DBOOST_DATE_TIME_POSIX_TIME_STD_CONFIG -DBOOST_NETWORK_ENABLE_HTTPS -O2 -g -Wall -Wno-unused-function -Wno-deprecated-declarations -Wno-unused-variable -DBOOST_LOG_DYN_LINK -I$(DCDBBASEPATH)/dcdb/include -I$(DCDBDEPLOYPATH)/include -I$(DCDBDEPSPATH)/cpp-netlib-0.12.0-final/deps/asio/asio/include
LIBS = -L../deps/mosquitto_build/lib -L$(DCDBDEPLOYPATH)/lib/ -ldl -lmosquitto -lboost_system -lboost_thread -lboost_log_setup -lboost_log -lpthread -lcrypto -lssl -lcppnetlib-server-parsers -lcppnetlib-uri -rdynamic
OBJS = src/dcdbpusher.o src/Configuration.o src/Sensor.o src/MQTTPusher.o src/HttpsServer.o
PLUGINS_BASE = libdcdbplugin_pdu libdcdbplugin_sysfs libdcdbplugin_ipmi libdcdbplugin_bacnet
ifeq ($(OS),Darwin)
......
......@@ -42,6 +42,19 @@ global {
verbosity 5
daemonize false
tempdir ./../testDir/
cacheInterval 900
}
restAPI {
address localhost:8000
certificate path/to/file
privateKey path/to/file
dhFile path/to/file
authkey token {
PUTReq
GETReq
}
}
plugins {
......@@ -59,26 +72,73 @@ plugins {
```
Explanation of the values:
| Value             | Explanation |
| Value | Explanation |
|:----- |:----------- |
| global | Wrapper structure for the global values.
|   mqttBroker | Define address and port of the MQTT-broker which collects the messages (sensor values) send by dcdbpusher.
|   mqttprefix | To not rewrite a full MQTT-topic for every sensor one can specify here a consistend prefix.
|   threads | Specify how many threads should be created to handle the sensors async. Default value of threads is 1. Note that the MQTTPusher always starts an extra thread. So the actual number of started threads is always one more than defined here. Specifying not enough threads can result in a delay for some sensors until they are read.
|   verbosity | Level of detail in the logfile (dcdb.log). Set to a value between 5 (all log-messages, default) and 0 (only fatal messages). NOTE: level of verbosity for the command-line log can be set via the -v flag independently when invoking dcdbpusher.
|   daemonize | Set to 'true' if dcdbpusher should run detached as daemon. Default is false.
|   tempdir | One can specify a writeable directory where dcdbpusher can write its temporary and logging files to. Default is the current (' ./ ' ) directory.
| mqttBroker | Define address and port of the MQTT-broker which collects the messages (sensor values) send by dcdbpusher.
| mqttprefix | To not rewrite a full MQTT-topic for every sensor one can specify here a consistent prefix.
| threads | Specify how many threads should be created to handle the sensors async. Default value of threads is 1. Note that the MQTTPusher always starts an extra thread. So the actual number of started threads is always one more than defined here. Specifying not enough threads can result in a delay for some sensors until they are read.
| verbosity | Level of detail in the logfile (dcdb.log). Set to a value between 5 (all log-messages, default) and 0 (only fatal messages). NOTE: level of verbosity for the command-line log can be set via the -v flag independently when invoking dcdbpusher.
| daemonize | Set to 'true' if dcdbpusher should run detached as daemon. Default is false.
| tempdir | One can specify a writeable directory where dcdbpusher can write its temporary and logging files to. Default is the current (' ./ ' ) directory.
| cacheInterval | Define a time interval in seconds. The last sensor readings within this time interval will be kept. This value can be overwritten by plugins.
| | |
| restAPI | Bundles all values related to the RestAPI. See the corresponding [section](#restApi) for more information on supported functionality.
| address | Define address and port where the REST API server should run on.
| certificate | Provide the (path and) file which the HTTPS server should use as certificate.
| privateKey | Provide the (path and) file which should be used as corresponding private key for the certificate. If private key and certificate are stored in the same file one should nevertheless provide the path to the cert-file here again.
| dhFile | Provide the (path and) file where Diffie-Hellman parameters for the key exchange are stored.
| authkey | This struct is used to define authentication key tokens for the REST API. Within the struct, define which operations over the REST API are allowed for the token (e.g. PUTReq or GETReq). Each token must be unique.
| | |
| plugins | In this section one can specify the plugins which should be used.
|   plugin name | The plugin name is used to build the corresponding lib-name (e.g. sysfs --> libdcdbplugin_sysfs.1.0)
|     path | Specify the path where the plugin (the shared library) is located. If left empty, dcdbpusher will look in the default lib-directories (usr/lib and friends) for the plugin-file.
|     config | One can specify a separate config-file (including path to it) for the plugin to use. If not specified, dcdbpusher will look up pluginName.conf (e.g. sysfs.conf) in the same directory where global.conf is located.
| plugin name | The plugin name is used to build the corresponding lib-name (e.g. sysfs --> libdcdbplugin_sysfs.1.0)
| path | Specify the path where the plugin (the shared library) is located. If left empty, dcdbpusher will look in the default lib-directories (usr/lib and friends) for the plugin-file.
| config | One can specify a separate config-file (including path to it) for the plugin to use. If not specified, dcdbpusher will look up pluginName.conf (e.g. sysfs.conf) in the same directory where global.conf is located.
| | |
Formats of the other sensor-specific config-files are explained in the corresponding [subsections](#IPMI). Example configuration-files can be found in the `config/` directory.
## <a name="restApi">REST API</a>
Dcdbpusher runs a HTTPS server which provides some functionality to be controlled over a RESTful API. The API is by default hosted at port 8000 on the localhost but the address can be changed in the [`global.conf`](#GC).
A HTTPS request to dcdbpusher should have the following format: `[GET|PUT] host:port[ressource]?[queries]`.
Tables with allowed ressources sorted by REST methods can be found below. A query consists of a key-value pair of the format `key=value`. For every request at least the authentication token has to be appended as query. Multiple queries are separated by semicolons(';').
### Table of queries
| Key | Value | Explanation |
|:--- |:----- |:----------- |
| authkey | Your authentication token | The authentication token is required to verify that you are allowed to make this particular request. The authkey query must be included by every request
| interval | Time-value in [s] | Only for GET request of sensor readings average. One can (optionally) specify a custom time interval for which the average of sensor readings is calculated. By default, every sensor reading in the cache is used to calculate the average.
### Ressources for GET request
| Ressource | Explanation |
|:--------- |:----------- |
| /help | Responds with a small cheatsheet which presents all currently supported ressources.
| /plugins | (Discovery) Returns a list of all currently loaded plugins.
| /[plugin]/sensors | (Discovery) Returns a list of all sensors which belong to [plugin]. To find out which plugins are available one can request the `/plugins` ressource.
| /[plugin]/[sensor]/avg | Calculates and returns the average of the last sensor readings. Can be combined with the interval query.
### Ressources for PUT request
| Ressource | Explanation |
|:--------- |:----------- |
| /[plugin]/[action] | One can request to do a action on the specified plugin. Currently supported actions are `start` and `stop` which start or stop the polling of the sensors of the plugin.
### Examples
Two examples for HTTPS requests:
* `GET https://localhost:8000/sysfs/freq1/avg?authkey=myToken;interval=15`
* `PUT https://localhost:8000/bacnet/stop?authkey=myToken`
## MQTT topic
For communication between the different DCDB-components (database, dcdbpusher) the [MQTT protocol](https://mqtt.org/) is used. In order to identify each sensor, everyone has to have a unique MQTT topic assigned. A MQTT topic for DCDB consists of exactly 128 bits (= 32 hex characters), not including '/' separators.
......@@ -93,6 +153,7 @@ Although every plugin requires different configuration parameters for its sensor
|
global { |global {
mqttprefix /00112233445566778899AABB0000 | mqttprefix /00112233445566778899AABB0000
cacheInterval 120 | cacheInterval 120
} |}
|
SensorTemplate { |SensorTemplate {
......@@ -132,25 +193,26 @@ sensors { |Units {
```
Explanation of the values:
| Value &ensp; &ensp; &ensp; &ensp; &ensp; &ensp; &ensp; &ensp; &ensp; | Explanation |
| Value | Explanation |
|:----- |:----------- |
| global | Here one can overwrite the global values defined in `global.conf`. The overwritten values are only used in the scope of the specific plugin.
| &ensp;&ensp;mqttprefix | Currently only overwriting of the mqttprefix is supported.
| global | Here one can overwrite the global values defined in `global.conf`. The overwritten values are only used in the scope of the specific plugin. Overwriting of global values is completely optional. However, even if no global values are overwritten at least the `global{}` struct should be present.
| mqttprefix | Define separate MQTT prefix for this plugin.
| cacheInterval | Overwrite global caching interval with plugin specific value.
| | |
| SensorTemplate | Define template sensors to be used later in the configuration. This feature is mainly for convenience reasons. One is not obligated to define any template sensors. However it is required to at least define an empty SensorTemplate {} structure.
| &ensp;&ensp;sensor name | Every template sensor needs a name for future reference. A template sensor can define every value (including values specific to a plugin) a normal sensor can (see below).
| sensor name | Every template sensor needs a name for future reference. A template sensor can define every value (including values specific to a plugin) a normal sensor can (see below).
| | |
| Units | Section to define different units where sensors may be aggregated to. Not every plugin makes use of this subdivision. Where it is used it the Units may be named different.
| &ensp;&ensp;sensorUnit u1 | If the unit subdivision is used, subunits need to be used and also need to be named.
| &ensp;&ensp;&ensp;&ensp;unitParams | One may be able to define special parameters specific to the subunits. They should be explained with the corresponding plugin if it makes use of subunits.
| sensorUnit u1 | If the unit subdivision is used, subunits need to be used and also need to be named.
| unitParams | One may be able to define special parameters specific to the subunits. They should be explained with the corresponding plugin if it makes use of subunits.
| | |
| &ensp;&ensp;&ensp;&ensp;sensors | Define in this section the sensors for the plugin/subunit
| &ensp;&ensp;&ensp;&ensp;&ensp;&ensp;sensor name | Every sensor needs a unique name
| &ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;default | Mention here the name of the template sensor to be used. The sensor will be initialized with the exact same values as the template sensor. This field can be left out if all required values for the sensor are defined manually.
| &ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;interval | Time in [ms] between two consecutive sensor reads. Default is 1000[ms] = 1[s]
| &ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;mqttsuffix | Suffix which is appended to the MQTT-prefix. Together with the prefix this should be a globally unique MQTT-topic for every sensor.
| &ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;minValues | Minimum number of sensor reads the sensor should gather before they are sent together to the database. Useful to reduce MQTT-overhead. Default is 1 (every sensor value is sent on his own).
| &ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;params | Every plugin requires additional configuration parameters. Those may be unique to every plugin and are explained in the corresponding subsections below.
| sensors | Define in this section the sensors for the plugin/subunit
| sensor name | Every sensor needs a unique name
| default | Mention here the name of the template sensor to be used. The sensor will be initialized with the exact same values as the template sensor. This field can be left out if all required values for the sensor are defined manually.
| interval | Time in [ms] between two consecutive sensor reads. Default is 1000[ms] = 1[s]
| mqttsuffix | Suffix which is appended to the MQTT-prefix. Together with the prefix this should be a globally unique MQTT-topic for every sensor.
| minValues | Minimum number of sensor reads the sensor should gather before they are sent together to the database. Useful to reduce MQTT-overhead. Default is 1 (every sensor value is sent on his own).
| params | Every plugin requires additional configuration parameters. Those may be unique to every plugin and are explained in the corresponding subsections below.
For an easy start one can begin with modifying the supplied example configuration files in the `/config` directory.
......@@ -209,7 +271,7 @@ hosts {
```
Explanation of the values specific for the IPMI plugin:
| Value &ensp; &ensp; &ensp; &ensp; &ensp; &ensp; &ensp; &ensp; &ensp; | Explanation |
| Value | Explanation |
|:----- |:----------- |
| sessiontimeout | Session timeout value for the IPMI-connection
| retransmissiontimeout | Retransmission timeout value for the IPMI-connection
......
......@@ -6,6 +6,7 @@ global {
timeout 1000
apdu_timeout 200
apdu_retries 1
cacheInterval 90
}
templates {
......
......@@ -5,6 +5,23 @@ global {
verbosity 5
daemonize false
tempdir .
cacheInterval 120
}
restAPI {
address localhost:8000
certificate ../deps/openssl-1.0.2l/certs/demo/ca-cert.pem
privateKey ../deps/openssl-1.0.2l/certs/demo/ca-cert.pem
dhFile ../deps/openssl-1.0.2l/crypto/dh/dh2048.pem
authkey qwertz {
PUTReq
GETReq
}
authkey yxcvbn {
GETReq
}
}
plugins {
......
global {
cacheInterval 60
sessiontimeout 500
retransmissiontimeout 200
mqttprefix /AABBAABBAABBAACCDDCCDDCC
......
......@@ -27,11 +27,17 @@ Configuration::Configuration(const std::string& cfgFilePath) :
_global.daemonize = 0;
_global.brokerHost = "";
_global.brokerPort = 1883;
_global.mqttPrefix = "";
_global.threads = 1;
_global.tempdir = "./";
_global.logLevelFile = boost::log::trivial::trace;
_global.logLevelCmd = boost::log::trivial::info;
//log levels will get inverted...
_global.logLevelFile = boost::log::trivial::fatal;
_global.logLevelCmd = boost::log::trivial::warning;
_global.pluginSettings.mqttPrefix = "";
_global.pluginSettings.tempdir = "./";
_global.pluginSettings.cacheInterval = 900000;
_global.restAPISettings.restHost = "";
_global.restAPISettings.restPort = "8000";
}
Configuration::~Configuration() {
......@@ -60,7 +66,7 @@ bool Configuration::readGlobal() {
return false;
}
//read global variables
//read global struct
BOOST_FOREACH(boost::property_tree::iptree::value_type &global, cfg.get_child("global")) {
if (boost::iequals(global.first, "mqttBroker")) {
_global.brokerHost = global.second.data();
......@@ -70,14 +76,14 @@ bool Configuration::readGlobal() {
_global.brokerHost.erase(pos);
}
} else if (boost::iequals(global.first, "mqttprefix")) {
_global.mqttPrefix = global.second.data();
if (_global.mqttPrefix[_global.mqttPrefix.length()-1] != '/') {
_global.mqttPrefix.append("/");
_global.pluginSettings.mqttPrefix = global.second.data();
if (_global.pluginSettings.mqttPrefix[_global.pluginSettings.mqttPrefix.length()-1] != '/') {
_global.pluginSettings.mqttPrefix.append("/");
}
} else if (boost::iequals(global.first, "tempdir")) {
_global.tempdir = global.second.data();
if (_global.tempdir[_global.tempdir.length()-1] != '/') {
_global.tempdir.append("/");
_global.pluginSettings.tempdir = global.second.data();
if (_global.pluginSettings.tempdir[_global.pluginSettings.tempdir.length()-1] != '/') {
_global.pluginSettings.tempdir.append("/");
}
} else if (boost::iequals(global.first, "threads")) {
_global.threads = stoi(global.second.data());
......@@ -87,6 +93,31 @@ bool Configuration::readGlobal() {
}
} else if (boost::iequals(global.first, "verbosity")) {
_global.logLevelFile = static_cast<boost::log::trivial::severity_level>(stoi(global.second.data()));
} else if (boost::iequals(global.first, "cacheInterval")) {
_global.pluginSettings.cacheInterval = stoul(global.second.data());
_global.pluginSettings.cacheInterval *= 1000;
} else {
LOG(warning) << " Value \"" << global.first << "\" not recognized. Omitting";
}
}
//read restAPI struct
BOOST_FOREACH(boost::property_tree::iptree::value_type &global, cfg.get_child("restAPI")) {
if (boost::iequals(global.first, "address")) {
_global.restAPISettings.restHost = global.second.data();
size_t pos = _global.restAPISettings.restHost.find(":");
if (pos != string::npos) {
_global.restAPISettings.restPort = _global.restAPISettings.restHost.substr(pos+1);
_global.restAPISettings.restHost.erase(pos);
}
} else if (boost::iequals(global.first, "certificate")) {
_global.restAPISettings.certificate = global.second.data();
} else if (boost::iequals(global.first, "privateKey")) {
_global.restAPISettings.privateKey = global.second.data();
} else if (boost::iequals(global.first, "dhFile")) {
_global.restAPISettings.dhFile = global.second.data();
} else if (boost::iequals(global.first, "authkey")) {
//Avoid unnecessary "Value not recognized" message
} else {
LOG(warning) << " Value \"" << global.first << "\" not recognized. Omitting";
}
......@@ -103,7 +134,7 @@ bool Configuration::read() {
try {
boost::property_tree::read_info(globalConfig, cfg);
} catch (boost::property_tree::info_parser_error& e) {
LOG(fatal) << "global.conf not found! Please make sure the config-path is correct.";
LOG(error) << "Error when reading plugins from global.conf: " << e.what();
return false;
}
......@@ -154,7 +185,7 @@ bool Configuration::read() {
LOG(info) << pluginConfig << " found";
dynLib.DL = dlopen(pluginLib.c_str(), RTLD_NOW);
if(!dynLib.DL) {
LOG(error) << "Cannot load " << plugin.second.data() << "-library: " << dlerror();
LOG(error) << "Cannot load " << dynLib.id << "-library: " << dlerror();
return false;
}
//reset errors
......@@ -164,29 +195,29 @@ bool Configuration::read() {
dynLib.create = (create_t*) dlsym(dynLib.DL, "create");
const char* dlsym_error = dlerror();
if (dlsym_error) {
LOG(error) << "Cannot load symbol create for " << plugin.second.data() << ": " << dlsym_error;
LOG(error) << "Cannot load symbol create for " << dynLib.id << ": " << dlsym_error;
return false;
}
dynLib.destroy = (destroy_t*) dlsym(dynLib.DL, "destroy");
dlsym_error = dlerror();
if (dlsym_error) {
LOG(error) << "Cannot load symbol destroy for " << plugin.second.data() << ": " << dlsym_error;
LOG(error) << "Cannot load symbol destroy for " << dynLib.id << ": " << dlsym_error;
return false;
}
dynLib.configurator = dynLib.create();
//set prefix to global prefix (may be overwritten)
dynLib.configurator->setGlobalSettings(_global);
dynLib.configurator->setGlobalSettings(_global.pluginSettings);
//read in config
if (!(dynLib.configurator->readConfig(pluginConfig))) {
LOG(error) << "Plugin \"" << plugin.second.data() << "\" could not read configuration!";
LOG(error) << "Plugin \"" << dynLib.id << "\" could not read configuration!";
return false;
}
// returning an empty vector sensor may indicate problems with the config file
if(dynLib.configurator->getSensors().size() == 0) {
LOG(warning) << "Plugin \"" << plugin.second.data() << "\" created no sensors!";
LOG(warning) << "Plugin \"" << dynLib.id << "\" created no sensors!";
}
//save only pointers to the sensors; copy-constructing an dynamically loaded object may lead to segfaults
......@@ -201,7 +232,7 @@ bool Configuration::read() {
//save dl-struct
_plugins.push_back(dynLib);
LOG(info) << "Plugin \"" << plugin.second.data() << "\" loaded!";
LOG(info) << "Plugin \"" << dynLib.id << "\" loaded!";
} else {
LOG(info) << pluginConfig << " not found. Omitting";
}
......@@ -211,6 +242,56 @@ bool Configuration::read() {
return true;
}
bool Configuration::readAuthkeys(HttpsServer& server) {
//open file
std::string globalConfig = _cfgFilePath;
globalConfig.append("global.conf");
boost::property_tree::iptree cfg;
//parse to property_tree
try {
boost::property_tree::read_info(globalConfig, cfg);
} catch (boost::property_tree::info_parser_error& e) {
LOG(error) << "Error when reading authkeys from global.conf: " << e.what();
return false;
}
//read authkeys
BOOST_FOREACH(boost::property_tree::iptree::value_type &global, cfg.get_child("restAPI")) {
if (boost::iequals(global.first, "authkey")) {
#ifdef DEBUG
LOG(info) << "Authentication token \"" << global.second.data() << "\"";
#endif
std::bitset<NUM_PERMISSIONS> permissions;
BOOST_FOREACH(boost::property_tree::iptree::value_type &perm, global.second) {
if (boost::iequals(perm.first, "GETReq")) {
#ifdef DEBUG
LOG(info) << " Permission \"GETReq\"";
#endif
permissions[GETReq] = true;
} else if (boost::iequals(perm.first, "PUTReq")) {
#ifdef DEBUG
LOG(info) << " Permission \"PUTReq\"";
#endif
permissions[PUTReq] = true;
} else {
#ifdef DEBUG
LOG(warning) << "Permission \"" << perm.first << "\" not recognized. Omitting";
#endif
}
}
if (!server.addAuthkey(authkeyMap_t::value_type(global.second.data(), permissions))) {
#ifdef DEBUG
LOG(warning) << "Authkey already present!";
#endif
}
} else {
//
}
}
return true;
}
bool Configuration::checkMqtt(const std::string& mqtt) {
//MQTT topic must have 128 bit = 16 bytes = 32 hex chars
//but can have more with some extra '/', therefore remove all '/'
......
......@@ -8,25 +8,26 @@
#ifndef CONFIGURATION_H_
#define CONFIGURATION_H_
#include <vector>
#include <set>
#include "Configurator.h"
#include "HttpsServer.h"
#include "PluginDefinitions.h"
#include "Sensor.h"
#include <boost/property_tree/ptree.hpp>
#include <boost/log/trivial.hpp>
//struct of values required for a dynamic library.
typedef struct {
std::string id;
void* DL;
Configurator* configurator;
create_t* create;
destroy_t* destroy;
} dl_t;
typedef std::vector<dl_t> pluginVector_t;
int daemonize;
int brokerPort;
std::string brokerHost;
uint32_t threads;
boost::log::trivial::severity_level logLevelFile;
boost::log::trivial::severity_level logLevelCmd;
pluginSettings_t pluginSettings;
restAPISettings_t restAPISettings;
} global_t;
typedef std::set<std::string> mqttSet_t;
/**
......@@ -52,7 +53,7 @@ public:
bool readGlobal();
/**
* Reads configuration (excpet global values) from global.conf (located at _cfgFilePath).
* Reads configuration (except global values) from global.conf (located at _cfgFilePath).
* Detects which sensor types are required and dynamically opens required libraries.
* Invokes dynamic libraries to read their configuration.
*
......@@ -60,6 +61,14 @@ public:
*/
bool read();
/**
* Reads the authentication keys and forwards them to the HttpsServer
*
* @param server The Rest API server where to add the authkeys
* @return true on success, false otherwise
*/
bool readAuthkeys(HttpsServer& server);
/**
* Read and set general sensor values (like interval, minvalues, ...).
* @param sensor The sensor to be configured
......
......@@ -14,22 +14,17 @@
#include <string>
typedef struct {
int daemonize;
int brokerPort;
std::string brokerHost;
std::string mqttPrefix;
std::string tempdir;
uint32_t threads;
boost::log::trivial::severity_level logLevelFile;
boost::log::trivial::severity_level logLevelCmd;
} global_t;
unsigned int cacheInterval;
} pluginSettings_t;
/**
* Abstract base class, which defines the interface for the configurators in the shared dynamic libraries.
*/
class Configurator {
public:
Configurator() {}
Configurator() : _cacheInterval(900000) {}
virtual ~Configurator() {}
/**
......@@ -39,8 +34,9 @@ public:
*/
virtual bool readConfig(std::string cfgPath) = 0;
virtual void setGlobalSettings(const global_t& globalSettings) {
_mqttPrefix = globalSettings.mqttPrefix;
virtual void setGlobalSettings(const pluginSettings_t& pluginSettings) {
_mqttPrefix = pluginSettings.mqttPrefix;
_cacheInterval = pluginSettings.cacheInterval;
}
std::vector<Sensor*>& getSensors() {
......@@ -49,6 +45,7 @@ public:
protected:
std::string _mqttPrefix;
unsigned int _cacheInterval;
std::vector<Sensor*> _sensors;
boost::log::sources::severity_logger<boost::log::trivial::severity_level> lg;
};
......
/*
* HttpsServer.cpp
*
* Created on: 25.05.2018
* Author: Micha Mueller
*/
#include "HttpsServer.h"
#include "timestamp.h"
#include <iostream>
#include <memory>
#include <functional>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/info_parser.hpp>
#include <boost/algorithm/string/split.hpp>
#define LOGH(sev) LOG(sev) << "HttpsServer: "
HttpsServer::requestHandler::requestHandler(HttpsServer& httpsServer) : _httpsServer(httpsServer) {}
void HttpsServer::requestHandler::operator()(server::request const &request, server::connection_ptr connection) {
//first log some info about client
server::string_type ip = source(request);
unsigned int port = request.source_port;
LOGH(info) << 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") {
LOGH(warning) << "Unsupported " << method << " request was made";
connection->set_status(server::connection::not_supported);
goto error;
}
LOGH(info) << method << " request of " << request.destination << " was made";
//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);
}
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) {
size_t pos = key.find("=");
if (pos != std::string::npos) {
std::string value;
value = key.substr(pos+1);
key.erase(pos);
queries.push_back(std::make_pair(key, value));
}
}
}
//finished string processing
//authkey is required in every case
for (auto& p : queries) {
if (p.first == "authkey") {
auth_value = p.second;
break;
}
}
if (pathStrs.size() < 1) {
LOGH(warning) << "Received malformed request: No first path part";
connection->set_status(server::connection::bad_request);
goto error;
}