Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
dcdb
dcdb
Commits
8e056d72
Commit
8e056d72
authored
Nov 09, 2018
by
Daniele Tafani
Browse files
Working HTTP server for Grafana with HTTP basic authentication support.
parent
e6873872
Changes
10
Expand all
Hide whitespace changes
Inline
Side-by-side
HTTPServer/Configuration.cpp
View file @
8e056d72
...
...
@@ -99,7 +99,7 @@ bool Configuration::readGlobal() {
_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
"
))
{
}
else
if
(
boost
::
iequals
(
global
.
first
,
"
user
"
))
{
//Avoid unnecessary "Value not recognized" message
}
else
{
LOG
(
warning
)
<<
" Value
\"
"
<<
global
.
first
<<
"
\"
not recognized. Omitting"
;
...
...
@@ -109,7 +109,8 @@ bool Configuration::readGlobal() {
return
true
;
}
bool
Configuration
::
readAuthkeys
(
HttpsServer
*
server
)
{
bool
Configuration
::
checkCredentials
(
std
::
string
credentials
)
{
//open file
std
::
string
globalConfig
=
_cfgFilePath
;
globalConfig
.
append
(
"global.conf"
);
...
...
@@ -119,44 +120,33 @@ bool Configuration::readAuthkeys(HttpsServer* server) {
try
{
boost
::
property_tree
::
read_info
(
globalConfig
,
cfg
);
}
catch
(
boost
::
property_tree
::
info_parser_error
&
e
)
{
LOG
(
error
)
<<
"Error when reading
authkey
s from global.conf: "
<<
e
.
what
();
LOG
(
error
)
<<
"Error when reading
credential
s from global.conf: "
<<
e
.
what
();
return
false
;
}
//read
authkey
s
//read
credential
s
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
(
boost
::
iequals
(
global
.
first
,
"user"
))
{
//Check user
if
(
boost
::
iequals
(
credentials
.
substr
(
0
,
credentials
.
find
(
":"
)),
global
.
second
.
data
()))
{
BOOST_FOREACH
(
boost
::
property_tree
::
iptree
::
value_type
&
pass
,
global
.
second
)
{
if
(
boost
::
iequals
(
pass
.
first
,
"password"
))
{
//Check password
if
(
boost
::
iequals
(
credentials
.
substr
(
credentials
.
find
(
":"
)
+
1
),
pass
.
second
.
data
()))
{
return
true
;
}
}
}
}
if
(
!
server
->
addAuthkey
(
authkeyMap_t
::
value_type
(
global
.
second
.
data
(),
permissions
)))
{
#ifdef DEBUG
LOG
(
warning
)
<<
"Authkey already present!"
;
#endif
}
}
else
{
//
}
}
return
tru
e
;
return
fals
e
;
}
global_t
&
Configuration
::
getGlobal
()
{
...
...
HTTPServer/Configuration.h
View file @
8e056d72
...
...
@@ -10,8 +10,12 @@
#define Configuration_h
#include
<stdio.h>
#include
<utility>
#include
<map>
#include
<bitset>
#include
<set>
#include
"
https
erver.h"
#include
"
HTTPS
erver.h"
#include
<boost/property_tree/ptree.hpp>
#include
<boost/property_tree/json_parser.hpp>
#include
<boost/log/trivial.hpp>
...
...
@@ -52,12 +56,12 @@ public:
bool
readGlobal
();
/**
*
Reads the authentication keys and forwards them to the HttpsS
erver
*
Check the credentials to connect to the Grafana s
erver
*
* @param
server
T
he
Rest API server where to add the authkeys
* @param
credentials
t
he
string username:password
* @return true on success, false otherwise
*/
bool
readAuthkeys
(
HttpsServer
*
server
);
bool
checkCredentials
(
std
::
string
credentials
);
/*
* Return global settings
...
...
HTTPServer/
https
erver.cpp
→
HTTPServer/
HTTPS
erver.cpp
View file @
8e056d72
This diff is collapsed.
Click to expand it.
HTTPServer/Server.h
→
HTTPServer/
HTTP
Server.h
View file @
8e056d72
//
// Server.h
// httpserver
// HTTPServer.h
//
// The header file of HTTPServer.cpp.
//
// Created by Tafani, Daniele on 27.09.18.
// Copyright © 2018 LRZ. All rights reserved.
...
...
@@ -11,8 +12,8 @@
#include
"Logging.h"
#include
"cassandra.h"
#ifndef Server_h
#define Server_h
#ifndef
HTTP
Server_h
#define
HTTP
Server_h
#pragma once
...
...
@@ -23,6 +24,8 @@
#include
<iostream>
#include
<fstream>
#include
<random>
#include
<locale>
#include
<ctime>
#ifdef _WIN32
#define NOMINMAX
...
...
@@ -31,6 +34,13 @@
# include <sys/time.h>
#endif
//Headers from Boost
#include
<boost/thread/condition.hpp>
#include
<boost/thread/mutex.hpp>
#include
<boost/thread/thread.hpp>
#include
<asio/ssl.hpp>
//Headers from cpprestsdk
#include
"cpprest/json.h"
#include
"cpprest/http_listener.h"
#include
"cpprest/uri.h"
...
...
@@ -40,6 +50,7 @@
#include
"cpprest/containerstream.h"
#include
"cpprest/producerconsumerstream.h"
//Headers from dcdb
#include
"dcdb/connection.h"
#include
"dcdb/timestamp.h"
#include
"dcdb/sensorid.h"
...
...
@@ -47,37 +58,140 @@
#include
"dcdb/sensorconfig.h"
#include
"dcdb/sensor.h"
#pragma warning ( push )
#pragma warning ( disable : 4457 )
#pragma warning ( pop )
#include
<locale>
#include
<ctime>
using
namespace
std
;
using
namespace
web
;
using
namespace
http
;
using
namespace
utility
;
using
namespace
http
::
experimental
::
listener
;
/**
* Struct defining the REST API Settings.
* It holds connection settings of the HTTP server (hostname and port) and SSL settings (certificate, private key, dhFile).
*/
typedef
struct
{
std
::
string
restHost
;
std
::
string
restPort
;
std
::
string
certificate
;
std
::
string
privateKey
;
std
::
string
dhFile
;
}
restAPISettings_t
;
/**
* Struct defining the Cassandra Settings.
* It holds connection settings of the Apache Cassandra server (hostname and port).
*/
typedef
struct
{
std
::
string
cassandraHost
;
uint16_t
cassandraPort
;
}
cassandraSettings_t
;
/**
* This class implements the behaviour of a request handler of the REST server.
*/
class
handler
{
public:
/**
* The default constructor of the class.
*/
handler
();
/**
* The constructor of the class with URL of the REST server.
* @param url
*/
handler
(
utility
::
string_t
url
);
/**
* The destructor of the class.
*/
virtual
~
handler
();
pplx
::
task
<
void
>
open
(){
return
m_listener
.
open
();}
pplx
::
task
<
void
>
close
(){
return
m_listener
.
close
();}
protected:
private:
/**
* This function handles HTTP GET requests.
* It is only used once to handle a Grafana request to define a data source.
*
* @param message: the HTTP message containing the GET request.
*/
void
handle_get
(
http_request
message
);
/**
* This function handles HTTP PUT requests.
* We do not use this handler in Grafana for the moment.
*
* @param message: the HTTP message containing the PUT request.
*/
void
handle_put
(
http_request
message
);
/**
* This function handles HTTP POST requests.
* It is used to handle Grafana queries (for sensor names and data).
*
* @param message: the HTTP message containing the POST request.
*/
void
handle_post
(
http_request
message
);
/**
* This function handles HTTP DELETE requests.
* Not used at the moment.
*
* @param message: the HTTP message containing the DELETE request.
*/
void
handle_delete
(
http_request
message
);
void
handle_error
(
pplx
::
task
<
void
>&
t
);
/**
* This function decodes base64 string for HTTP basic authentication.
*
* @param input: the base64 string to be decoded.
*/
std
::
string
decode
(
std
::
string
input
);
/**
* The HTTP listener of the handler that supports different HTTP requests.
*
*/
http_listener
m_listener
;
};
#endif
/* Server_h */
/*
* Provides REST API services over configurable host and port.
*/
class
HttpsServer
{
public:
HttpsServer
(
restAPISettings_t
restAPISettings
,
cassandraSettings_t
cassandraSettings
,
boost
::
asio
::
io_service
&
io
);
virtual
~
HttpsServer
();
/*
* Starts the handler of the server.
*/
void
run
()
{
_handler
->
open
().
wait
();
}
/*
* Stops the handler of the server.
*/
void
stop
()
{
_handler
->
close
().
wait
();
}
private:
std
::
unique_ptr
<
handler
>
_handler
;
boost
::
log
::
sources
::
severity_logger
<
boost
::
log
::
trivial
::
severity_level
>
lg
;
};
#endif
/* HTTPServer_h */
HTTPServer/Makefile
View file @
8e056d72
include
../config.mk
CXXFLAGS
=
-O2
-g
--std
=
c++11
-Wall
-Wno-unused-local-typedefs
-Wno-deprecated-declarations
-Wno-unknown-warning-option
-fmessage-length
=
0
-I
../include/
-I
../lib/include
-I
$(DCDBDEPLOYPATH)
/include
-DBOOST_DATE_TIME_POSIX_TIME_STD_CONFIG
-I
$(DCDBDEPSPATH)
/cpp-netlib-0.12.0-final/deps/asio/asio/include
-I
$(DCDBDEPSPATH)
/cpp-netlib-0.12.0-final
-I
$(DCDBDEPSPATH)
/cpp-netlib-0.12.0-final/deps/cxxopts/src
-DASIO_HEADER_ONLY
-DBOOST_TEST_DYN_LINK
CXXFLAGS
=
-O2
-g
--std
=
c++11
-Wall
-Wno-unused-local-typedefs
-Wno-deprecated-declarations
-Wno-unknown-warning-option
-fmessage-length
=
0
-I
../include/
-I
../lib/include
-I
$(DCDBDEPLOYPATH)
/include
-DBOOST_DATE_TIME_POSIX_TIME_STD_CONFIG
-DBOOST_LOG_DYN_LINK
-I
$(DCDBDEPSPATH)
/cpp-netlib-0.12.0-final/deps/asio/asio/include
-DBOOST_TEST_DYN_LINK
OBJS
=
httpserver.o
\
Configuration.o
\
Logging.o
OBJS
=
HTTPServer.o
\
Configuration.o
LIBS
=
-L
$(DCDBDEPLOYPATH)
/lib/
-L
../lib
-ldcdb
-lcassandra
-lboost_system
-lboost_thread
-lboost_log
_setup
-lboost_log
-lpthread
-lcrypto
-lssl
-lcpp
netlib-server-parsers
-lcppnetlib-uri
TARGET
=
httpserver
LIBS
=
-L
$(DCDBDEPLOYPATH)
/lib/
-L
../lib
-ldcdb
-lcassandra
-lboost_system
-lboost_chrono
-lboost_thread
-lboost_log
-lboost_log
_setup
-lpthread
-lcrypto
-lssl
-lcpp
rest
TARGET
=
httpserver
.PHONY
:
clean install
P
=
$(
shell
cd
$(DCDBDEPLOYPATH)
/lib/
&&
pwd
)
U
=
$(
shell
uname
)
P
=
$(
shell
cd
$(DCDBDEPLOYPATH)
/lib/
&&
pwd
)
U
=
$(
shell
uname
)
$(TARGET)
:
$(OBJS)
@
LD_LIBRARY_PATH
=
$(DCDBDEPLOYPATH)
/lib:
$$
LD_LIBRARY_PATH
\
...
...
HTTPServer/Server.cpp
deleted
100644 → 0
View file @
e6873872
//
// Server.cpp
// httpserver
//
// Created by Tafani, Daniele on 27.09.18.
// Copyright © 2018 LRZ. All rights reserved.
//
#include
"Server.h"
#include
<iostream>
using
namespace
std
;
using
namespace
web
;
using
namespace
http
;
using
namespace
utility
;
using
namespace
http
::
experimental
::
listener
;
std
::
unique_ptr
<
handler
>
g_httpHandler
;
Configuration
*
_configuration
;
HttpsServer
*
_httpsServer
;
DCDB
::
Connection
*
cassandraConnection
;
handler
::
handler
()
{
//ctor
}
handler
::
handler
(
utility
::
string_t
url
)
:
m_listener
(
url
)
{
m_listener
.
support
(
methods
::
GET
,
std
::
bind
(
&
handler
::
handle_get
,
this
,
std
::
placeholders
::
_1
));
m_listener
.
support
(
methods
::
PUT
,
std
::
bind
(
&
handler
::
handle_put
,
this
,
std
::
placeholders
::
_1
));
m_listener
.
support
(
methods
::
POST
,
std
::
bind
(
&
handler
::
handle_post
,
this
,
std
::
placeholders
::
_1
));
m_listener
.
support
(
methods
::
DEL
,
std
::
bind
(
&
handler
::
handle_delete
,
this
,
std
::
placeholders
::
_1
));
}
handler
::~
handler
()
{
//dtor
}
void
handler
::
handle_error
(
pplx
::
task
<
void
>&
t
)
{
try
{
t
.
get
();
}
catch
(...)
{
// Ignore the error, Log it if a logger is available
}
}
//
// Get Request
// Primarily used for
//
void
handler
::
handle_get
(
http_request
message
)
{
ucout
<<
message
.
to_string
()
<<
endl
;
auto
paths
=
http
::
uri
::
split_path
(
http
::
uri
::
decode
(
message
.
relative_uri
().
path
()));
message
.
relative_uri
().
path
();
//Dbms* d = new Dbms();
//d->connect();
concurrency
::
streams
::
fstream
::
open_istream
(
U
(
"static/index.html"
),
std
::
ios
::
in
).
then
([
=
](
concurrency
::
streams
::
istream
is
)
{
message
.
reply
(
status_codes
::
OK
,
is
,
U
(
"text/html"
)).
then
([](
pplx
::
task
<
void
>
t
)
{
try
{
t
.
get
();
}
catch
(...){
//
}
});
}).
then
([
=
](
pplx
::
task
<
void
>
t
)
{
try
{
t
.
get
();
}
catch
(...){
message
.
reply
(
status_codes
::
InternalError
,
U
(
"INTERNAL ERROR "
));
}
});
return
;
};
//
// A POST request
//
void
handler
::
handle_post
(
http_request
message
)
{
ucout
<<
message
.
to_string
()
<<
endl
;
std
::
string
response
;
http_response
resp
;
if
(
message
.
relative_uri
().
to_string
()
==
"/search"
)
{
DCDB
::
SensorConfig
sensorConfig
(
cassandraConnection
);
std
::
list
<
std
::
string
>
publicSensors
;
sensorConfig
.
getPublicSensorNames
(
publicSensors
);
response
=
"["
;
for
(
std
::
list
<
std
::
string
>::
iterator
it
=
publicSensors
.
begin
();
it
!=
publicSensors
.
end
();
it
++
)
{
response
=
response
+
"
\"
"
+
(
*
it
)
+
"
\"
,"
;
}
response
.
pop_back
();
response
=
response
+
"]"
;
message
.
reply
(
status_codes
::
OK
,
response
);
}
else
if
(
message
.
relative_uri
().
to_string
()
==
"/query"
)
{
//extract JSON from body
pplx
::
task
<
json
::
value
>
task
=
message
.
extract_json
(
true
);
const
json
::
value
&
jsonValue
=
task
.
get
();
//get start time as string
std
::
string
startStr
=
jsonValue
.
at
(
"range"
).
at
(
"from"
).
as_string
();
startStr
[
startStr
.
find
(
"T"
)]
=
' '
;
startStr
.
pop_back
();
//get end time as string
std
::
string
endStr
=
jsonValue
.
at
(
"range"
).
at
(
"to"
).
as_string
();
endStr
[
endStr
.
find
(
"T"
)]
=
' '
;
endStr
.
pop_back
();
//get sensors
std
::
list
<
std
::
string
>
sensors
;
for
(
int
i
=
0
;
i
<
jsonValue
.
at
(
"targets"
).
size
();
i
++
)
sensors
.
push_back
(
jsonValue
.
at
(
"targets"
).
at
(
i
).
at
(
"target"
).
as_string
());
//Prepare query
DCDB
::
TimeStamp
start
,
end
;
start
=
DCDB
::
TimeStamp
(
startStr
,
false
);
end
=
DCDB
::
TimeStamp
(
endStr
,
false
);
DCDB
::
SensorConfig
sensorConfig
(
cassandraConnection
);
json
::
value
output
;
uint64_t
targetIdx
=
0
;
for
(
std
::
list
<
std
::
string
>::
iterator
it
=
sensors
.
begin
();
it
!=
sensors
.
end
();
it
++
)
{
DCDB
::
Sensor
sensor
(
cassandraConnection
,
*
it
);
//Shoot!
std
::
list
<
DCDB
::
SensorDataStoreReading
>
results
;
sensor
.
query
(
results
,
start
,
end
,
DCDB
::
AGGREGATE_NONE
);
//Format response for Grafana
output
[
targetIdx
][
"target"
]
=
json
::
value
::
string
(
U
(
*
it
))
;
uint64_t
dataIdx
=
0
;
for
(
std
::
list
<
DCDB
::
SensorDataStoreReading
>::
iterator
reading
=
results
.
begin
();
reading
!=
results
.
end
();
reading
++
)
{
std
::
vector
<
json
::
value
>
elements
;
elements
.
push_back
(
boost
::
lexical_cast
<
double
>
((
*
reading
).
value
)
/
3600000
);
//just to get Wh as a momentary example
elements
.
push_back
((
*
reading
).
timeStamp
.
getRaw
()
/
1000000
);
//The Grafana JSON data source plugin accepts only ms for timestamps
output
[
targetIdx
][
"datapoints"
][
dataIdx
]
=
json
::
value
::
array
(
elements
);
dataIdx
++
;
}
targetIdx
++
;
ucout
<<
output
.
serialize
()
<<
endl
;
}
resp
.
set_body
(
output
);
resp
.
set_status_code
(
status_codes
::
OK
);
message
.
reply
(
resp
);
}
return
;
};
//
// A DELETE request
//
void
handler
::
handle_delete
(
http_request
message
)
{
ucout
<<
message
.
to_string
()
<<
endl
;
string
rep
=
U
(
"WRITE YOUR OWN DELETE OPERATION"
);
message
.
reply
(
status_codes
::
OK
,
rep
);
return
;
};
//
// A PUT request
//
void
handler
::
handle_put
(
http_request
message
)
{
ucout
<<
message
.
to_string
()
<<
endl
;
string
rep
=
U
(
"WRITE YOUR OWN PUT OPERATION"
);
message
.
reply
(
status_codes
::
OK
,
rep
);
return
;
};
void
on_initialize
(
const
string_t
&
address
)
{
uri_builder
uri
(
address
);
auto
addr
=
uri
.
to_uri
().
to_string
();
g_httpHandler
=
std
::
unique_ptr
<
handler
>
(
new
handler
(
addr
));
g_httpHandler
->
open
().
wait
();
ucout
<<
utility
::
string_t
(
U
(
"Listening for requests at: "
))
<<
addr
<<
std
::
endl
;
return
;
}
void
on_shutdown
()
{
g_httpHandler
->
close
().
wait
();
return
;
}
#ifdef _WIN32
int
wmain
(
int
argc
,
wchar_t
*
argv
[])
#else
int
main
(
int
argc
,
char
*
argv
[])
#endif
{
_configuration
=
new
Configuration
(
argv
[
argc
-
1
]);
//Read global variables from config file
if
(
!
_configuration
->
readGlobal
())
{
//LOG(fatal) << "Failed to read global configuration!";
std
::
cerr
<<
"Failed to read global configuration!"
;
return
1
;
}
global_t
&
globalSettings
=
_configuration
->
getGlobal
();
cassandraSettings_t
&
cassandraSettings
=
globalSettings
.
cassandraSettings
;
restAPISettings_t
&
restAPISettings
=
globalSettings
.
restAPISettings
;
/* Connect to the cassandra server */
cassandraConnection
=
new
DCDB
::
Connection
();
cassandraConnection
->
setHostname
(
cassandraSettings
.
cassandraHost
);
cassandraConnection
->
setPort
(
cassandraSettings
.
cassandraPort
);
if
(
!
cassandraConnection
->
connect
())
std
::
cerr
<<
"Cannot connect to Cassandra database."
<<
std
::
endl
;
utility
::
string_t
port
=
U
(
restAPISettings
.
restPort
);
// if(argc == 2)
// {
// port = argv[1];
// }
utility
::
string_t
address
=
U
(
"http://"
+
restAPISettings
.
restHost
+
":"
);
address
.
append
(
port
);
on_initialize
(
address
);
std
::
cout
<<
"Press ENTER to exit."
<<
std
::
endl
;
std
::
string
line
;
std
::
getline
(
std
::
cin
,
line
);
on_shutdown
();
return
0
;
}
HTTPServer/global.conf
View file @
8e056d72
...
...
@@ -15,12 +15,12 @@ restAPI {
privateKey
/
Users
/
di34bap
/
Projects
/
DCDB
/
deps
/
openssl
-
1
.
0
.
2
l
/
certs
/
demo
/
ca
-
cert
.
pem
dhFile
/
Users
/
di34bap
/
Projects
/
DCDB
/
deps
/
openssl
-
1
.
0
.
2
l
/
crypto
/
dh
/
dh2048
.
pem
authkey
qwertz
{
PUTReq
GETReq
user
user1
{
password
pass1