diff --git a/CMakeLists.txt b/CMakeLists.txt
index d4e6c35a31a5615fd037d2fa899c98fdbf564709..e50c24502162da1734ccc05caa5da3eae86716a7 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -29,6 +29,11 @@ set (HIDRA2_CXX_COMMON_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/common/cpp/includ
 
 find_package (Threads)
 
+set (CMAKE_PREFIX_PATH "${LIBCURL_DIR}")
+find_package (CURL REQUIRED)
+message (STATUS "Found cURL libraries: ${CURL_LIBRARIES}")
+message (STATUS "cURL include: ${CURL_INCLUDE_DIRS}")
+
 
 # format sources
 include(astyle)
diff --git a/broker/src/hidra2_broker/database/database_test.go b/broker/src/hidra2_broker/database/database_test.go
index 207b40683c2a6575b546a468a4b698d6f7071d9a..3073ed61b1a621ae47e3a41903e75f2a85836255 100644
--- a/broker/src/hidra2_broker/database/database_test.go
+++ b/broker/src/hidra2_broker/database/database_test.go
@@ -12,7 +12,10 @@ func TestMockDataBase(t *testing.T) {
 	db.On("Close").Return()
 	db.On("Copy").Return(nil)
 	db.On("GetNextRecord", "").Return([]byte(""), nil)
-
 	db.Connect("")
 	db.GetNextRecord("")
+	db.Close()
+	db.Copy()
+	var err DBError
+	err.Error()
 }
diff --git a/broker/src/hidra2_broker/database/mock_database.go b/broker/src/hidra2_broker/database/mock_database.go
index fbeb4582c4e4304979a1d05f7ee70b7dd2a04ce8..cf25a1eea71a73fa8973027b8a70cfc574643fda 100644
--- a/broker/src/hidra2_broker/database/mock_database.go
+++ b/broker/src/hidra2_broker/database/mock_database.go
@@ -19,7 +19,7 @@ func (db *MockedDatabase) Close() {
 	db.Called()
 }
 
-func (db *MockedDatabase) Copy() Agent{
+func (db *MockedDatabase) Copy() Agent {
 	db.Called()
 	return db
 }
diff --git a/broker/src/hidra2_broker/server/get_next.go b/broker/src/hidra2_broker/server/get_next.go
index ae29d355f5d7f1181d867b32cc811df9df7910cd..672a398987e0a85d40f93d98537beb0e68d77263 100644
--- a/broker/src/hidra2_broker/server/get_next.go
+++ b/broker/src/hidra2_broker/server/get_next.go
@@ -15,8 +15,8 @@ func extractRequestParameters(r *http.Request) (string, bool) {
 
 func routeGetNext(w http.ResponseWriter, r *http.Request) {
 	r.Header.Set("Content-type", "application/json")
-   // w.Write([]byte("Hello"))
-   // return
+	// w.Write([]byte("Hello"))
+	// return
 	db_name, ok := extractRequestParameters(r)
 	if !ok {
 		w.WriteHeader(http.StatusBadRequest)
@@ -31,6 +31,7 @@ func routeGetNext(w http.ResponseWriter, r *http.Request) {
 func getNextRecord(db_name string) (answer []byte, code int) {
 	db_new := db.Copy()
 	defer db_new.Close()
+	statistics.IncreaseCounter()
 	answer, err := db_new.GetNextRecord(db_name)
 	if err != nil {
 		err_db, ok := err.(*database.DBError)
diff --git a/broker/src/hidra2_broker/server/get_next_test.go b/broker/src/hidra2_broker/server/get_next_test.go
index 11385f121b5412533e9d30ff6c87357ca211fe07..36f5cad439a721ed9f94d552752f3605b6d876be 100644
--- a/broker/src/hidra2_broker/server/get_next_test.go
+++ b/broker/src/hidra2_broker/server/get_next_test.go
@@ -3,6 +3,7 @@ package server
 import (
 	"errors"
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/suite"
 	"hidra2_broker/database"
 	"hidra2_broker/utils"
 	"net/http"
@@ -30,45 +31,57 @@ func TestGetNextWithoutDatabaseName(t *testing.T) {
 	assert.Equal(t, http.StatusNotFound, w.Code, "no database name")
 }
 
-func ExpectCopyClose(mock_db *database.MockedDatabase){
+func ExpectCopyClose(mock_db *database.MockedDatabase) {
 	mock_db.On("Copy").Return(mock_db)
 	mock_db.On("Close").Return()
 }
 
-func TestGetNextWithWrongDatabaseName(t *testing.T) {
-	mock_db := new(database.MockedDatabase)
-	db = mock_db
-	defer func() { db = nil }()
-	ExpectCopyClose(mock_db)
-	mock_db.On("GetNextRecord", "foo").Return([]byte(""),
+type GetNextTestSuite struct {
+	suite.Suite
+	mock_db *database.MockedDatabase
+}
+
+func (suite *GetNextTestSuite) SetupTest() {
+	statistics.Reset()
+	suite.mock_db = new(database.MockedDatabase)
+	db = suite.mock_db
+	ExpectCopyClose(suite.mock_db)
+}
+
+func (suite *GetNextTestSuite) TearDownTest() {
+	assertExpectations(suite.T(), suite.mock_db)
+	db = nil
+}
+
+func TestGetNextTestSuite(t *testing.T) {
+	suite.Run(t, new(GetNextTestSuite))
+}
+
+func (suite *GetNextTestSuite) TestGetNextWithWrongDatabaseName() {
+	suite.mock_db.On("GetNextRecord", "foo").Return([]byte(""),
 		&database.DBError{utils.StatusWrongInput, ""})
 
 	w := doRequest("/database/foo/next")
-	assert.Equal(t, http.StatusBadRequest, w.Code, "wrong database name")
-	assertExpectations(t, mock_db)
+	suite.Equal(http.StatusBadRequest, w.Code, "wrong database name")
 }
 
-func TestGetNextWithInternalDBError(t *testing.T) {
-	mock_db := new(database.MockedDatabase)
-	db = mock_db
-	defer func() { db = nil }()
-	ExpectCopyClose(mock_db)
-	mock_db.On("GetNextRecord", "foo").Return([]byte(""), errors.New(""))
-
+func (suite *GetNextTestSuite) TestGetNextWithInternalDBError() {
+	suite.mock_db.On("GetNextRecord", "foo").Return([]byte(""), errors.New(""))
 	w := doRequest("/database/foo/next")
-	assert.Equal(t, http.StatusInternalServerError, w.Code, "internal error")
-	assertExpectations(t, mock_db)
+	suite.Equal(http.StatusInternalServerError, w.Code, "internal error")
 }
 
-func TestGetNextWithGoodDatabaseName(t *testing.T) {
-	mock_db := new(database.MockedDatabase)
-	db = mock_db
-	defer func() { db = nil }()
-	ExpectCopyClose(mock_db)
-	mock_db.On("GetNextRecord", "dbname").Return([]byte("Hello"), nil)
+func (suite *GetNextTestSuite) TestGetNextWithGoodDatabaseName() {
+	suite.mock_db.On("GetNextRecord", "dbname").Return([]byte("Hello"), nil)
 
 	w := doRequest("/database/dbname/next")
-	assert.Equal(t, http.StatusOK, w.Code, "GetNext OK")
-	assert.Equal(t, "Hello", string(w.Body.Bytes()), "GetNext sends data")
-	assertExpectations(t, mock_db)
+	suite.Equal(http.StatusOK, w.Code, "GetNext OK")
+	suite.Equal("Hello", string(w.Body.Bytes()), "GetNext sends data")
+}
+
+func (suite *GetNextTestSuite) TestGetNextAddsCounter() {
+	suite.mock_db.On("GetNextRecord", "dbname").Return([]byte("Hello"), nil)
+
+	doRequest("/database/dbname/next")
+	suite.Equal(1, statistics.GetCounter(), "GetNext increases counter")
 }
diff --git a/broker/src/hidra2_broker/server/server.go b/broker/src/hidra2_broker/server/server.go
index 7e85cbca759de0a7b43a76bd1c25af1aa12dd1d6..eadd2ba6b3cc0b202c92f3901ecec985f0d7ceb8 100644
--- a/broker/src/hidra2_broker/server/server.go
+++ b/broker/src/hidra2_broker/server/server.go
@@ -7,15 +7,18 @@ import (
 var db database.Agent
 
 type serverSettings struct {
-	DbAddress string
-	Port      int
+	BrokerDbAddress string
+	MonitorDbAddress string
+	MonitorDbName string
+	Port            int
 }
 
 var settings serverSettings
+var statistics serverStatistics
 
 func InitDB(dbAgent database.Agent) error {
 	db = dbAgent
-	return db.Connect(settings.DbAddress)
+	return db.Connect(settings.BrokerDbAddress)
 }
 
 func CleanupDB() {
diff --git a/broker/src/hidra2_broker/server/server_nottested.go b/broker/src/hidra2_broker/server/server_nottested.go
index cf6bd72f72fff462af1860df2d8bcec5587ed83f..35ed53f930526c6be8c30f32204db8a8cc0d23ea 100644
--- a/broker/src/hidra2_broker/server/server_nottested.go
+++ b/broker/src/hidra2_broker/server/server_nottested.go
@@ -3,17 +3,45 @@
 package server
 
 import (
+	"errors"
 	"hidra2_broker/utils"
 	"log"
 	"net/http"
 	"strconv"
 )
 
+func StartStatistics() {
+	statistics.Writer = new(StatisticInfluxDbWriter)
+	statistics.Reset()
+	go statistics.Monitor()
+}
+
 func Start() {
+	StartStatistics()
 	mux := utils.NewRouter(listRoutes)
-	log.Fatal(http.ListenAndServe("localhost:"+strconv.Itoa(settings.Port), http.HandlerFunc(mux.ServeHTTP)))
+	log.Fatal(http.ListenAndServe(":"+strconv.Itoa(settings.Port), http.HandlerFunc(mux.ServeHTTP)))
 }
 
 func ReadConfig(fname string) error {
-	return utils.ReadJsonFromFile(fname, &settings)
+	if err := utils.ReadJsonFromFile(fname, &settings); err != nil {
+		return err
+	}
+
+	if settings.BrokerDbAddress == "" {
+		return errors.New("BrokerDbAddress not set")
+	}
+
+	if settings.MonitorDbAddress == "" {
+		return errors.New("MonitorDbAddress not set")
+	}
+
+	if settings.Port == 0 {
+		return errors.New("Server port not set")
+	}
+
+	if settings.MonitorDbName == "" {
+		return errors.New("MonitorDbName not set")
+	}
+
+	return nil
 }
diff --git a/broker/src/hidra2_broker/server/server_test.go b/broker/src/hidra2_broker/server/server_test.go
index 9d6d3d73074f68c138b54fe02b3d3339b9b41363..baba3ed60dbd538d5b1fa85b01c2490fa1d62e13 100644
--- a/broker/src/hidra2_broker/server/server_test.go
+++ b/broker/src/hidra2_broker/server/server_test.go
@@ -34,7 +34,7 @@ func TestInitDBWithWrongAddress(t *testing.T) {
 
 	mock_db.ExpectedCalls = nil
 
-	settings.DbAddress = "0.0.0.0:0000"
+	settings.BrokerDbAddress = "0.0.0.0:0000"
 
 	for _, test := range initDBTests {
 		mock_db.On("Connect", "0.0.0.0:0000").Return(test.answer)
diff --git a/broker/src/hidra2_broker/server/statistics.go b/broker/src/hidra2_broker/server/statistics.go
new file mode 100644
index 0000000000000000000000000000000000000000..f0851ae33f58070035d51fc3588f99843718043d
--- /dev/null
+++ b/broker/src/hidra2_broker/server/statistics.go
@@ -0,0 +1,55 @@
+package server
+
+import (
+	"fmt"
+	"log"
+	"time"
+	"sync"
+)
+
+type statisticsWriter interface {
+	Write(*serverStatistics) error
+}
+
+type serverStatistics struct {
+	counter int
+	mux sync.Mutex
+	Writer  statisticsWriter
+}
+
+func (st *serverStatistics) IncreaseCounter() {
+	st.mux.Lock()
+	defer st.mux.Unlock()
+	st.counter++
+}
+
+func (st *serverStatistics) GetCounter() int {
+	st.mux.Lock()
+	defer st.mux.Unlock()
+	return st.counter
+}
+
+func (st *serverStatistics) Reset() {
+	st.mux.Lock()
+	defer st.mux.Unlock()
+	st.counter = 0
+}
+
+func (st *serverStatistics) WriteStatistic() (err error) {
+	defer func() {
+		if p := recover(); p != nil {
+			err = fmt.Errorf("WriteStatistic error: %v", p)
+		}
+	}()
+	return st.Writer.Write(st)
+}
+
+func (st *serverStatistics) Monitor() {
+	for {
+		time.Sleep(1000 * time.Millisecond)
+		if err := st.WriteStatistic(); err != nil {
+			log.Println(err.Error())
+		}
+		st.Reset()
+	}
+}
diff --git a/broker/src/hidra2_broker/server/statistics_test.go b/broker/src/hidra2_broker/server/statistics_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..a16a8be4a10fccdfb1b60ce8b81c6b8e1dc97d85
--- /dev/null
+++ b/broker/src/hidra2_broker/server/statistics_test.go
@@ -0,0 +1,41 @@
+package server
+
+import (
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/mock"
+	"testing"
+)
+
+type mockWriter struct {
+	mock.Mock
+}
+
+func (writer *mockWriter) Write(statistics *serverStatistics) error {
+	args := writer.Called(statistics)
+	return args.Error(0)
+}
+
+func assertMockWriterExpectations(t *testing.T, mock_writer *mockWriter) {
+	mock_writer.AssertExpectations(t)
+	mock_writer.ExpectedCalls = nil
+}
+
+func TestMonitorOK(t *testing.T) {
+	mock_writer := new(mockWriter)
+	statistics.Writer = mock_writer
+	statistics.Reset()
+	statistics.IncreaseCounter()
+
+	mock_writer.On("Write", &statistics).Return(nil)
+
+	err := statistics.WriteStatistic()
+	assert.Nil(t, err, "Statistics written")
+
+	assertMockWriterExpectations(t, mock_writer)
+}
+
+func TestMonitorCatchesError(t *testing.T) {
+	statistics.Writer = nil
+	err := statistics.WriteStatistic()
+	assert.NotNil(t, err, "Error with nil pointer")
+}
diff --git a/broker/src/hidra2_broker/server/statistics_writers.go b/broker/src/hidra2_broker/server/statistics_writers.go
new file mode 100644
index 0000000000000000000000000000000000000000..db6e46fb293895c77fdfe9599d635f8f4f52ace9
--- /dev/null
+++ b/broker/src/hidra2_broker/server/statistics_writers.go
@@ -0,0 +1,46 @@
+//+build !test
+
+package server
+
+import (
+	"github.com/influxdata/influxdb/client/v2"
+	"log"
+	"time"
+)
+
+type StatisticLogWriter struct {
+}
+
+func (writer *StatisticLogWriter) Write(statistics *serverStatistics) error {
+	log.Println(statistics.GetCounter())
+	return nil
+}
+
+type StatisticInfluxDbWriter struct {
+}
+
+func (writer *StatisticInfluxDbWriter) Write(statistics *serverStatistics) error {
+	c, err := client.NewHTTPClient(client.HTTPConfig{
+		Addr: "http://"+ settings.MonitorDbAddress,
+	})
+	if err != nil {
+		return err
+	}
+	defer c.Close()
+
+	bp, _ := client.NewBatchPoints(client.BatchPointsConfig{
+		Database:  settings.MonitorDbName,
+	})
+
+	tags := map[string]string{"Group ID": "0"}
+	fields := map[string]interface{}{
+		"rate": statistics.GetCounter(),
+	}
+	pt, err := client.NewPoint("RequestsRate", tags, fields, time.Now())
+	if err != nil {
+		return err
+	}
+	bp.AddPoint(pt)
+
+	return c.Write(bp)
+}
diff --git a/broker/src/hidra2_broker/utils/helpers.go b/broker/src/hidra2_broker/utils/helpers.go
index 02bb46796c257c6f8f784e25d8a47ee202e84e57..f1d0da4dc985d0231cbe34602f1942ce01fde362 100644
--- a/broker/src/hidra2_broker/utils/helpers.go
+++ b/broker/src/hidra2_broker/utils/helpers.go
@@ -33,5 +33,6 @@ func ReadJsonFromFile(fname string, config interface{}) error {
 	if err != nil {
 		return err
 	}
+
 	return nil
 }
diff --git a/common/cpp/CMakeLists.txt b/common/cpp/CMakeLists.txt
index 2e79b345e35a1a91c74e90320c463f8f66b3800d..981bb1f4484a0033056be6f49ccdde39cf9796e1 100644
--- a/common/cpp/CMakeLists.txt
+++ b/common/cpp/CMakeLists.txt
@@ -6,6 +6,8 @@ add_subdirectory(src/json_parser)
 
 add_subdirectory(src/data_structs)
 
+add_subdirectory(src/http_client)
+
 if(BUILD_MONGODB_CLIENTLIB)
     add_subdirectory(src/database)
 endif()
diff --git a/worker/api/cpp/src/curl_http_client.h b/common/cpp/include/http_client/curl_http_client.h
similarity index 61%
rename from worker/api/cpp/src/curl_http_client.h
rename to common/cpp/include/http_client/curl_http_client.h
index 135a6cd5c5b38af9e3d54421538a2197ee9b0889..9b65491aac43110c1e4de6bed792c9bf77ba7ab6 100644
--- a/worker/api/cpp/src/curl_http_client.h
+++ b/common/cpp/include/http_client/curl_http_client.h
@@ -13,8 +13,12 @@ class CurlHttpClient final : public HttpClient {
   public:
     CurlHttpClient();
     std::string Get(const std::string& uri, HttpCode* response_code, Error* err) const noexcept override;
+    std::string Post(const std::string& uri, const std::string& data, HttpCode* response_code,
+                     Error* err) const noexcept override;
     virtual ~CurlHttpClient();
   private:
+    std::string Command(bool post, const std::string& uri, const std::string& data, HttpCode* response_code,
+                        Error* err) const noexcept;
     mutable std::mutex mutex_;
     CURL* curl_ = 0;
 };
diff --git a/worker/api/cpp/src/http_client.h b/common/cpp/include/http_client/http_client.h
similarity index 92%
rename from worker/api/cpp/src/http_client.h
rename to common/cpp/include/http_client/http_client.h
index 31b838a62924f70fe83aecd5e70b4654421671aa..92a9299617bc6bea7b2979bc5d04865a44fc35bc 100644
--- a/worker/api/cpp/src/http_client.h
+++ b/common/cpp/include/http_client/http_client.h
@@ -1,7 +1,7 @@
 #ifndef HIDRA2_HTTP_CLIENT_H
 #define HIDRA2_HTTP_CLIENT_H
 
-#include <hidra2_worker.h>
+#include "common/error.h"
 
 namespace hidra2 {
 
@@ -10,12 +10,12 @@ enum class HttpCode;
 class HttpClient {
   public:
     virtual std::string Get(const std::string& uri, HttpCode* response_code, Error* err) const noexcept = 0;
+    virtual std::string Post(const std::string& uri, const std::string& data, HttpCode* response_code,
+                             Error* err) const noexcept = 0;
     virtual ~HttpClient() = default;
 
 };
 
-Error HttpCodeToWorkerError(const HttpCode& code);
-
 enum class HttpCode : int {
     Continue           = 100,
     SwitchingProtocols = 101,
diff --git a/worker/api/cpp/src/http_error.h b/common/cpp/include/http_client/http_error.h
similarity index 100%
rename from worker/api/cpp/src/http_error.h
rename to common/cpp/include/http_client/http_error.h
diff --git a/common/cpp/include/json_parser/json_parser.h b/common/cpp/include/json_parser/json_parser.h
index 7373e91a74bf5043487846b675be1ce29fc62363..231df49fd713664d6fd306195ff78641ed269fea 100644
--- a/common/cpp/include/json_parser/json_parser.h
+++ b/common/cpp/include/json_parser/json_parser.h
@@ -7,6 +7,7 @@
 #include <string>
 
 #include "common/error.h"
+#include "io/io.h"
 
 namespace hidra2 {
 
@@ -14,20 +15,37 @@ class RapidJson;
 
 class JsonParser {
   public:
-    JsonParser(const std::string& json, bool read_from_file);
-    ~JsonParser();
-    JsonParser(JsonParser&& other);
     Error GetUInt64(const std::string& name, uint64_t* val) const noexcept;
+    Error GetBool(const std::string& name, bool* val) const noexcept;
     Error GetString(const std::string& name, std::string* val) const noexcept;
     Error GetArrayUInt64(const std::string& name, std::vector<uint64_t>* val) const noexcept;
     Error GetArrayString(const std::string& name, std::vector<std::string>* val) const noexcept;
     JsonParser Embedded(const std::string& name) const noexcept;
+    ~JsonParser();
+  protected:
+    JsonParser(const std::string& json, const std::unique_ptr<IO>* io); // nullptr as second parameter will use default IO
+    JsonParser(const std::string& json);
+    JsonParser(JsonParser&& other);
   private:
+    std::unique_ptr<IO> default_io_;
     std::unique_ptr<RapidJson>  rapid_json_;
     JsonParser(RapidJson* rapid_json_);
 
 };
 
+
+class JsonStringParser : public JsonParser {
+  public:
+    JsonStringParser(const std::string& json): JsonParser(json) {};
+};
+
+
+class JsonFileParser : public JsonParser {
+  public:
+    JsonFileParser(const std::string& json, const std::unique_ptr<IO>* io = nullptr): JsonParser(json, io) {};
+};
+
+
 }
 
 
diff --git a/common/cpp/include/unittests/MockHttpClient.h b/common/cpp/include/unittests/MockHttpClient.h
new file mode 100644
index 0000000000000000000000000000000000000000..a497c396e4a03ab2b52f26a39f701dce2c14c9fd
--- /dev/null
+++ b/common/cpp/include/unittests/MockHttpClient.h
@@ -0,0 +1,35 @@
+#ifndef HIDRA2_MOCKHTTPCLIENT_H
+#define HIDRA2_MOCKHTTPCLIENT_H
+
+#include <gtest/gtest.h>
+#include <gmock/gmock.h>
+
+#include "http_client/http_client.h"
+
+namespace hidra2 {
+
+class MockHttpClient : public HttpClient {
+  public:
+    std::string Get(const std::string& uri, HttpCode* code, Error* err) const noexcept override {
+        ErrorInterface* error = nullptr;
+        auto responce = Get_t(uri, code, &error);
+        err->reset(error);
+        return responce;
+    }
+    std::string Post(const std::string& uri, const std::string& data, HttpCode* code, Error* err) const noexcept override {
+        ErrorInterface* error = nullptr;
+        auto responce = Post_t(uri, data, code, &error);
+        err->reset(error);
+        return responce;
+    }
+    MOCK_CONST_METHOD3(Get_t,
+                       std::string(const std::string& uri, HttpCode* code, ErrorInterface** err));
+    MOCK_CONST_METHOD4(Post_t,
+                       std::string(const std::string& uri, const std::string& data, HttpCode* code, ErrorInterface** err));
+
+};
+
+
+}
+
+#endif //HIDRA2_MOCKHTTPCLIENT_H
diff --git a/common/cpp/src/data_structs/data_structs.cpp b/common/cpp/src/data_structs/data_structs.cpp
index 331a741a3ef2380e662901b2f95b77a961be8ee6..5887f4e6994e66f0356f2464c99fcd9798a8b235 100644
--- a/common/cpp/src/data_structs/data_structs.cpp
+++ b/common/cpp/src/data_structs/data_structs.cpp
@@ -16,7 +16,7 @@ std::string FileInfo::Json() const {
 }
 
 
-bool TimeFromJson(const JsonParser& parser, const std::string name, std::chrono::system_clock::time_point* val) {
+bool TimeFromJson(const JsonStringParser& parser, const std::string name, std::chrono::system_clock::time_point* val) {
     uint64_t nanoseconds_from_epoch;
     if (parser.GetUInt64(name, &nanoseconds_from_epoch)) {
         return false;
@@ -34,7 +34,7 @@ bool TimeFromJson(const JsonParser& parser, const std::string name, std::chrono:
 bool FileInfo::SetFromJson(const std::string& json_string) {
     auto old = *this;
 
-    JsonParser parser(json_string, false);
+    JsonStringParser parser(json_string);
 
     if (parser.GetUInt64("_id", &id) ||
             parser.GetUInt64("size", &size) ||
diff --git a/common/cpp/src/http_client/CMakeLists.txt b/common/cpp/src/http_client/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..b274c9319e20e484c12d9f22f8668fda3f6a39f9
--- /dev/null
+++ b/common/cpp/src/http_client/CMakeLists.txt
@@ -0,0 +1,16 @@
+set(TARGET_NAME curl_http_client)
+set(SOURCE_FILES
+        curl_http_client.cpp
+        )
+
+
+################################
+# Library
+################################
+add_library(${TARGET_NAME} OBJECT ${SOURCE_FILES})
+IF(WIN32)
+    target_compile_definitions(${TARGET_NAME} PUBLIC -DCURL_STATICLIB)
+ENDIF()
+target_include_directories(${TARGET_NAME} PUBLIC ${HIDRA2_CXX_COMMON_INCLUDE_DIR} ${CURL_INCLUDE_DIRS})
+
+
diff --git a/worker/api/cpp/src/curl_http_client.cpp b/common/cpp/src/http_client/curl_http_client.cpp
similarity index 69%
rename from worker/api/cpp/src/curl_http_client.cpp
rename to common/cpp/src/http_client/curl_http_client.cpp
index 364b87cd1975a90de5ec53cc3eefaeb8e126265c..a6c98fa136c99d1d530c54f2cb7ece2b0e79bce6 100644
--- a/worker/api/cpp/src/curl_http_client.cpp
+++ b/common/cpp/src/http_client/curl_http_client.cpp
@@ -1,4 +1,4 @@
-#include "curl_http_client.h"
+#include "http_client/curl_http_client.h"
 
 #include <cstring>
 
@@ -25,13 +25,20 @@ size_t curl_write( void* ptr, size_t size, size_t nmemb, void* buffer) {
     return size * nmemb;
 }
 
-void SetCurlOptions(CURL* curl, const std::string& uri, char* errbuf, std::string* buffer) {
+void SetCurlOptions(CURL* curl, bool post, const std::string& data, const std::string& uri, char* errbuf,
+                    std::string* buffer) {
     errbuf[0] = 0;
     curl_easy_setopt(curl, CURLOPT_URL, uri.c_str());
     curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write);
     curl_easy_setopt(curl, CURLOPT_WRITEDATA, buffer);
     curl_easy_setopt(curl, CURLOPT_FAILONERROR, 0L);
     curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf);
+    //todo use a config parameter for this
+    curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT_MS, 5000L);
+
+    if (post) {
+        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str());
+    }
 
 }
 
@@ -56,24 +63,38 @@ Error ProcessCurlResponse(CURL* curl, CURLcode res, const char* errbuf,
         return nullptr;
     } else {
         *buffer = GetCurlError(curl, res, errbuf);
-        return TextError(std::string(WorkerErrorMessage::kErrorReadingSource) + ": " + *buffer);
+        return TextError("Curl client error: " + *buffer);
     }
 }
 
-std::string CurlHttpClient::Get(const std::string& uri, HttpCode* response_code, Error* err) const noexcept {
+std::string CurlHttpClient::Command(bool post, const std::string& uri, const std::string& data, HttpCode* response_code,
+                                    Error* err) const noexcept {
     std::lock_guard<std::mutex> lock{mutex_};
 
     std::string buffer;
     char errbuf[CURL_ERROR_SIZE];
-    SetCurlOptions(curl_, uri, errbuf, &buffer);
+
+    SetCurlOptions(curl_, post, data, uri, errbuf, &buffer);
 
     auto res = curl_easy_perform(curl_);
 
     *err = ProcessCurlResponse(curl_, res, errbuf, &buffer, response_code);
 
     return buffer;
+
+}
+
+
+std::string CurlHttpClient::Get(const std::string& uri, HttpCode* response_code, Error* err) const noexcept {
+    return Command(false, uri, "", response_code, err);
 }
 
+std::string CurlHttpClient::Post(const std::string& uri, const std::string& data, HttpCode* response_code,
+                                 Error* err) const noexcept {
+    return Command(true, uri, data, response_code, err);
+}
+
+
 CurlHttpClient::CurlHttpClient() {
     curl_ = curl_easy_init();
     if (!curl_) {
diff --git a/common/cpp/src/json_parser/json_parser.cpp b/common/cpp/src/json_parser/json_parser.cpp
index 1a848557881de69cf1fbcab77fcc2997e6f28d00..03de7dd24d25f90f9ac85585dee3974f5d536fd5 100644
--- a/common/cpp/src/json_parser/json_parser.cpp
+++ b/common/cpp/src/json_parser/json_parser.cpp
@@ -1,14 +1,18 @@
 #include "json_parser/json_parser.h"
 #include "rapid_json.h"
+#include "io/io_factory.h"
 
 namespace hidra2 {
 
-
 JsonParser::~JsonParser() {
 
 }
 
-JsonParser::JsonParser(const std::string& json, bool read_from_file) : rapid_json_{new RapidJson(json, read_from_file)} {
+JsonParser::JsonParser(const std::string& json, const std::unique_ptr<IO>* io ) :
+    default_io_{GenerateDefaultIO()}, rapid_json_{new RapidJson(json, io != nullptr ? io : & default_io_)} {
+}
+
+JsonParser::JsonParser(const std::string& json) : rapid_json_{new RapidJson(json, nullptr)} {
 }
 
 Error JsonParser::GetArrayUInt64(const std::string& name, std::vector<uint64_t>* val) const noexcept {
@@ -21,6 +25,11 @@ Error JsonParser::GetArrayString(const std::string& name, std::vector<std::strin
 }
 
 
+Error JsonParser::GetBool(const std::string& name, bool* val) const noexcept {
+    return rapid_json_->GetBool(name, val);
+}
+
+
 Error JsonParser::GetUInt64(const std::string& name, uint64_t* val) const noexcept {
     return rapid_json_->GetUInt64(name, val);
 }
@@ -30,7 +39,7 @@ Error JsonParser::GetString(const std::string& name, std::string* val) const noe
 }
 
 JsonParser JsonParser::Embedded(const std::string& name) const noexcept {
-    RapidJson* rapid_json = new RapidJson(*rapid_json_.get(), name);
+    RapidJson* rapid_json = new RapidJson(*rapid_json_.get(), name) ;
     return JsonParser(rapid_json);
 }
 
diff --git a/common/cpp/src/json_parser/rapid_json.cpp b/common/cpp/src/json_parser/rapid_json.cpp
index 37d1c8771aa04d6f4150df97c229157ce6c10916..e63474f99e6c92dc39e124f6b32b2f593650312d 100644
--- a/common/cpp/src/json_parser/rapid_json.cpp
+++ b/common/cpp/src/json_parser/rapid_json.cpp
@@ -3,12 +3,9 @@
 
 using namespace rapidjson;
 
-#include "io/io_factory.h"
-
 namespace hidra2 {
 
-RapidJson::RapidJson(const std::string& json, bool read_from_file): io__{GenerateDefaultIO()}, json_{json},
-    read_from_file_{read_from_file} {
+RapidJson::RapidJson(const std::string& json, const std::unique_ptr<IO>* io): io__{io}, json_{json} {
 
 }
 
@@ -21,9 +18,9 @@ Error RapidJson::LazyInitialize()const noexcept {
         return nullptr;
 
     auto str = json_;
-    if (read_from_file_) {
+    if (io__) {
         Error err;
-        str = io__->ReadFileToString(json_, &err);
+        str = (*io__)->ReadFileToString(json_, &err);
         if (err != nullptr) {
             return err;
         }
@@ -34,24 +31,28 @@ Error RapidJson::LazyInitialize()const noexcept {
     }
 
     object_ = doc_.GetObject();
-
+    object_p_ = &object_;
+    initialized_ = true;
     return nullptr;
 }
 
-hidra2::Error CheckValueType(const std::string& name, ValueType type, const Value& val) {
+hidra2::Error CheckValueType(const std::string& name, ValueType type, const Value* val) {
     bool res = false;
     switch (type) {
     case ValueType::kObject:
-        res = val.IsObject();
+        res = val->IsObject();
         break;
     case ValueType::kString:
-        res = val.IsString();
+        res = val->IsString();
         break;
     case ValueType::kUint64:
-        res = val.IsInt64();
+        res = val->IsInt64();
+        break;
+    case ValueType::kBool:
+        res = val->IsBool();
         break;
     case ValueType::kArray:
-        res = val.IsArray();
+        res = val->IsArray();
         break;
     }
     if (!res) {
@@ -62,46 +63,56 @@ hidra2::Error CheckValueType(const std::string& name, ValueType type, const Valu
 }
 
 
-hidra2::Error RapidJson::GetValue(const std::string& name, ValueType type, Value* val)const noexcept {
+hidra2::Error RapidJson::GetValuePointer(const std::string& name, ValueType type, Value** val)const noexcept {
     if (Error err = LazyInitialize()) {
         return err;
     }
 
-    auto iterator = object_.FindMember(name.c_str());
-    if (iterator == object_.MemberEnd()) {
+    auto iterator = object_p_->FindMember(name.c_str());
+    if (iterator == object_p_->MemberEnd()) {
         return  TextError("cannot find: " + name);
     }
 
-    *val =  iterator->value;
+    *val =  &iterator->value;
     return CheckValueType(name, type, *val);
 }
 
 Error RapidJson::GetUInt64(const std::string& name, uint64_t* val) const noexcept {
-    Value json_val;
-    if (Error err = GetValue(name, ValueType::kUint64, &json_val)) {
+    Value* json_val;
+    if (Error err = GetValuePointer(name, ValueType::kUint64, &json_val)) {
+        return err;
+    }
+    *val = json_val->GetInt64();
+    return nullptr;
+}
+
+Error RapidJson::GetBool(const std::string& name, bool* val) const noexcept {
+    Value* json_val;
+    if (Error err = GetValuePointer(name, ValueType::kBool, &json_val)) {
         return err;
     }
-    *val = json_val.GetInt64();
+    *val = json_val->GetBool();
     return nullptr;
 }
 
 Error RapidJson::GetString(const std::string& name, std::string* val) const noexcept {
-    Value json_val;
-    if (Error err = GetValue(name, ValueType::kString, &json_val)) {
+    Value* json_val;
+    if (Error err = GetValuePointer(name, ValueType::kString, &json_val)) {
         return err;
     }
-    *val = json_val.GetString();
+    *val = json_val->GetString();
     return nullptr;
 }
 
 
 Error RapidJson::GetArrayUInt64(const std::string& name, std::vector<uint64_t>* val) const noexcept {
-    Value json_val;
-    if (Error err = GetValue(name, ValueType::kArray, &json_val)) {
+    Value* json_val;
+    if (Error err = GetValuePointer(name, ValueType::kArray, &json_val)) {
         return err;
     }
 
-    for (auto& v : json_val.GetArray()) {
+    val->clear();
+    for (auto& v : json_val->GetArray()) {
         if (!v.IsInt64()) {
             return TextError("wrong type of array element: " + name);
         }
@@ -112,12 +123,13 @@ Error RapidJson::GetArrayUInt64(const std::string& name, std::vector<uint64_t>*
 }
 
 Error RapidJson::GetArrayString(const std::string& name, std::vector<std::string>* val) const noexcept {
-    Value json_val;
-    if (Error err = GetValue(name, ValueType::kArray, &json_val)) {
+    Value* json_val;
+    if (Error err = GetValuePointer(name, ValueType::kArray, &json_val)) {
         return err;
     }
 
-    for (auto& v : json_val.GetArray()) {
+    val->clear();
+    for (auto& v : json_val->GetArray()) {
         if (!v.IsString()) {
             return TextError("wrong type of array element: " + name);
         }
@@ -127,9 +139,8 @@ Error RapidJson::GetArrayString(const std::string& name, std::vector<std::string
 
 }
 
-
 RapidJson::RapidJson(const RapidJson& parent, const std::string& subname) {
-    auto err = parent.GetValue(subname, ValueType::kObject, &object_);
+    auto err = parent.GetValuePointer(subname, ValueType::kObject, &object_p_);
     if (err) {
         embedded_error_ = std::move(err);
         return;
diff --git a/common/cpp/src/json_parser/rapid_json.h b/common/cpp/src/json_parser/rapid_json.h
index ef26611e4abcafc3137fd18e7eef18ab07a717ee..f0369ba1430d1c9b8afff0ff8e95df5e39999e88 100644
--- a/common/cpp/src/json_parser/rapid_json.h
+++ b/common/cpp/src/json_parser/rapid_json.h
@@ -12,28 +12,30 @@ enum class ValueType {
     kUint64,
     kString,
     kObject,
-    kArray
+    kArray,
+    kBool
 };
 
 class RapidJson {
   public:
-    RapidJson(const std::string& json, bool read_from_file);
+    RapidJson(const std::string& json, const std::unique_ptr<IO>* io);
     RapidJson(const RapidJson& parent, const std::string& subname);
     Error GetUInt64(const std::string& name, uint64_t* val) const noexcept;
+    Error GetBool(const std::string& name, bool* val) const noexcept;
     Error GetString(const std::string& name, std::string* val) const noexcept;
     Error GetArrayUInt64(const std::string& name, std::vector<uint64_t>* val) const noexcept;
     Error GetArrayString(const std::string& name, std::vector<std::string>* val) const noexcept;
-    std::unique_ptr<IO> io__;
   private:
+    const std::unique_ptr<IO>* io__;
     mutable rapidjson::Document doc_;
     mutable rapidjson::Value object_;
+    mutable rapidjson::Value* object_p_;
     std::string json_;
-    bool read_from_file_;
     mutable bool initialized_ = false;
     Error LazyInitialize() const noexcept;
     Error embedded_error_ = nullptr;
 
-    hidra2::Error GetValue(const std::string& name, ValueType type, rapidjson::Value* val)const noexcept;
+    hidra2::Error GetValuePointer(const std::string& name, ValueType type, rapidjson::Value** val)const noexcept;
 };
 
 }
diff --git a/common/cpp/src/system_io/system_io.cpp b/common/cpp/src/system_io/system_io.cpp
index 70c4a93ddfbbe4fd6c6872491370d2539e651ff5..1315a8eb613e98f34a5a7e1ff27acabe33474327 100644
--- a/common/cpp/src/system_io/system_io.cpp
+++ b/common/cpp/src/system_io/system_io.cpp
@@ -23,7 +23,7 @@
 
 namespace hidra2 {
 
-const int SystemIO::kNetBufferSize = 1024 * 1024 ; //MiByte
+const int SystemIO::kNetBufferSize = 1024 * 1024; //* 1024 ; //MiByte
 
 /*******************************************************************************
  *                              system_io.cpp                                  *
diff --git a/common/cpp/src/system_io/system_io_linux.cpp b/common/cpp/src/system_io/system_io_linux.cpp
index e0faf78b744ddc6becc4e556d2d575ec9e6fdcf6..c84e2f1e93e49dbc45fa632cef0c86216b90cbce 100644
--- a/common/cpp/src/system_io/system_io_linux.cpp
+++ b/common/cpp/src/system_io/system_io_linux.cpp
@@ -176,7 +176,6 @@ void SystemIO::CollectFileInformationRecursively(const std::string& path,
 
 void SystemIO::ApplyNetworkOptions(SocketDescriptor socket_fd, Error* err) const {
     //TODO: Need to change network layer code, so everything can be NonBlocking
-    // in use and one have to wait for some time until the system cleans up the stuff
     int flag;
     if (
         /*(flags = fcntl(socket_fd, F_GETFL, 0)) == -1
diff --git a/common/cpp/unittests/json_parser/test_json_parser.cpp b/common/cpp/unittests/json_parser/test_json_parser.cpp
index 0767fd02065553e54cafbc86556183eb40702eec..90ef15799972e9e560e7a7022346cf8101f41ba8 100644
--- a/common/cpp/unittests/json_parser/test_json_parser.cpp
+++ b/common/cpp/unittests/json_parser/test_json_parser.cpp
@@ -20,7 +20,8 @@ using ::testing::SetArgPointee;
 using ::testing::HasSubstr;
 using ::testing::ElementsAre;
 
-using hidra2::JsonParser;
+using hidra2::JsonFileParser;
+using hidra2::JsonStringParser;
 using hidra2::RapidJson;
 using hidra2::MockIO;
 using hidra2::IO;
@@ -29,30 +30,35 @@ using hidra2::IO;
 namespace {
 
 TEST(ParseString, SimpleConvertToJson) {
-    std::string json = R"({"_id":2,"foo":"foo","bar":1})";
+    std::string json = R"({"_id":2,"foo":"foo","bar":1,"flag":true})";
 
-    JsonParser parser{json, false};
+    JsonStringParser parser{json};
 
     uint64_t id, bar;
     std::string foo;
+    bool flag;
     auto err1 = parser.GetUInt64("_id", &id);
     auto err2 = parser.GetString("foo", &foo);
     auto err3 = parser.GetUInt64("bar", &bar);
+    auto err4 = parser.GetBool("flag", &flag);
 
     ASSERT_THAT(err1, Eq(nullptr));
     ASSERT_THAT(err2, Eq(nullptr));
     ASSERT_THAT(err3, Eq(nullptr));
+    ASSERT_THAT(err4, Eq(nullptr));
+
 
     ASSERT_THAT(id, Eq(2));
     ASSERT_THAT(foo, Eq("foo"));
     ASSERT_THAT(bar, Eq(1));
+    ASSERT_THAT(flag, true);
 
 }
 
 TEST(ParseString, EmbeddedConvertToJson) {
     std::string json = R"({"id":{"test":2}})";
 
-    JsonParser parser{json, false};
+    JsonStringParser parser{json};
 
     uint64_t id;
     auto err1 = parser.Embedded("id").GetUInt64("test", &id);
@@ -64,7 +70,7 @@ TEST(ParseString, EmbeddedConvertToJson) {
 TEST(ParseString, DoubleEmbeddedConvertToJson) {
     std::string json = R"({"id":{"test":{"test2":2}}})";
 
-    JsonParser parser{json, false};
+    JsonStringParser parser{json};
 
     uint64_t id;
     auto err1 = parser.Embedded("id").Embedded("test").GetUInt64("test2", &id);
@@ -76,7 +82,7 @@ TEST(ParseString, DoubleEmbeddedConvertToJson) {
 TEST(ParseString, ErrorOnWrongEmbeddedKey) {
     std::string json = R"({"id1":{"test":2}})";
 
-    JsonParser parser{json, false};
+    JsonStringParser parser{json};
 
     uint64_t id;
     auto err = parser.Embedded("id").GetUInt64("test", &id);
@@ -88,7 +94,7 @@ TEST(ParseString, ErrorOnWrongEmbeddedKey) {
 TEST(ParseString, ErrorOnWrongEmbeddedSubKey) {
     std::string json = R"({"id1":{"test1":2}})";
 
-    JsonParser parser{json, false};
+    JsonStringParser parser{json};
 
     uint64_t id;
     auto err = parser.Embedded("id").GetUInt64("test", &id);
@@ -100,7 +106,7 @@ TEST(ParseString, ErrorOnWrongEmbeddedSubKey) {
 TEST(ParseString, ErrorOnWrongKey) {
     std::string json = R"({"_id":"2"})";
 
-    JsonParser parser{json, false};
+    JsonStringParser parser{json};
 
     uint64_t id;
     auto err = parser.GetUInt64("_id1", &id);
@@ -112,7 +118,7 @@ TEST(ParseString, ErrorOnWrongKey) {
 TEST(ParseString, ErrorOnWrongType) {
     std::string json = R"({"_id":"2"})";
 
-    JsonParser parser{json, false};
+    JsonStringParser parser{json};
 
     uint64_t id;
     auto err = parser.GetUInt64("_id", &id);
@@ -125,7 +131,7 @@ TEST(ParseString, ErrorOnWrongType) {
 TEST(ParseString, ErrorOnWrongDocument) {
     std::string json = R"({"_id":2)";
 
-    JsonParser parser{json, false};
+    JsonStringParser parser{json};
 
     uint64_t id;
     auto err = parser.GetUInt64("_id", &id);
@@ -139,7 +145,7 @@ TEST(ParseString, ErrorOnWrongDocument) {
 TEST(ParseString, IntArrayConvertToJson) {
     std::string json = R"({"array":[1,2,3]})";
 
-    JsonParser parser{json, false};
+    JsonStringParser parser{json};
 
     std::vector<uint64_t> vec;
     auto err = parser.GetArrayUInt64("array", &vec);
@@ -148,10 +154,25 @@ TEST(ParseString, IntArrayConvertToJson) {
     ASSERT_THAT(vec, ElementsAre(1, 2, 3));
 }
 
+TEST(ParseString, IntArrayConvertToJsonTwice) {
+    std::string json = R"({"array":[1,2,3]})";
+
+    JsonStringParser parser{json};
+
+    std::vector<uint64_t> vec;
+    auto err = parser.GetArrayUInt64("array", &vec);
+    auto err2 = parser.GetArrayUInt64("array", &vec);
+
+    ASSERT_THAT(err, Eq(nullptr));
+    ASSERT_THAT(err2, Eq(nullptr));
+    ASSERT_THAT(vec, ElementsAre(1, 2, 3));
+}
+
+
 TEST(ParseString, IntArrayErrorConvertToJson) {
     std::string json = R"({"array":[1,2,"3"]})";
 
-    JsonParser parser{json, false};
+    JsonStringParser parser{json};
 
     std::vector<uint64_t> vec;
     auto err = parser.GetArrayUInt64("array", &vec);
@@ -161,10 +182,11 @@ TEST(ParseString, IntArrayErrorConvertToJson) {
 }
 
 
+
 TEST(ParseString, StringArrayConvertToJson) {
     std::string json = R"({"array":["s1","s2","s3"]})";
 
-    JsonParser parser{json, false};
+    JsonStringParser parser{json};
 
     std::vector<std::string> vec;
     auto err = parser.GetArrayString("array", &vec);
@@ -175,13 +197,16 @@ TEST(ParseString, StringArrayConvertToJson) {
 
 class ParseFileTests : public Test {
   public:
-    RapidJson parser{"filename", true};
     NiceMock<MockIO> mock_io;
+    std::unique_ptr<IO> io_ptr = std::unique_ptr<IO> {
+        &mock_io
+    };
+    JsonFileParser parser{"filename", &io_ptr};
+
     void SetUp() override {
-        parser.io__ = std::unique_ptr<IO> {&mock_io};
     }
     void TearDown() override {
-        parser.io__.release();
+        io_ptr.release();
     }
 };
 
@@ -195,6 +220,24 @@ TEST_F(ParseFileTests, CorrectConvertFileToJson) {
     auto err = parser.GetUInt64("_id", &id);
     ASSERT_THAT(id, Eq(2));
 }
+
+TEST_F(ParseFileTests, InitializedOnlyOnce) {
+    std::string json = R"({"_id":2})";
+
+    EXPECT_CALL(mock_io, ReadFileToString_t("filename", _)).
+    WillOnce(DoAll(testing::SetArgPointee<1>(nullptr), testing::Return(json)));
+
+    uint64_t id, id2;
+    auto err1 = parser.GetUInt64("_id", &id);
+    auto err2 = parser.GetUInt64("_id", &id2);
+
+    ASSERT_THAT(err1, Eq(nullptr));
+    ASSERT_THAT(err2, Eq(nullptr));
+    ASSERT_THAT(id, Eq(2));
+    ASSERT_THAT(id2, Eq(2));
+}
+
+
 TEST_F(ParseFileTests, CannotReadFile) {
     std::string json = R"({"_id":2})";
 
diff --git a/examples/producer/dummy-data-producer/check_linux.sh b/examples/producer/dummy-data-producer/check_linux.sh
index 04f592decbf9d22d1301157df898e0f9c2b764fa..51a14177248525416ad5e995fcca02f755bddf75 100644
--- a/examples/producer/dummy-data-producer/check_linux.sh
+++ b/examples/producer/dummy-data-producer/check_linux.sh
@@ -6,5 +6,5 @@ database_name=test_run
 
 
 #just test that it starts, no reciever is running
-$@ 0.0.0.0 1 1 2>&1 | grep "refused"
+$@ 0.0.0.0 1 1 2>&1 | grep "Failed to connect"
 
diff --git a/examples/producer/dummy-data-producer/dummy_data_producer.cpp b/examples/producer/dummy-data-producer/dummy_data_producer.cpp
index a9476d52422276874a68b692957d4d034c19191b..c7bae8553d6b740df188573d851196d2089ba107 100644
--- a/examples/producer/dummy-data-producer/dummy_data_producer.cpp
+++ b/examples/producer/dummy-data-producer/dummy_data_producer.cpp
@@ -28,7 +28,7 @@ bool SendDummyData(hidra2::Producer* producer, size_t number_of_byte, uint64_t i
     auto buffer = std::unique_ptr<uint8_t>(new uint8_t[number_of_byte]);
 
     for(uint64_t i = 0; i < iterations; i++) {
-        std::cout << "Send file " << i + 1 << "/" << iterations << std::endl;
+//        std::cerr << "Send file " << i + 1 << "/" << iterations << std::endl;
 
         auto err = producer->Send(i, buffer.get(), number_of_byte);
 
@@ -36,7 +36,7 @@ bool SendDummyData(hidra2::Producer* producer, size_t number_of_byte, uint64_t i
             std::cerr << "File was not successfully send: " << err << std::endl;
             return false;
         } else {
-            std::cout << "File was successfully send." << std::endl;
+//            std::cerr << "File was successfully send." << std::endl;
         }
     }
 
@@ -45,12 +45,12 @@ bool SendDummyData(hidra2::Producer* producer, size_t number_of_byte, uint64_t i
 
 int main (int argc, char* argv[]) {
     std::string receiver_address;
-    size_t number_of_byte;
+    size_t number_of_kbytes;
     uint64_t iterations;
-    std::tie(receiver_address, number_of_byte, iterations) = ProcessCommandArguments(argc, argv);
+    std::tie(receiver_address, number_of_kbytes, iterations) = ProcessCommandArguments(argc, argv);
 
     std::cout << "receiver_address: " << receiver_address << std::endl
-              << "number_of_byte: " << number_of_byte << std::endl
+              << "Package size: " << number_of_kbytes << "k" << std::endl
               << "iterations: " << iterations << std::endl
               << std::endl;
 
@@ -62,9 +62,16 @@ int main (int argc, char* argv[]) {
     }
     std::cout << "Successfully connected" << std::endl;
 
-    if(!SendDummyData(producer.get(), number_of_byte, iterations)) {
+    high_resolution_clock::time_point t1 = high_resolution_clock::now();
+    if(!SendDummyData(producer.get(), number_of_kbytes * 1024, iterations)) {
         return EXIT_FAILURE;
     }
+    high_resolution_clock::time_point t2 = high_resolution_clock::now();
+    double duration_sec = std::chrono::duration_cast<std::chrono::milliseconds>( t2 - t1 ).count() / 1000.0;
+    double size_gb = double(number_of_kbytes) * iterations / 1024.0 / 1024.0 * 8.0;
+    double rate = iterations / duration_sec;
+    std::cout << "Rate: " << rate << " Hz" << std::endl;
+    std::cout << "Bandwidth " << size_gb / duration_sec << " Gbit/s" << std::endl;
 
     return EXIT_SUCCESS;
 }
diff --git a/examples/worker/getnext_broker/CMakeLists.txt b/examples/worker/getnext_broker/CMakeLists.txt
index 58f244cf82c5f36d1100d5ea47622f2671c00175..903ff9db59315b9c4c03dd680adf631f48c8efd8 100644
--- a/examples/worker/getnext_broker/CMakeLists.txt
+++ b/examples/worker/getnext_broker/CMakeLists.txt
@@ -10,7 +10,7 @@ set_target_properties(${TARGET_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY
 
 get_target_property(VAR ${TARGET_NAME} RUNTIME_OUTPUT_DIRECTORY)
 
-configure_file(${CMAKE_SOURCE_DIR}/tests/settings/broker_settings.json settings.json COPYONLY)
+configure_file(${CMAKE_SOURCE_DIR}/tests/automatic/settings/broker_settings.json settings.json COPYONLY)
 add_script_test("${TARGET_NAME}" "${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME} $<TARGET_PROPERTY:hidra2-broker,EXENAME>")
 
 set (dir examples/worker/${TARGET_NAME})
diff --git a/receiver/CMakeLists.txt b/receiver/CMakeLists.txt
index 3fd260ef2dad27c224f83a2311bca432f79626e3..6f5ee995cbb7c61e4178e5c3687602836b493292 100644
--- a/receiver/CMakeLists.txt
+++ b/receiver/CMakeLists.txt
@@ -5,25 +5,27 @@ set(SOURCE_FILES
         src/receiver_error.h
         src/request.cpp
         src/request_handler_file_write.cpp
-        )
+        src/statistics.cpp
+        src/statistics_sender_influx_db.cpp
+        src/receiver_config.cpp src/receiver_config.h)
 
 
 ################################
 # Library
 ################################
-add_library(${TARGET_NAME} STATIC ${SOURCE_FILES} $<TARGET_OBJECTS:system_io>)
+
+
+add_library(${TARGET_NAME} STATIC ${SOURCE_FILES} $<TARGET_OBJECTS:system_io> $<TARGET_OBJECTS:curl_http_client>
+         $<TARGET_OBJECTS:json_parser>)
 set_target_properties(${TARGET_NAME} PROPERTIES LINKER_LANGUAGE CXX)
-target_include_directories(${TARGET_NAME} PUBLIC ${HIDRA2_CXX_COMMON_INCLUDE_DIR})
+target_include_directories(${TARGET_NAME} PUBLIC ${HIDRA2_CXX_COMMON_INCLUDE_DIR} ${CURL_INCLUDE_DIRS})
+target_link_libraries(${TARGET_NAME} ${CURL_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
+
 
 add_executable(${TARGET_NAME}-bin src/main.cpp)
 set_target_properties(${TARGET_NAME}-bin PROPERTIES OUTPUT_NAME ${TARGET_NAME})
 target_link_libraries(${TARGET_NAME}-bin ${TARGET_NAME})
 
-#Add all necessary common libraries
-GET_PROPERTY(HIDRA2_COMMON_IO_LIBRARIES GLOBAL PROPERTY HIDRA2_COMMON_IO_LIBRARIES)
-target_link_libraries(${TARGET_NAME}-bin ${HIDRA2_COMMON_IO_LIBRARIES})
-set_target_properties(${TARGET_NAME}-bin PROPERTIES LINKER_LANGUAGE CXX)
-
 ################################
 # Testing
 ################################
@@ -33,8 +35,12 @@ set_property(TARGET ${TARGET_NAME} PROPERTY ENABLE_EXPORTS true)
 set(TEST_SOURCE_FILES
         unittests/test_receiver.cpp
         unittests/test_connection.cpp
+        unittests/test_statistics.cpp
+        unittests/test_config.cpp
         unittests/test_request.cpp
         unittests/test_request_handler_file_write.cpp
+        unittests/test_statistics_sender_influx_db.cpp
+        unittests/mock_receiver_config.cpp
         )
 #
 set(TEST_LIBRARIES "${TARGET_NAME};system_io")
diff --git a/receiver/src/connection.cpp b/receiver/src/connection.cpp
index b97358222717c8f4e26dad76f22d4bd56b9a276e..1f3794491fae420280a1c2442c0faec06b500ca0 100644
--- a/receiver/src/connection.cpp
+++ b/receiver/src/connection.cpp
@@ -10,7 +10,7 @@ size_t Connection::kRequestHandlerMaxBufferSize;
 std::atomic<uint32_t> Connection::kNetworkProducerPeerImplGlobalCounter(0);
 
 Connection::Connection(SocketDescriptor socket_fd, const std::string& address): request_factory__{new RequestFactory},
-io__{GenerateDefaultIO()} {
+io__{GenerateDefaultIO()}, statistics__{new Statistics} {
     socket_fd_ = socket_fd;
     connection_id_ = kNetworkProducerPeerImplGlobalCounter++;
     address_ = address;
@@ -34,7 +34,7 @@ NetworkErrorCode GetNetworkCodeFromError(const Error& err) {
 
 Error Connection::ProcessRequest(const std::unique_ptr<Request>& request) const noexcept {
     Error err;
-    err = request->Handle();
+    err = request->Handle(&statistics__);
     GenericNetworkResponse generic_response;
     generic_response.error_code = GetNetworkCodeFromError(err);
     if(err) {
@@ -45,6 +45,13 @@ Error Connection::ProcessRequest(const std::unique_ptr<Request>& request) const
 }
 
 
+void Connection::ProcessStatisticsAfterRequest(const std::unique_ptr<Request>& request) const noexcept {
+    statistics__->IncreaseRequestCounter();
+    statistics__->IncreaseRequestDataVolume(request->GetDataSize() + sizeof(GenericNetworkRequestHeader) +
+                                            sizeof(GenericNetworkResponse));
+    statistics__->SendIfNeeded();
+}
+
 void Connection::Listen() const noexcept {
     while(true) {
         Error err;
@@ -59,15 +66,18 @@ void Connection::Listen() const noexcept {
             std::cerr << "[" << GetId() << "] Error sending response: " << err << std::endl;
             break;
         }
+        ProcessStatisticsAfterRequest(request);
     }
     io__->CloseSocket(socket_fd_, nullptr);
-    std::cout << "[" << GetId() << "] Disconnected." << std::endl;
+    statistics__->Send();
+    std::cerr << "[" << GetId() << "] Disconnected." << std::endl;
 }
 
 
 std::unique_ptr<Request> Connection::WaitForNewRequest(Error* err) const noexcept {
     //TODO: to be overwritten with MessagePack (or similar)
     GenericNetworkRequestHeader generic_request_header;
+    statistics__->StartTimer(StatisticEntity::kNetwork);
     io__->ReceiveWithTimeout(socket_fd_, &generic_request_header, sizeof(GenericNetworkRequestHeader), 50, err);
     if(*err) {
         if(*err == IOErrorTemplates::kTimeout) {
@@ -75,6 +85,7 @@ std::unique_ptr<Request> Connection::WaitForNewRequest(Error* err) const noexcep
         }
         return nullptr;
     }
+    statistics__->StopTimer();
     return request_factory__->GenerateRequest(generic_request_header, socket_fd_, err);
 }
 
diff --git a/receiver/src/connection.h b/receiver/src/connection.h
index 9861ca0d00cdce67138b6cf51f38c8881b94b953..377019c82f34bff696c026c249853829116ff35a 100644
--- a/receiver/src/connection.h
+++ b/receiver/src/connection.h
@@ -14,6 +14,7 @@
 #include "common/networking.h"
 #include "io/io.h"
 #include "request.h"
+#include "statistics.h"
 
 namespace hidra2 {
 
@@ -35,10 +36,13 @@ class Connection {
 
     std::unique_ptr<RequestFactory> request_factory__;
     std::unique_ptr<IO> io__;
+    mutable std::unique_ptr<Statistics> statistics__;
+
 
   private:
     std::unique_ptr<Request> WaitForNewRequest(Error* err) const noexcept;
     Error ProcessRequest(const std::unique_ptr<Request>& request) const noexcept;
+    void ProcessStatisticsAfterRequest(const std::unique_ptr<Request>& request) const noexcept;
 };
 
 }
diff --git a/receiver/src/main.cpp b/receiver/src/main.cpp
index 3b871296c3a2838b2fc4561e5e01579380ae74cb..dcb19c776da0237e932246dc89794e5fbbc8a69e 100644
--- a/receiver/src/main.cpp
+++ b/receiver/src/main.cpp
@@ -1,12 +1,32 @@
 #include <iostream>
 #include "receiver.h"
 
+#include "receiver_config_factory.h"
+#include "receiver_config.h"
+
+
+hidra2::Error ReadConfigFile(int argc, char* argv[]) {
+    if (argc != 2) {
+        std::cerr << "Usage: " << argv[0] << " <config file>" << std::endl;
+        exit(EXIT_FAILURE);
+    }
+    hidra2::ReceiverConfigFactory factory;
+    return factory.SetConfigFromFile(argv[1]);
+}
+
 int main (int argc, char* argv[]) {
-    static const std::string address = "0.0.0.0:4200";
 
-    auto* receiver = new hidra2::Receiver();
+    auto err = ReadConfigFile(argc, argv);
+    if (err) {
+        std::cerr << "Cannot read config file: " << err << std::endl;
+        return 1;
+    }
+
+    auto config = hidra2::GetReceiverConfig();
 
-    hidra2::Error err;
+    static const std::string address = "0.0.0.0:" + std::to_string(config->listen_port);
+
+    auto* receiver = new hidra2::Receiver();
 
     std::cout << "Listening on " << address << std::endl;
     receiver->Listen(address, &err);
diff --git a/receiver/src/receiver_config.cpp b/receiver/src/receiver_config.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ba4e4b250daea114344e47c6c8c6a300e9f6ab0d
--- /dev/null
+++ b/receiver/src/receiver_config.cpp
@@ -0,0 +1,29 @@
+#include "receiver_config.h"
+#include "receiver_config_factory.h"
+#include "io/io_factory.h"
+#include "json_parser/json_parser.h"
+
+namespace hidra2 {
+
+ReceiverConfig config;
+
+ReceiverConfigFactory::ReceiverConfigFactory() : io__{GenerateDefaultIO()} {
+
+}
+
+Error ReceiverConfigFactory::SetConfigFromFile(std::string file_name) {
+    JsonFileParser parser(file_name, &io__);
+    Error err;
+    (err = parser.GetString("MonitorDbAddress", &config.monitor_db_uri)) ||
+    (err = parser.GetUInt64("ListenPort", &config.listen_port)) ||
+    (err = parser.GetBool("WriteToDisk", &config.write_to_disk)) ||
+    (err = parser.GetString("MonitorDbName", &config.monitor_db_name));
+    return err;
+}
+
+const ReceiverConfig*  GetReceiverConfig() {
+    return &config;
+}
+
+
+}
\ No newline at end of file
diff --git a/receiver/src/receiver_config.h b/receiver/src/receiver_config.h
new file mode 100644
index 0000000000000000000000000000000000000000..f0aa92adf8e6317c698f2a5621de6407ad25d4c2
--- /dev/null
+++ b/receiver/src/receiver_config.h
@@ -0,0 +1,21 @@
+#ifndef HIDRA2_RECEIVER_CONFIG_H
+#define HIDRA2_RECEIVER_CONFIG_H
+
+#include "io/io.h"
+#include "common/error.h"
+
+namespace hidra2 {
+
+struct ReceiverConfig {
+    std::string monitor_db_uri;
+    std::string monitor_db_name;
+    uint64_t listen_port = 0;
+    bool write_to_disk = false;
+};
+
+const ReceiverConfig* GetReceiverConfig();
+
+}
+
+
+#endif //HIDRA2_RECEIVER_CONFIG_H
diff --git a/receiver/src/receiver_config_factory.h b/receiver/src/receiver_config_factory.h
new file mode 100644
index 0000000000000000000000000000000000000000..ea4087e85c6d906d75244f6b76ee9dc4cd77c738
--- /dev/null
+++ b/receiver/src/receiver_config_factory.h
@@ -0,0 +1,20 @@
+#ifndef HIDRA2_RECEIVER_CONFIG_FACTORY__H
+#define HIDRA2_RECEIVER_CONFIG_FACTORY__H
+
+#include "io/io.h"
+#include "common/error.h"
+
+namespace hidra2 {
+
+class ReceiverConfigFactory {
+  public:
+    ReceiverConfigFactory();
+    Error SetConfigFromFile(std::string file_name);
+  public:
+    std::unique_ptr<IO> io__;
+};
+
+}
+
+
+#endif //HIDRA2_RECEIVER_CONFIG_FACTORY__H
diff --git a/receiver/src/request.cpp b/receiver/src/request.cpp
index 7f38d168f1af06cc33e3efae9f3e96bd08a2f453..19ac5ca8a7dfd394f652412dcdb56e069ae0b54c 100644
--- a/receiver/src/request.cpp
+++ b/receiver/src/request.cpp
@@ -1,6 +1,7 @@
 #include "request.h"
 #include "io/io_factory.h"
 
+#include "receiver_config.h"
 namespace hidra2 {
 
 Request::Request(const GenericNetworkRequestHeader& header,
@@ -28,19 +29,23 @@ Error Request::ReceiveData() {
 }
 
 
-Error Request::Handle() {
+Error Request::Handle(std::unique_ptr<Statistics>* statistics) {
     Error err;
     if (request_header_.data_size != 0) {
+        (*statistics)->StartTimer(StatisticEntity::kNetwork);
         auto err = ReceiveData();
         if (err) {
             return err;
         }
+        (*statistics)->StopTimer();
     }
     for (auto handler : handlers_) {
+        (*statistics)->StartTimer(handler->GetStatisticEntity());
         auto err = handler->ProcessRequest(*this);
         if (err) {
             return err;
         }
+        (*statistics)->StopTimer();
     }
     return nullptr;
 }
@@ -75,7 +80,9 @@ std::unique_ptr<Request> RequestFactory::GenerateRequest(const GenericNetworkReq
     switch (request_header.op_code) {
     case Opcode::kNetOpcodeSendData: {
         auto request = std::unique_ptr<Request> {new Request{request_header, socket_fd}};
-        request->AddHandler(&request_handler_filewrite_);
+        if (GetReceiverConfig()->write_to_disk) {
+            request->AddHandler(&request_handler_filewrite_);
+        }
         return request;
     }
     default:
diff --git a/receiver/src/request.h b/receiver/src/request.h
index 3d8ef1025dddc481ee9bd2dca594668f93367eab..b32a9e580401fa666c3fbcc404f03c68f8fb8ae8 100644
--- a/receiver/src/request.h
+++ b/receiver/src/request.h
@@ -6,6 +6,7 @@
 #include "io/io.h"
 #include "request_handler.h"
 #include "request_handler_file_write.h"
+#include "statistics.h"
 
 namespace hidra2 {
 
@@ -13,7 +14,7 @@ using RequestHandlerList = std::vector<const RequestHandler*>;
 
 class Request {
   public:
-    virtual Error Handle();
+    virtual Error Handle(std::unique_ptr<Statistics>*);
     virtual ~Request() = default;
     Request(const GenericNetworkRequestHeader& request_header, SocketDescriptor socket_fd);
     void AddHandler(const RequestHandler*);
diff --git a/receiver/src/request_handler.h b/receiver/src/request_handler.h
index 2d208c636cf6eb8d3e8a62c961ecb6c2db054584..179171dfe63fb594437c16b5e2801560ad0aca91 100644
--- a/receiver/src/request_handler.h
+++ b/receiver/src/request_handler.h
@@ -2,6 +2,7 @@
 #define HIDRA2_REQUEST_HANDLER_H
 
 #include "receiver_error.h"
+#include "statistics.h"
 
 namespace hidra2 {
 
@@ -10,6 +11,7 @@ class Request;
 class RequestHandler {
   public:
     virtual Error ProcessRequest(const Request& request) const = 0;
+    virtual StatisticEntity GetStatisticEntity() const  = 0;
     virtual ~RequestHandler() = default;
   private:
 };
diff --git a/receiver/src/request_handler_file_write.cpp b/receiver/src/request_handler_file_write.cpp
index 3b1a3c2bc4925502ed2789d3ac3e121f459edebb..3db786945e361be77064ea2cb4929c5611b6d1c4 100644
--- a/receiver/src/request_handler_file_write.cpp
+++ b/receiver/src/request_handler_file_write.cpp
@@ -21,4 +21,9 @@ RequestHandlerFileWrite::RequestHandlerFileWrite() : io__{GenerateDefaultIO()} {
 
 }
 
+StatisticEntity RequestHandlerFileWrite::GetStatisticEntity() const {
+    return StatisticEntity::kDisk;
+}
+
+
 }
diff --git a/receiver/src/request_handler_file_write.h b/receiver/src/request_handler_file_write.h
index 8b9828ccd7310c98ea5d9ceaae4c66a4abb12d3f..af81e2d959b54099dea75f6d494725e0de2db14e 100644
--- a/receiver/src/request_handler_file_write.h
+++ b/receiver/src/request_handler_file_write.h
@@ -12,6 +12,7 @@ const uint64_t kMaxFileSize = uint64_t(1024) * 1024 * 1024 * 2; //2GB
 class RequestHandlerFileWrite final: public RequestHandler {
   public:
     RequestHandlerFileWrite();
+    StatisticEntity GetStatisticEntity() const override;
     Error ProcessRequest(const Request& request) const override;
     std::unique_ptr<IO> io__;
 };
diff --git a/receiver/src/statistics.cpp b/receiver/src/statistics.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..fa806eb46bb702d55c95b4ff690411a8c6b248b8
--- /dev/null
+++ b/receiver/src/statistics.cpp
@@ -0,0 +1,80 @@
+#include "statistics.h"
+#include "statistics_sender_influx_db.h"
+#include <algorithm>
+
+using std::chrono::high_resolution_clock;
+
+namespace hidra2 {
+
+void Statistics::SendIfNeeded() noexcept {
+    if (GetTotalElapsedMs() > write_interval_) {
+        Send();
+    }
+}
+
+void Statistics::Send() noexcept {
+    statistics_sender__->SendStatistics(PrepareStatisticsToSend());
+    ResetStatistics();
+}
+
+
+StatisticsToSend Statistics::PrepareStatisticsToSend() const noexcept {
+    StatisticsToSend stat;
+    stat.n_requests = nrequests_;
+    stat.data_volume = volume_counter_;
+    stat.elapsed_ms = std::max(uint64_t{1}, GetTotalElapsedMs());
+    for (auto i = 0; i < kNStatisticEntities; i++) {
+        stat.entity_shares[i] =  double(GetElapsedMs(StatisticEntity(i))) / stat.elapsed_ms;
+    }
+    return stat;
+}
+
+uint64_t Statistics::GetTotalElapsedMs() const noexcept {
+    return std::chrono::duration_cast<std::chrono::milliseconds>
+           ( high_resolution_clock::now() - last_timepoint_).count();
+}
+
+uint64_t Statistics::GetElapsedMs(StatisticEntity entity) const noexcept {
+    return std::chrono::duration_cast<std::chrono::milliseconds>(time_counters_[entity]).count();
+}
+
+void Statistics::SetWriteInterval(uint64_t interval_ms) {
+    write_interval_ = interval_ms;
+}
+
+void Statistics::ResetStatistics() noexcept {
+    last_timepoint_ = high_resolution_clock::now();
+    nrequests_ = 0;
+    for (int i = 0; i < kNStatisticEntities; i++) {
+        time_counters_[i] = std::chrono::nanoseconds{0};
+    }
+    volume_counter_ = 0;
+}
+
+void Statistics::IncreaseRequestCounter() noexcept {
+    nrequests_++;
+}
+
+Statistics::Statistics(unsigned int write_frequency) : statistics_sender__{new StatisticsSenderInfluxDb},
+write_interval_{write_frequency} {
+    ResetStatistics();
+}
+
+void Statistics::StartTimer(const StatisticEntity& entity) noexcept {
+    current_statistic_entity_ = entity;
+    current_timer_last_timepoint_ = high_resolution_clock::now();
+}
+
+void Statistics::IncreaseRequestDataVolume(uint64_t transferred_data_volume) noexcept {
+    volume_counter_ += transferred_data_volume;
+}
+
+
+void Statistics::StopTimer() noexcept {
+    auto elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>
+                   (high_resolution_clock::now() - current_timer_last_timepoint_);
+    time_counters_[current_statistic_entity_] += elapsed;
+}
+
+
+}
\ No newline at end of file
diff --git a/receiver/src/statistics.h b/receiver/src/statistics.h
new file mode 100644
index 0000000000000000000000000000000000000000..de374ed981a2dc9236cbeec7ae792770b32b6ffb
--- /dev/null
+++ b/receiver/src/statistics.h
@@ -0,0 +1,56 @@
+#ifndef HIDRA2_STATISTICS_H
+#define HIDRA2_STATISTICS_H
+
+#include <chrono>
+#include <memory>
+
+#include "statistics_sender.h"
+
+namespace hidra2 {
+
+static const auto kNStatisticEntities = 3;
+enum StatisticEntity : int {
+    kDatabase = 0,
+    kDisk,
+    kNetwork,
+};
+
+struct StatisticsToSend {
+    double entity_shares[kNStatisticEntities];
+    uint64_t elapsed_ms;
+    uint64_t data_volume;
+    uint64_t n_requests;
+};
+
+class Statistics {
+  public:
+// virtual needed for unittests, could be replaced with #define VIRTUAL ... in case of performance issues
+    virtual void SendIfNeeded() noexcept;
+    virtual void Send() noexcept;
+    explicit Statistics(unsigned int write_interval = kDefaultStatisticWriteIntervalMs);
+    virtual void IncreaseRequestCounter() noexcept;
+    virtual void StartTimer(const StatisticEntity& entity) noexcept;
+    virtual void IncreaseRequestDataVolume(uint64_t transferred_data_volume) noexcept;
+    virtual void StopTimer() noexcept;
+
+    void SetWriteInterval(uint64_t interval_ms);
+    std::unique_ptr<StatisticsSender> statistics_sender__;
+  private:
+    uint64_t GetElapsedMs(StatisticEntity entity) const noexcept;
+    void ResetStatistics() noexcept;
+    uint64_t GetTotalElapsedMs() const noexcept;
+    StatisticsToSend PrepareStatisticsToSend() const noexcept;
+    static const unsigned int kDefaultStatisticWriteIntervalMs = 10000;
+    uint64_t nrequests_;
+    std::chrono::high_resolution_clock::time_point last_timepoint_;
+    std::chrono::high_resolution_clock::time_point current_timer_last_timepoint_;
+    StatisticEntity current_statistic_entity_ = StatisticEntity::kDatabase;
+    std::chrono::nanoseconds time_counters_[kNStatisticEntities];
+    uint64_t volume_counter_;
+    unsigned int write_interval_;
+
+};
+
+}
+
+#endif //HIDRA2_STATISTICS_H
diff --git a/receiver/src/statistics_sender.h b/receiver/src/statistics_sender.h
new file mode 100644
index 0000000000000000000000000000000000000000..2cf12b272a7b70bcfbaebadb493db5a47972cb29
--- /dev/null
+++ b/receiver/src/statistics_sender.h
@@ -0,0 +1,18 @@
+#ifndef HIDRA2_STATISTICS_SENDER_H
+#define HIDRA2_STATISTICS_SENDER_H
+
+#include <cstdint>
+
+namespace hidra2 {
+
+struct StatisticsToSend;
+
+class StatisticsSender {
+  public:
+    virtual void SendStatistics(const StatisticsToSend& statistic) const noexcept = 0;
+    virtual ~StatisticsSender() = default;
+};
+
+}
+
+#endif //HIDRA2_STATISTICS_SENDER_H
diff --git a/receiver/src/statistics_sender_influx_db.cpp b/receiver/src/statistics_sender_influx_db.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..84f8b07d4db80011c8fabf1673ee33d91e855719
--- /dev/null
+++ b/receiver/src/statistics_sender_influx_db.cpp
@@ -0,0 +1,52 @@
+#include "statistics_sender_influx_db.h"
+
+#include <iostream>
+
+#include "statistics.h"
+#include "http_client/curl_http_client.h"
+#include "receiver_config.h"
+
+namespace hidra2 {
+
+template<typename ... Args>
+std::string string_format( const std::string& format, Args ... args ) {
+    size_t size = snprintf( nullptr, 0, format.c_str(), args ... ) + 1;
+    std::unique_ptr<char[]> buf( new char[ size ] );
+    snprintf( buf.get(), size, format.c_str(), args ... );
+    return std::string( buf.get(), buf.get() + size - 1 );
+}
+
+
+void StatisticsSenderInfluxDb::SendStatistics(const StatisticsToSend& statistic) const noexcept {
+    HttpCode code;
+    Error err;
+    auto responce = httpclient__->Post(GetReceiverConfig()->monitor_db_uri + "/write?db=" +
+                                       GetReceiverConfig()->monitor_db_name, StatisticsToString(statistic),
+                                       &code, &err);
+    if (err) {
+        std::cerr << "Error sending statistics: " << err << std::endl;
+        return;
+    }
+
+    if (code != HttpCode::OK && code != HttpCode::NoContent) {
+        std::cerr << "Error sending statistics: " << responce << std::endl;
+    }
+}
+
+std::string StatisticsSenderInfluxDb::StatisticsToString(const StatisticsToSend& statistic) const noexcept {
+    std::string str;
+    std::string tags = "receiver=1,connection=1";
+    str = "statistics," + tags + " elapsed_ms=" + string_format("%ld", statistic.elapsed_ms);
+    str += ",data_volume=" + string_format("%ld", statistic.data_volume);
+    str += ",n_requests=" + string_format("%ld", statistic.n_requests);
+    str += ",db_share=" + string_format("%.4f", statistic.entity_shares[StatisticEntity::kDatabase]);
+    str += ",network_share=" + string_format("%.4f", statistic.entity_shares[StatisticEntity::kNetwork]);
+    str += ",disk_share=" + string_format("%.4f", statistic.entity_shares[StatisticEntity::kDisk]);
+    return str;
+}
+
+StatisticsSenderInfluxDb::StatisticsSenderInfluxDb(): httpclient__{new CurlHttpClient} {
+};
+
+
+}
diff --git a/receiver/src/statistics_sender_influx_db.h b/receiver/src/statistics_sender_influx_db.h
new file mode 100644
index 0000000000000000000000000000000000000000..aeff173333ec802cea81ae732e28d3dfa703832c
--- /dev/null
+++ b/receiver/src/statistics_sender_influx_db.h
@@ -0,0 +1,22 @@
+#ifndef HIDRA2_STATISTICS_SENDER_INFLUX_DB_H
+#define HIDRA2_STATISTICS_SENDER_INFLUX_DB_H
+
+#include "http_client/http_client.h"
+#include "statistics_sender.h"
+
+
+namespace hidra2 {
+
+class StatisticsSenderInfluxDb : public StatisticsSender {
+  public:
+    StatisticsSenderInfluxDb();
+    virtual void SendStatistics(const StatisticsToSend& statistic) const noexcept override;
+    std::unique_ptr<HttpClient> httpclient__;
+  private:
+    std::string StatisticsToString(const StatisticsToSend& statistic) const noexcept;
+
+};
+
+}
+
+#endif //HIDRA2_STATISTICS_SENDER_INFLUX_DB_H
diff --git a/receiver/unittests/mock_receiver_config.cpp b/receiver/unittests/mock_receiver_config.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7819fe13a934464ce8ddd2fbbb8f8abd90d7403c
--- /dev/null
+++ b/receiver/unittests/mock_receiver_config.cpp
@@ -0,0 +1,37 @@
+#include <gtest/gtest.h>
+#include <gmock/gmock.h>
+
+#include "mock_receiver_config.h"
+#include "../src/receiver_config_factory.h"
+
+#include <unittests/MockIO.h>
+
+using testing::_;
+
+namespace hidra2 {
+
+Error SetReceiverConfig (const ReceiverConfig& config) {
+    MockIO mock_io;
+    ReceiverConfigFactory config_factory;
+    config_factory.io__ = std::unique_ptr<IO> {&mock_io};
+
+    auto config_string = std::string("{\"MonitorDbAddress\":") + "\"" + config.monitor_db_uri + "\"";
+    config_string += "," + std::string("\"MonitorDbName\":") + "\"" + config.monitor_db_name + "\"";
+    config_string += "," + std::string("\"ListenPort\":") + std::to_string(config.listen_port);
+    config_string += "," + std::string("\"WriteToDisk\":") + (config.write_to_disk ? "true" : "false");
+    config_string += "}";
+
+    EXPECT_CALL(mock_io, ReadFileToString_t("fname", _)).WillOnce(
+        testing::Return(config_string)
+    );
+
+    auto err = config_factory.SetConfigFromFile("fname");
+
+    config_factory.io__.release();
+
+    return err;
+}
+
+}
+
+
diff --git a/receiver/unittests/mock_receiver_config.h b/receiver/unittests/mock_receiver_config.h
new file mode 100644
index 0000000000000000000000000000000000000000..9a724668cf0286d448bb9fb0129bd0ee18e1af12
--- /dev/null
+++ b/receiver/unittests/mock_receiver_config.h
@@ -0,0 +1,14 @@
+#ifndef HIDRA2_MOCK_RECEIVER_CONFIG_H
+#define HIDRA2_MOCK_RECEIVER_CONFIG_H
+
+#include "../src/receiver_config.h"
+
+
+namespace hidra2 {
+
+Error SetReceiverConfig (const ReceiverConfig& config);
+
+}
+
+
+#endif
diff --git a/receiver/unittests/mock_statistics.h b/receiver/unittests/mock_statistics.h
new file mode 100644
index 0000000000000000000000000000000000000000..dc4077a72ff9f683a106df5368c1de03da4c4f9c
--- /dev/null
+++ b/receiver/unittests/mock_statistics.h
@@ -0,0 +1,47 @@
+#ifndef HIDRA2_MOCK_STATISTICS_H
+#define HIDRA2_MOCK_STATISTICS_H
+
+#include <gtest/gtest.h>
+#include <gmock/gmock.h>
+
+#include "../src/statistics.h"
+
+namespace hidra2 {
+
+class MockStatistics : public hidra2::Statistics {
+  public:
+    void SendIfNeeded() noexcept override {
+        SendIfNeeded_t();
+    }
+
+    void Send() noexcept override {
+        Send_t();
+    }
+
+    void IncreaseRequestCounter() noexcept override {
+        IncreaseRequestCounter_t();
+    }
+    void StartTimer(const hidra2::StatisticEntity& entity) noexcept override {
+        StartTimer_t(entity);
+    }
+    void IncreaseRequestDataVolume(uint64_t transferred_data_volume) noexcept override {
+        IncreaseRequestDataVolume_t(transferred_data_volume);
+    }
+    void StopTimer() noexcept override {
+        StopTimer_t();
+    }
+
+    MOCK_METHOD0(SendIfNeeded_t, void());
+    MOCK_METHOD0(Send_t, void());
+    MOCK_METHOD0(IncreaseRequestCounter_t, void());
+    MOCK_METHOD0(StopTimer_t, void());
+    MOCK_METHOD1(IncreaseRequestDataVolume_t, void (uint64_t
+                                                    transferred_data_volume));
+    MOCK_METHOD1(StartTimer_t, void(
+                     const hidra2::StatisticEntity& entity));
+
+};
+
+}
+
+#endif //HIDRA2_MOCK_STATISTICS_H
diff --git a/receiver/unittests/test_config.cpp b/receiver/unittests/test_config.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ea15cede3b0eb2ba3bb0511f4d3381ee575a1454
--- /dev/null
+++ b/receiver/unittests/test_config.cpp
@@ -0,0 +1,70 @@
+#include <gtest/gtest.h>
+#include <gmock/gmock.h>
+#include <unittests/MockIO.h>
+
+#include "../src/receiver_config.h"
+#include "../src/receiver_config_factory.h"
+#include "mock_receiver_config.h"
+
+using ::testing::Test;
+using ::testing::Return;
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::SetArgReferee;
+using ::testing::Gt;
+using ::testing::Eq;
+using ::testing::Ne;
+using ::testing::Mock;
+using ::testing::NiceMock;
+using ::testing::SaveArg;
+using ::testing::SaveArgPointee;
+using ::testing::InSequence;
+using ::testing::SetArgPointee;
+using ::hidra2::Error;
+using ::hidra2::ErrorInterface;
+using ::hidra2::FileDescriptor;
+using ::hidra2::SocketDescriptor;
+using ::hidra2::MockIO;
+
+using ::hidra2::ReceiverConfigFactory;
+using hidra2::GetReceiverConfig;
+
+namespace {
+
+
+class ConfigTests : public Test {
+  public:
+    MockIO mock_io;
+    ReceiverConfigFactory config_factory;
+    void SetUp() override {
+        config_factory.io__ = std::unique_ptr<hidra2::IO> {&mock_io};
+    }
+    void TearDown() override {
+        config_factory.io__.release();
+    }
+
+};
+
+
+TEST_F(ConfigTests, ReadSettings) {
+
+    hidra2::ReceiverConfig test_config;
+    test_config.listen_port = 4200;
+    test_config.monitor_db_name = "db_test";
+    test_config.monitor_db_uri = "localhost:8086";
+    test_config.write_to_disk = true;
+
+
+    auto err = hidra2::SetReceiverConfig(test_config);
+
+    auto config = GetReceiverConfig();
+
+    ASSERT_THAT(err, Eq(nullptr));
+    ASSERT_THAT(config->monitor_db_uri, Eq("localhost:8086"));
+    ASSERT_THAT(config->monitor_db_name, Eq("db_test"));
+    ASSERT_THAT(config->listen_port, Eq(4200));
+    ASSERT_THAT(config->write_to_disk, true);
+
+}
+
+}
diff --git a/receiver/unittests/test_connection.cpp b/receiver/unittests/test_connection.cpp
index 846e6df26be5d0283e761d1d1d73cdd54765b18e..d09e26507ecb8e02de64b065bd1673799f5ac736 100644
--- a/receiver/unittests/test_connection.cpp
+++ b/receiver/unittests/test_connection.cpp
@@ -1,9 +1,12 @@
 #include <gtest/gtest.h>
 #include <gmock/gmock.h>
 #include <unittests/MockIO.h>
+
 #include "../src/connection.h"
 #include "../src/receiver_error.h"
 #include "../src/request.h"
+#include "../src/statistics.h"
+#include "mock_statistics.h"
 
 using ::testing::Test;
 using ::testing::Return;
@@ -12,6 +15,7 @@ using ::testing::DoAll;
 using ::testing::SetArgReferee;
 using ::testing::Gt;
 using ::testing::Eq;
+using ::testing::Ne;
 using ::testing::Mock;
 using ::testing::NiceMock;
 using ::testing::SaveArg;
@@ -30,19 +34,32 @@ using ::hidra2::Opcode;
 using ::hidra2::Connection;
 using ::hidra2::MockIO;
 using hidra2::Request;
+using hidra2::Statistics;
+using hidra2::StatisticEntity;
+using hidra2::MockStatistics;
 
 namespace {
 
+TEST(Connection, Constructor) {
+    Connection connection{0, "some_address"};
+    ASSERT_THAT(dynamic_cast<hidra2::Statistics*>(connection.statistics__.get()), Ne(nullptr));
+    ASSERT_THAT(dynamic_cast<hidra2::IO*>(connection.io__.get()), Ne(nullptr));
+    ASSERT_THAT(dynamic_cast<hidra2::RequestFactory*>(connection.request_factory__.get()), Ne(nullptr));
+}
+
+
+
 class MockRequest: public Request {
   public:
     MockRequest(const GenericNetworkRequestHeader& request_header, SocketDescriptor socket_fd):
         Request(request_header, socket_fd) {};
-    Error Handle() override {
+    Error Handle(std::unique_ptr<Statistics>* statistics) override {
         return Error{Handle_t()};
     };
     MOCK_CONST_METHOD0(Handle_t, ErrorInterface * ());
 };
 
+
 class MockRequestFactory: public hidra2::RequestFactory {
   public:
     std::unique_ptr<Request> GenerateRequest(const GenericNetworkRequestHeader& request_header,
@@ -65,18 +82,22 @@ class ConnectionTests : public Test {
     Connection connection{0, "some_address"};
     MockIO mock_io;
     MockRequestFactory mock_factory;
+    NiceMock<MockStatistics> mock_statictics;
     void SetUp() override {
-        connection.io__ = std::unique_ptr<hidra2::IO> {&mock_io};;
+        connection.io__ = std::unique_ptr<hidra2::IO> {&mock_io};
+        connection.statistics__ = std::unique_ptr<hidra2::Statistics> {&mock_statictics};
         connection.request_factory__ = std::unique_ptr<hidra2::RequestFactory> {&mock_factory};
         ON_CALL(mock_io, ReceiveWithTimeout_t(_, _, _, _, _)).
         WillByDefault(DoAll(testing::SetArgPointee<4>(nullptr),
                             testing::Return(0)));
         EXPECT_CALL(mock_io, CloseSocket_t(_, _));
+        EXPECT_CALL(mock_statictics, Send_t());
 
     }
     void TearDown() override {
         connection.io__.release();
         connection.request_factory__.release();
+        connection.statistics__.release();
     }
 
 };
@@ -180,4 +201,63 @@ TEST_F(ConnectionTests, SendsErrorToProducer) {
 
 }
 
+void MockExitCycle(const MockIO& mock_io, MockStatistics& mock_statictics) {
+    EXPECT_CALL(mock_statictics, StartTimer_t(StatisticEntity::kNetwork));
+
+    EXPECT_CALL(mock_io, ReceiveWithTimeout_t(_, _, _, _, _))
+    .WillOnce(
+        DoAll(SetArgPointee<4>(new hidra2::IOError("", hidra2::IOErrorType::kUnknownIOError)),
+              Return(0))
+    );
+}
+
+MockRequest* MockWaitRequest(const MockRequestFactory& mock_factory) {
+    GenericNetworkRequestHeader header;
+    header.data_size = 1;
+    auto request = new MockRequest{header, 1};
+    EXPECT_CALL(mock_factory, GenerateRequest_t(_, _, _)).WillOnce(
+        Return(request)
+    );
+    return request;
+}
+
+TEST_F(ConnectionTests, FillsStatistics) {
+    InSequence sequence;
+
+    EXPECT_CALL(mock_statictics, StartTimer_t(StatisticEntity::kNetwork));
+
+    EXPECT_CALL(mock_io, ReceiveWithTimeout_t(_, _, _, _, _));
+
+    EXPECT_CALL(mock_statictics, StopTimer_t());
+
+    auto request = MockWaitRequest(mock_factory);
+
+    EXPECT_CALL(*request, Handle_t()).WillOnce(
+        Return(nullptr)
+    );
+
+
+    EXPECT_CALL(mock_io, Send_t(_, _, _, _)).WillOnce(
+        DoAll(SetArgPointee<3>(nullptr),
+              Return(0)
+             ));
+
+
+    EXPECT_CALL(mock_statictics, IncreaseRequestCounter_t());
+
+    EXPECT_CALL(mock_statictics, IncreaseRequestDataVolume_t(1 + sizeof(hidra2::GenericNetworkRequestHeader) +
+                sizeof(hidra2::GenericNetworkResponse)));
+
+
+    EXPECT_CALL(mock_statictics, SendIfNeeded_t());
+
+    MockExitCycle(mock_io, mock_statictics);
+
+    connection.Listen();
+
+    std::this_thread::sleep_for(std::chrono::milliseconds(1));
+
+}
+
+
 }
diff --git a/receiver/unittests/test_request.cpp b/receiver/unittests/test_request.cpp
index 9b263826fe308534afd922688f6a64cf2311619e..1d1e175279e9c6f3a2588b6a014c48ad22f9969c 100644
--- a/receiver/unittests/test_request.cpp
+++ b/receiver/unittests/test_request.cpp
@@ -7,6 +7,9 @@
 #include "../src/request_handler.h"
 #include "../src/request_handler_file_write.h"
 
+#include "mock_statistics.h"
+#include "mock_receiver_config.h"
+
 using ::testing::Test;
 using ::testing::Return;
 using ::testing::_;
@@ -31,6 +34,13 @@ using ::hidra2::Opcode;
 using ::hidra2::Connection;
 using ::hidra2::MockIO;
 using hidra2::Request;
+using hidra2::MockStatistics;
+
+using hidra2::StatisticEntity;
+
+using hidra2::ReceiverConfig;
+using hidra2::SetReceiverConfig;
+
 
 namespace {
 
@@ -40,6 +50,10 @@ class MockReqestHandler : public hidra2::RequestHandler {
         return Error{ProcessRequest_t(request)};
     }
 
+    StatisticEntity GetStatisticEntity() const override {
+        return StatisticEntity::kDisk;
+    }
+
     MOCK_CONST_METHOD1(ProcessRequest_t, ErrorInterface * (const Request& request));
 
 };
@@ -49,7 +63,10 @@ class FactoryTests : public Test {
     hidra2::RequestFactory factory;
     Error err{nullptr};
     GenericNetworkRequestHeader generic_request_header;
+    ReceiverConfig config;
     void SetUp() override {
+        config.write_to_disk = true;
+        SetReceiverConfig(config);
     }
     void TearDown() override {
     }
@@ -71,6 +88,15 @@ TEST_F(FactoryTests, ReturnsDataRequestOnkNetOpcodeSendDataCode) {
     ASSERT_THAT(dynamic_cast<const hidra2::RequestHandlerFileWrite*>(request->GetListHandlers()[0]), Ne(nullptr));
 }
 
+TEST_F(FactoryTests, DoNotAddWriterIfNotWanted) {
+    generic_request_header.op_code = hidra2::Opcode::kNetOpcodeSendData;
+    config.write_to_disk = false;
+    SetReceiverConfig(config);
+
+    auto request = factory.GenerateRequest(generic_request_header, 1, &err);
+    ASSERT_THAT(err, Eq(nullptr));
+    ASSERT_THAT(request->GetListHandlers().size(), Eq(0));
+}
 
 
 
@@ -82,7 +108,10 @@ class RequestTests : public Test {
     uint64_t data_id_{15};
     std::unique_ptr<Request> request;
     NiceMock<MockIO> mock_io;
+    NiceMock<MockStatistics> mock_statistics;
+    std::unique_ptr<hidra2::Statistics>  stat;
     void SetUp() override {
+        stat = std::unique_ptr<hidra2::Statistics> {&mock_statistics};
         generic_request_header.data_size = data_size_;
         generic_request_header.data_id = data_id_;
         request.reset(new Request{generic_request_header, socket_fd_});
@@ -94,6 +123,7 @@ class RequestTests : public Test {
     }
     void TearDown() override {
         request->io__.release();
+        stat.release();
     }
 
 };
@@ -106,7 +136,7 @@ TEST_F(RequestTests, HandleDoesNotReceiveEmptyData) {
 
     EXPECT_CALL(mock_io, Receive_t(_, _, _, _)).Times(0);
 
-    auto err = request->Handle();
+    auto err = request->Handle(&stat);
 
     ASSERT_THAT(err, Eq(nullptr));
 }
@@ -117,15 +147,35 @@ TEST_F(RequestTests, HandleReturnsErrorOnDataReceive) {
               Return(0)
              ));
 
-    auto err = request->Handle();
+    auto err = request->Handle(&stat);
     ASSERT_THAT(err, Eq(hidra2::IOErrorTemplates::kReadError));
 }
 
 
+
+TEST_F(RequestTests, HandleMeasuresTimeOnDataReceive) {
+
+    EXPECT_CALL(mock_statistics, StartTimer_t(hidra2::StatisticEntity::kNetwork));
+
+    EXPECT_CALL(mock_io, Receive_t(socket_fd_, _, data_size_, _)).WillOnce(
+        DoAll(SetArgPointee<3>(nullptr),
+              Return(0)
+             ));
+
+    EXPECT_CALL(mock_statistics, StopTimer_t());
+
+    request->Handle(&stat);
+}
+
+
+
+
 TEST_F(RequestTests, HandleProcessesRequests) {
 
     MockReqestHandler mock_request_handler;
 
+    EXPECT_CALL(mock_statistics, StartTimer_t(hidra2::StatisticEntity::kNetwork));
+
     EXPECT_CALL(mock_request_handler, ProcessRequest_t(_)).WillOnce(
         Return(nullptr)
     ).WillOnce(
@@ -135,7 +185,12 @@ TEST_F(RequestTests, HandleProcessesRequests) {
     request->AddHandler(&mock_request_handler);
     request->AddHandler(&mock_request_handler);
 
-    auto err = request->Handle();
+    EXPECT_CALL(mock_statistics, StartTimer_t(hidra2::StatisticEntity::kDisk)).Times(2);
+
+    EXPECT_CALL(mock_statistics, StopTimer_t()).Times(2);
+
+
+    auto err = request->Handle(&stat);
 
     ASSERT_THAT(err, Eq(hidra2::IOErrorTemplates::kUnknownIOError));
 }
@@ -148,7 +203,7 @@ TEST_F(RequestTests, DataIsNullAtInit) {
 
 TEST_F(RequestTests, GetDataIsNotNullptr) {
 
-    request->Handle();
+    request->Handle(&stat);
     auto& data = request->GetData();
 
 
diff --git a/receiver/unittests/test_request_handler_file_write.cpp b/receiver/unittests/test_request_handler_file_write.cpp
index 7c3dc1d09a1f0cc8396cd961e1c5ac7de93a986d..5640ce4539a64d0dc036a2f5731e5c91bb06804e 100644
--- a/receiver/unittests/test_request_handler_file_write.cpp
+++ b/receiver/unittests/test_request_handler_file_write.cpp
@@ -62,6 +62,12 @@ class FileWriteHandlerTests : public Test {
 
 };
 
+TEST_F(FileWriteHandlerTests, CheckStatisticEntity) {
+    auto entity = handler.GetStatisticEntity();
+    ASSERT_THAT(entity, Eq(hidra2::StatisticEntity::kDisk));
+}
+
+
 TEST_F(FileWriteHandlerTests, ErrorWhenZeroFileSize) {
     EXPECT_CALL(*mock_request, GetDataSize())
     .WillOnce(Return(0))
diff --git a/receiver/unittests/test_statistics.cpp b/receiver/unittests/test_statistics.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..13115b7b7eb2b9d47f1ab9b5524453ebf18f42d3
--- /dev/null
+++ b/receiver/unittests/test_statistics.cpp
@@ -0,0 +1,213 @@
+#include <gtest/gtest.h>
+#include <gmock/gmock.h>
+#include <thread>
+
+#include "../src/statistics.h"
+#include "../src/statistics_sender.h"
+#include "../src/statistics_sender_influx_db.h"
+
+using ::testing::Test;
+using ::testing::Gt;
+using ::testing::Ge;
+using ::testing::Le;
+using ::testing::Eq;
+using ::testing::Ne;
+using ::testing::Ref;
+using ::testing::_;
+
+using hidra2::Statistics;
+using hidra2::StatisticEntity;
+using hidra2::StatisticsSender;
+using hidra2::StatisticsSenderInfluxDb;
+using hidra2::StatisticsToSend;
+
+
+
+namespace {
+
+
+TEST(StatisticTestsConstructor, Constructor) {
+    Statistics statistics;
+    ASSERT_THAT(dynamic_cast<hidra2::StatisticsSenderInfluxDb*>(statistics.statistics_sender__.get()), Ne(nullptr));
+}
+
+
+class MockStatisticsSender: public StatisticsSender {
+  public:
+    void SendStatistics(const StatisticsToSend& statistics) const noexcept override {
+        SendStatistics_t(statistics);
+    }
+    MOCK_CONST_METHOD1(SendStatistics_t, void (const StatisticsToSend&));
+};
+
+class StatisticTests : public Test {
+  public:
+    Statistics statistics{0};
+    void TestTimer(const StatisticEntity& entity);
+    MockStatisticsSender mock_statistics_sender;
+    void SetUp() override {
+        statistics.statistics_sender__.reset(&mock_statistics_sender);
+    }
+    void TearDown() override {
+        statistics.statistics_sender__.release();
+    }
+    StatisticsToSend ExtractStat();
+};
+
+
+ACTION_P(SaveArg1ToSendStat, value) {
+    auto resp =  static_cast<const StatisticsToSend&>(arg0);
+    value->n_requests = resp.n_requests;
+    value->data_volume = resp.data_volume;
+    value->elapsed_ms = resp.elapsed_ms;
+    for (int i = 0; i < hidra2::kNStatisticEntities; i++) {
+        value->entity_shares[i] = resp.entity_shares[i];
+    }
+
+}
+
+
+StatisticsToSend StatisticTests::ExtractStat() {
+    std::this_thread::sleep_for(std::chrono::milliseconds(10));
+    StatisticsToSend stat;
+    stat.elapsed_ms = 0;
+    stat.n_requests = 0;
+    stat.data_volume = 0;
+    for (int i = 0; i < hidra2::kNStatisticEntities; i++) {
+        stat.entity_shares[i] = 0.0;
+    }
+
+    EXPECT_CALL(mock_statistics_sender, SendStatistics_t(_)).
+    WillOnce(SaveArg1ToSendStat(&stat));
+
+    statistics.SendIfNeeded();
+    return stat;
+}
+
+TEST_F(StatisticTests, IncreaseRequestCounter) {
+    statistics.IncreaseRequestCounter();
+
+    auto stat = ExtractStat();
+
+    ASSERT_THAT(stat.n_requests, Eq(1));
+}
+
+TEST_F(StatisticTests, StatisticsResetAfterSend) {
+    statistics.IncreaseRequestCounter();
+
+    ExtractStat();
+    auto stat = ExtractStat();
+
+    ASSERT_THAT(stat.n_requests, Eq(0));
+}
+
+
+TEST_F(StatisticTests, ElapsedTime) {
+
+    auto stat = ExtractStat();
+
+    ASSERT_THAT(stat.elapsed_ms, Ge(1));
+}
+
+
+
+TEST_F(StatisticTests, RequestCounterZeroAtInit) {
+    auto stat = ExtractStat();
+
+    ASSERT_THAT(stat.n_requests, Eq(0));
+}
+
+TEST_F(StatisticTests, GetDataVolume) {
+    statistics.IncreaseRequestDataVolume(100);
+
+    auto stat = ExtractStat();
+
+    ASSERT_THAT(stat.data_volume, Eq(100));
+}
+
+TEST_F(StatisticTests, DataVolumeZeroAtInit) {
+    auto stat = ExtractStat();
+
+    ASSERT_THAT(stat.data_volume, Eq(0));
+}
+
+void StatisticTests::TestTimer(const StatisticEntity& entity) {
+    statistics.StartTimer(entity);
+    std::this_thread::sleep_for(std::chrono::milliseconds(10));
+
+    statistics.StopTimer();
+
+    auto stat = ExtractStat();
+
+    ASSERT_THAT(stat.entity_shares[entity], Ge(0.4));
+
+}
+
+TEST_F(StatisticTests, TimerForDatabase) {
+    TestTimer(StatisticEntity::kDatabase);
+}
+
+TEST_F(StatisticTests, TimerForNetwork) {
+    TestTimer(StatisticEntity::kNetwork);
+}
+
+TEST_F(StatisticTests, TimerForDisk) {
+    TestTimer(StatisticEntity::kDisk);
+}
+
+TEST_F(StatisticTests, TimerForAll) {
+    statistics.StartTimer(StatisticEntity::kDatabase);
+    std::this_thread::sleep_for(std::chrono::milliseconds(20));
+    statistics.StopTimer();
+    statistics.StartTimer(StatisticEntity::kNetwork);
+    std::this_thread::sleep_for(std::chrono::milliseconds(30));
+    statistics.StopTimer();
+
+    statistics.StartTimer(StatisticEntity::kDisk);
+    std::this_thread::sleep_for(std::chrono::milliseconds(40));
+    statistics.StopTimer();
+
+    auto stat = ExtractStat();
+
+    ASSERT_THAT(stat.entity_shares[StatisticEntity::kDatabase], Ge(0.15));
+    ASSERT_THAT(stat.entity_shares[StatisticEntity::kDatabase], Le(0.25));
+
+    ASSERT_THAT(stat.entity_shares[StatisticEntity::kNetwork], Ge(0.25));
+    ASSERT_THAT(stat.entity_shares[StatisticEntity::kNetwork], Le(0.35));
+
+    ASSERT_THAT(stat.entity_shares[StatisticEntity::kDisk], Ge(0.35));
+    ASSERT_THAT(stat.entity_shares[StatisticEntity::kDisk], Le(0.45));
+}
+
+
+TEST_F(StatisticTests, SendStaticsDoesCallsSender) {
+    statistics.SetWriteInterval(1000);
+
+    EXPECT_CALL(mock_statistics_sender, SendStatistics_t(_)).Times(0);
+
+    statistics.SendIfNeeded();
+}
+
+
+TEST_F(StatisticTests, StatisticsSend) {
+    statistics.IncreaseRequestCounter();
+
+    StatisticsToSend stat;
+    stat.elapsed_ms = 0;
+    stat.n_requests = 0;
+    stat.data_volume = 0;
+    for (int i = 0; i < hidra2::kNStatisticEntities; i++) {
+        stat.entity_shares[i] = 0.0;
+    }
+
+    EXPECT_CALL(mock_statistics_sender, SendStatistics_t(_)).
+    WillOnce(SaveArg1ToSendStat(&stat));
+
+    statistics.Send();
+    std::cout << stat.elapsed_ms << std::endl;
+
+    ASSERT_THAT(stat.elapsed_ms, Ge(1));
+}
+
+
+}
diff --git a/receiver/unittests/test_statistics_sender_influx_db.cpp b/receiver/unittests/test_statistics_sender_influx_db.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0324253dd8c8773d06404abb209ba0f8b58eb403
--- /dev/null
+++ b/receiver/unittests/test_statistics_sender_influx_db.cpp
@@ -0,0 +1,83 @@
+#include <gtest/gtest.h>
+#include <gmock/gmock.h>
+#include <unittests/MockIO.h>
+
+#include "../src/statistics_sender_influx_db.h"
+#include "../src/statistics_sender.h"
+#include "http_client/curl_http_client.h"
+#include "unittests/MockHttpClient.h"
+#include "../src/statistics.h"
+
+#include "../src/receiver_config.h"
+#include "mock_receiver_config.h"
+
+
+using ::testing::Test;
+using ::testing::Return;
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::SetArgReferee;
+using ::testing::Gt;
+using ::testing::Eq;
+using ::testing::Ne;
+using ::testing::Mock;
+using ::testing::NiceMock;
+using ::testing::SaveArg;
+using ::testing::SaveArgPointee;
+using ::testing::InSequence;
+using ::testing::SetArgPointee;
+
+using hidra2::StatisticsSenderInfluxDb;
+using hidra2::MockHttpClient;
+using hidra2::StatisticsToSend;
+using hidra2::ReceiverConfig;
+using hidra2::SetReceiverConfig;
+
+namespace {
+
+TEST(SenderInfluxDb, Constructor) {
+    StatisticsSenderInfluxDb sender;
+    ASSERT_THAT(dynamic_cast<hidra2::CurlHttpClient*>(sender.httpclient__.get()), Ne(nullptr));
+}
+
+
+class SenderInfluxDbTests : public Test {
+  public:
+    StatisticsSenderInfluxDb sender;
+    MockHttpClient mock_http_client;
+    void SetUp() override {
+        sender.httpclient__.reset(&mock_http_client);
+    }
+    void TearDown() override {
+        sender.httpclient__.release();
+    }
+};
+
+
+TEST_F(SenderInfluxDbTests, SendStatisticsCallsPost) {
+    StatisticsToSend statistics;
+    statistics.n_requests = 4;
+    statistics.entity_shares[hidra2::StatisticEntity::kDisk] = 0.6;
+    statistics.entity_shares[hidra2::StatisticEntity::kNetwork] = 0.3;
+    statistics.entity_shares[hidra2::StatisticEntity::kDatabase] = 0.1;
+    statistics.elapsed_ms = 100;
+    statistics.data_volume = 1000;
+
+    ReceiverConfig config;
+    config.monitor_db_uri = "test_uri";
+    config.monitor_db_name = "test_name";
+    SetReceiverConfig(config);
+
+    std::string expect_string = "statistics,receiver=1,connection=1 elapsed_ms=100,data_volume=1000,"
+                                "n_requests=4,db_share=0.1000,network_share=0.3000,disk_share=0.6000";
+    EXPECT_CALL(mock_http_client, Post_t("test_uri/write?db=test_name", expect_string, _, _)).
+    WillOnce(
+        DoAll(SetArgPointee<3>(new hidra2::IOError("Test Read Error", hidra2::IOErrorType::kReadError)),
+              Return("")
+             ));
+
+    sender.SendStatistics(statistics);
+}
+
+
+}
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index fed871cd51912dfa1d57fd8626413cf56a76cd9b..3e431d34cd6ac7e59e99566ff0151bea49a95dbf 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -1,23 +1,4 @@
-CMAKE_MINIMUM_REQUIRED(VERSION 3.7) # needed for fixtures
-find_package(Threads REQUIRED)
+add_subdirectory(automatic)
 
-add_subdirectory(common/cpp)
-add_subdirectory(system_io)
-
-add_subdirectory(json_parser)
-
-if(BUILD_MONGODB_CLIENTLIB)
-    add_subdirectory(mongo_db)
-endif()
-
-
-if(BUILD_BROKER)
-    add_subdirectory(broker)
-endif()
-
-
-add_subdirectory(worker)
-
-add_subdirectory(producer_receiver)
 
 
diff --git a/tests/automatic/CMakeLists.txt b/tests/automatic/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..b0a661944ef8fd479b8d5aa8d4b4c69139995420
--- /dev/null
+++ b/tests/automatic/CMakeLists.txt
@@ -0,0 +1,23 @@
+CMAKE_MINIMUM_REQUIRED(VERSION 3.7) # needed for fixtures
+find_package(Threads)
+
+add_subdirectory(common/cpp)
+add_subdirectory(system_io)
+
+add_subdirectory(json_parser)
+
+if(BUILD_MONGODB_CLIENTLIB)
+    add_subdirectory(mongo_db)
+endif()
+
+
+if(BUILD_BROKER)
+    add_subdirectory(broker)
+endif()
+
+
+add_subdirectory(worker)
+
+add_subdirectory(curl_http_client)
+
+add_subdirectory(producer_receiver)
\ No newline at end of file
diff --git a/tests/broker/CMakeLists.txt b/tests/automatic/broker/CMakeLists.txt
similarity index 52%
rename from tests/broker/CMakeLists.txt
rename to tests/automatic/broker/CMakeLists.txt
index c3741747744a36054595915d91f42ecd5eb96943..6f1685c7d12bf036a9fa6f126b357fd962d2b954 100644
--- a/tests/broker/CMakeLists.txt
+++ b/tests/automatic/broker/CMakeLists.txt
@@ -1,3 +1,6 @@
 add_subdirectory(get_next)
 add_subdirectory(read_config)
 
+if (UNIX)
+add_subdirectory(check_monitoring)
+endif()
\ No newline at end of file
diff --git a/tests/automatic/broker/check_monitoring/CMakeLists.txt b/tests/automatic/broker/check_monitoring/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..19c68e39b9dc264e6ef541df207ad418a27af8f7
--- /dev/null
+++ b/tests/automatic/broker/check_monitoring/CMakeLists.txt
@@ -0,0 +1,8 @@
+set(TARGET_NAME hidra2-broker)
+
+################################
+# Testing
+################################
+configure_file(${CMAKE_SOURCE_DIR}/tests/automatic/settings/broker_settings.json settings.json COPYONLY)
+add_script_test("${TARGET_NAME}-monitoring" "$<TARGET_PROPERTY:${TARGET_NAME},EXENAME>" nomem
+        )
diff --git a/tests/automatic/broker/check_monitoring/check_linux.sh b/tests/automatic/broker/check_monitoring/check_linux.sh
new file mode 100644
index 0000000000000000000000000000000000000000..9af76490853e00d86cc70d3118295d9a5faba76f
--- /dev/null
+++ b/tests/automatic/broker/check_monitoring/check_linux.sh
@@ -0,0 +1,31 @@
+#!/usr/bin/env bash
+
+database_name=db_test
+
+set -e
+
+trap Cleanup EXIT
+
+Cleanup() {
+	echo cleanup
+	influx -execute "drop database ${database_name}"
+	kill -9 $brokerid
+}
+
+influx -execute "create database ${database_name}"
+
+$@ settings.json &
+
+sleep 0.3
+
+brokerid=`echo $!`
+
+for i in `seq 1 50`;
+do
+    curl --silent 127.0.0.1:5005/database/data/next >/dev/null 2>&1 &
+done
+
+
+sleep 2
+
+influx -execute "select sum(rate) from RequestsRate" -database=${database_name} -format=json | jq .results[0].series[0].values[0][1] | grep 50
diff --git a/tests/broker/get_next/CMakeLists.txt b/tests/automatic/broker/get_next/CMakeLists.txt
similarity index 66%
rename from tests/broker/get_next/CMakeLists.txt
rename to tests/automatic/broker/get_next/CMakeLists.txt
index 4eb27c1142b81b97dcc2c6326c739b2ca1fecaac..8b5f3b6f02b77436f882d1aecebf94026cc8222a 100644
--- a/tests/broker/get_next/CMakeLists.txt
+++ b/tests/automatic/broker/get_next/CMakeLists.txt
@@ -3,6 +3,6 @@ set(TARGET_NAME hidra2-broker)
 ################################
 # Testing
 ################################
-configure_file(${CMAKE_SOURCE_DIR}/tests/settings/broker_settings.json settings.json COPYONLY)
+configure_file(${CMAKE_SOURCE_DIR}/tests/automatic/settings/broker_settings.json settings.json COPYONLY)
 add_script_test("${TARGET_NAME}-getnext" "$<TARGET_PROPERTY:${TARGET_NAME},EXENAME>" nomem
         )
diff --git a/tests/broker/get_next/check_linux.sh b/tests/automatic/broker/get_next/check_linux.sh
similarity index 100%
rename from tests/broker/get_next/check_linux.sh
rename to tests/automatic/broker/get_next/check_linux.sh
diff --git a/tests/broker/get_next/check_windows.bat b/tests/automatic/broker/get_next/check_windows.bat
similarity index 100%
rename from tests/broker/get_next/check_windows.bat
rename to tests/automatic/broker/get_next/check_windows.bat
diff --git a/tests/broker/read_config/CMakeLists.txt b/tests/automatic/broker/read_config/CMakeLists.txt
similarity index 71%
rename from tests/broker/read_config/CMakeLists.txt
rename to tests/automatic/broker/read_config/CMakeLists.txt
index a8949ad818264d362b46d90222f3ad5348f623a4..76f6467cfd21c94965a03b39d446a95e53d2ac4c 100644
--- a/tests/broker/read_config/CMakeLists.txt
+++ b/tests/automatic/broker/read_config/CMakeLists.txt
@@ -3,7 +3,7 @@ set(TARGET_NAME hidra2-broker)
 ################################
 # Testing
 ################################
-configure_file(${CMAKE_SOURCE_DIR}/tests/settings/broker_settings.json settings_good.json COPYONLY)
+configure_file(${CMAKE_SOURCE_DIR}/tests/automatic/settings/broker_settings.json settings_good.json COPYONLY)
 configure_file(settings_bad.json settings_bad.json COPYONLY)
 add_script_test("${TARGET_NAME}-readconfig" "$<TARGET_PROPERTY:${TARGET_NAME},EXENAME>" nomem
         )
diff --git a/tests/broker/read_config/check_linux.sh b/tests/automatic/broker/read_config/check_linux.sh
similarity index 85%
rename from tests/broker/read_config/check_linux.sh
rename to tests/automatic/broker/read_config/check_linux.sh
index e491171a3c8d00802b7e1beb55c77acc3bb3040a..5b9e9c78c8b9c5c814b907ea08dfd4c745043e73 100644
--- a/tests/broker/read_config/check_linux.sh
+++ b/tests/automatic/broker/read_config/check_linux.sh
@@ -9,7 +9,7 @@ brokerid=`echo $!`
 kill -9 $brokerid
 
 # check if gives error with bad json file
-$@ settings_bad.json 2>&1 | grep "invalid"
+$@ settings_bad.json 2>&1 | grep "not set"
 
 # check if gives error non-existing file
 $@ settings_notexist.json 2>&1 | grep "no such"
diff --git a/tests/broker/read_config/check_windows.bat b/tests/automatic/broker/read_config/check_windows.bat
similarity index 77%
rename from tests/broker/read_config/check_windows.bat
rename to tests/automatic/broker/read_config/check_windows.bat
index 76f042ab8dd9116ef59851b116dc7361313d7c99..e78f9380f8728c8dbf81bede444dc009d428d6e5 100644
--- a/tests/broker/read_config/check_windows.bat
+++ b/tests/automatic/broker/read_config/check_windows.bat
@@ -6,7 +6,7 @@ start /B "" "%full_name%" settings_good.json
 ping 1.0.0.0 -n 1 -w 100 > nul
 Taskkill /IM "%short_name%" /F
 
-%full_name% settings_bad.json 2>&1 | findstr invalid  || goto :error
+%full_name% settings_bad.json 2>&1 | findstr /c:"not set"  || goto :error
 
 %full_name% settings_notexist.json 2>&1| findstr cannot  || goto :error
 
diff --git a/tests/automatic/broker/read_config/settings_bad.json b/tests/automatic/broker/read_config/settings_bad.json
new file mode 100644
index 0000000000000000000000000000000000000000..88157fb1b06da72f529ea9f42a808a58e91533cb
--- /dev/null
+++ b/tests/automatic/broker/read_config/settings_bad.json
@@ -0,0 +1,4 @@
+{
+  "BrokerDbAddres":"127.0.0.1:27017",
+  "port":12
+}
\ No newline at end of file
diff --git a/tests/common/cpp/CMakeLists.txt b/tests/automatic/common/cpp/CMakeLists.txt
similarity index 100%
rename from tests/common/cpp/CMakeLists.txt
rename to tests/automatic/common/cpp/CMakeLists.txt
diff --git a/tests/common/cpp/include/testing.h b/tests/automatic/common/cpp/include/testing.h
similarity index 100%
rename from tests/common/cpp/include/testing.h
rename to tests/automatic/common/cpp/include/testing.h
diff --git a/tests/common/cpp/src/testing.cpp b/tests/automatic/common/cpp/src/testing.cpp
similarity index 100%
rename from tests/common/cpp/src/testing.cpp
rename to tests/automatic/common/cpp/src/testing.cpp
diff --git a/tests/automatic/curl_http_client/CMakeLists.txt b/tests/automatic/curl_http_client/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..c704851020a6b314c6172cedde15c2e58cbe489c
--- /dev/null
+++ b/tests/automatic/curl_http_client/CMakeLists.txt
@@ -0,0 +1 @@
+add_subdirectory(curl_http_client_command)
diff --git a/tests/automatic/curl_http_client/curl_http_client_command/CMakeLists.txt b/tests/automatic/curl_http_client/curl_http_client_command/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..503b9e91b51343704ccd80b856da664d5c99aab7
--- /dev/null
+++ b/tests/automatic/curl_http_client/curl_http_client_command/CMakeLists.txt
@@ -0,0 +1,27 @@
+set(TARGET_NAME curl_httpclient_command)
+set(SOURCE_FILES curl_httpclient_command.cpp)
+
+
+################################
+# Executable and link
+################################
+add_executable(${TARGET_NAME} ${SOURCE_FILES})
+target_link_libraries(${TARGET_NAME} test_common hidra2-worker)
+
+#set_target_properties(${TARGET_NAME} PROPERTIES LINKER_LANGUAGE CXX)
+#if (CMAKE_COMPILER_IS_GNUCXX)
+#    set_target_properties(${TARGET_NAME} PROPERTIES LINK_FLAGS_DEBUG "--coverage")
+#endif()
+
+
+################################
+# Testing
+################################
+
+#add_test_setup_cleanup(${TARGET_NAME})
+add_integration_test(${TARGET_NAME}  get_google "GET google.com moved 302")
+add_integration_test(${TARGET_NAME}  get_badaddress "GET google.com/badaddress found 404")
+add_integration_test(${TARGET_NAME}  get_badaddress2 "GET 111 clienterror 404")
+add_integration_test(${TARGET_NAME}  post "POST httpbin.org/post testdata 200")
+add_integration_test(${TARGET_NAME}  post_badaddress "POST google.com/badaddress found 404")
+add_integration_test(${TARGET_NAME}  post_badaddress2 "POST 111 clienterror 404")
\ No newline at end of file
diff --git a/tests/worker/curl_http_client_get/curl_httpclient_get.cpp b/tests/automatic/curl_http_client/curl_http_client_command/curl_httpclient_command.cpp
similarity index 66%
rename from tests/worker/curl_http_client_get/curl_httpclient_get.cpp
rename to tests/automatic/curl_http_client/curl_http_client_command/curl_httpclient_command.cpp
index 478839271d364ce2bcfa98cc00de5ac4f602707d..f049bc15573a7e502e53719fa4128944c3848a87 100644
--- a/tests/worker/curl_http_client_get/curl_httpclient_get.cpp
+++ b/tests/automatic/curl_http_client/curl_http_client_command/curl_httpclient_command.cpp
@@ -8,20 +8,22 @@ using hidra2::M_AssertEq;
 using hidra2::M_AssertContains;
 
 struct Args {
+    std::string command;
     std::string uri;
     int code;
     std::string answer;
 };
 
 Args GetArgs(int argc, char* argv[]) {
-    if (argc != 4) {
+    if (argc != 5) {
         std::cout << "Wrong number of arguments" << std::endl;
         exit(EXIT_FAILURE);
     }
-    std::string uri{argv[1]};
-    std::string answer {argv[2]};
-    int code = std::stoi(argv[3]);
-    return Args{uri, code, answer};
+    std::string command{argv[1]};
+    std::string uri{argv[2]};
+    std::string answer {argv[3]};
+    int code = std::stoi(argv[4]);
+    return Args{command, uri, code, answer};
 }
 
 
@@ -34,7 +36,12 @@ int main(int argc, char* argv[]) {
     auto server_broker = static_cast<hidra2::ServerDataBroker*>(broker.get());
 
     hidra2::HttpCode code;
-    auto response = server_broker->httpclient__->Get(args.uri, &code, &err);
+    std::string response;
+    if (args.command == "GET") {
+        response = server_broker->httpclient__->Get(args.uri, &code, &err);
+    } else if  (args.command == "POST") {
+        response = server_broker->httpclient__->Post(args.uri, "testdata", &code, &err);
+    }
 
     if (err != nullptr) {
         M_AssertEq("clienterror", args.answer);
diff --git a/tests/json_parser/CMakeLists.txt b/tests/automatic/json_parser/CMakeLists.txt
similarity index 100%
rename from tests/json_parser/CMakeLists.txt
rename to tests/automatic/json_parser/CMakeLists.txt
diff --git a/tests/json_parser/parse_config_file/CMakeLists.txt b/tests/automatic/json_parser/parse_config_file/CMakeLists.txt
similarity index 100%
rename from tests/json_parser/parse_config_file/CMakeLists.txt
rename to tests/automatic/json_parser/parse_config_file/CMakeLists.txt
diff --git a/tests/json_parser/parse_config_file/config.json b/tests/automatic/json_parser/parse_config_file/config.json
similarity index 100%
rename from tests/json_parser/parse_config_file/config.json
rename to tests/automatic/json_parser/parse_config_file/config.json
diff --git a/tests/json_parser/parse_config_file/config_bad.json b/tests/automatic/json_parser/parse_config_file/config_bad.json
similarity index 100%
rename from tests/json_parser/parse_config_file/config_bad.json
rename to tests/automatic/json_parser/parse_config_file/config_bad.json
diff --git a/tests/json_parser/parse_config_file/parse_config_file.cpp b/tests/automatic/json_parser/parse_config_file/parse_config_file.cpp
similarity index 97%
rename from tests/json_parser/parse_config_file/parse_config_file.cpp
rename to tests/automatic/json_parser/parse_config_file/parse_config_file.cpp
index c185240553dc07c5786fcb8565ce38b3b0dc57f7..4fb391b756cf2b45059808fc330a9e3100ca496c 100644
--- a/tests/json_parser/parse_config_file/parse_config_file.cpp
+++ b/tests/automatic/json_parser/parse_config_file/parse_config_file.cpp
@@ -39,7 +39,8 @@ void AssertSettings(const Settings& settings) {
 }
 
 Settings Parse(const std::string& fname, Error* err) {
-    hidra2::JsonParser parser(fname, true);
+
+    hidra2::JsonFileParser parser(fname);
 
     Settings settings;
 
diff --git a/tests/mongo_db/CMakeLists.txt b/tests/automatic/mongo_db/CMakeLists.txt
similarity index 100%
rename from tests/mongo_db/CMakeLists.txt
rename to tests/automatic/mongo_db/CMakeLists.txt
diff --git a/tests/mongo_db/connect/CMakeLists.txt b/tests/automatic/mongo_db/connect/CMakeLists.txt
similarity index 100%
rename from tests/mongo_db/connect/CMakeLists.txt
rename to tests/automatic/mongo_db/connect/CMakeLists.txt
diff --git a/tests/mongo_db/connect/connect_mongodb.cpp b/tests/automatic/mongo_db/connect/connect_mongodb.cpp
similarity index 100%
rename from tests/mongo_db/connect/connect_mongodb.cpp
rename to tests/automatic/mongo_db/connect/connect_mongodb.cpp
diff --git a/tests/mongo_db/insert/CMakeLists.txt b/tests/automatic/mongo_db/insert/CMakeLists.txt
similarity index 100%
rename from tests/mongo_db/insert/CMakeLists.txt
rename to tests/automatic/mongo_db/insert/CMakeLists.txt
diff --git a/tests/mongo_db/insert/cleanup_linux.sh b/tests/automatic/mongo_db/insert/cleanup_linux.sh
similarity index 100%
rename from tests/mongo_db/insert/cleanup_linux.sh
rename to tests/automatic/mongo_db/insert/cleanup_linux.sh
diff --git a/tests/mongo_db/insert/cleanup_windows.bat b/tests/automatic/mongo_db/insert/cleanup_windows.bat
similarity index 100%
rename from tests/mongo_db/insert/cleanup_windows.bat
rename to tests/automatic/mongo_db/insert/cleanup_windows.bat
diff --git a/tests/mongo_db/insert/insert_mongodb.cpp b/tests/automatic/mongo_db/insert/insert_mongodb.cpp
similarity index 100%
rename from tests/mongo_db/insert/insert_mongodb.cpp
rename to tests/automatic/mongo_db/insert/insert_mongodb.cpp
diff --git a/tests/automatic/producer_receiver/CMakeLists.txt b/tests/automatic/producer_receiver/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..ad36394912da0e110b2707f73e0270452030d41f
--- /dev/null
+++ b/tests/automatic/producer_receiver/CMakeLists.txt
@@ -0,0 +1,4 @@
+add_subdirectory(transfer_single_file)
+if (UNIX)
+    add_subdirectory(check_monitoring)
+endif()
\ No newline at end of file
diff --git a/tests/automatic/producer_receiver/check_monitoring/CMakeLists.txt b/tests/automatic/producer_receiver/check_monitoring/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..298b81ed25e3e9ef14c159b37e04c9a8a000d29f
--- /dev/null
+++ b/tests/automatic/producer_receiver/check_monitoring/CMakeLists.txt
@@ -0,0 +1,8 @@
+set(TARGET_NAME receiver)
+
+################################
+# Testing
+################################
+configure_file(${CMAKE_SOURCE_DIR}/tests/automatic/settings/receiver.json receiver.json COPYONLY)
+add_script_test("${TARGET_NAME}-monitoring" "$<TARGET_FILE:dummy-data-producer> $<TARGET_FILE:receiver-bin>" nomem
+        )
diff --git a/tests/automatic/producer_receiver/check_monitoring/check_linux.sh b/tests/automatic/producer_receiver/check_monitoring/check_linux.sh
new file mode 100644
index 0000000000000000000000000000000000000000..3b4b82ec54b48718f5b00412ca0e9a83ddbbdd0e
--- /dev/null
+++ b/tests/automatic/producer_receiver/check_monitoring/check_linux.sh
@@ -0,0 +1,29 @@
+#!/usr/bin/env bash
+
+database_name=db_test
+
+set -e
+
+trap Cleanup EXIT
+
+Cleanup() {
+	echo cleanup
+	influx -execute "drop database ${database_name}"
+    kill $receiverid
+	rm -rf files
+
+}
+
+influx -execute "create database ${database_name}"
+
+nohup $2 receiver.json &>/dev/null &
+sleep 0.3
+receiverid=`echo $!`
+
+mkdir files
+
+$1 localhost:4200 100 112
+
+sleep 1
+
+influx -execute "select sum(n_requests) from statistics" -database=${database_name} -format=json | jq .results[0].series[0].values[0][1] | grep 112
diff --git a/tests/producer_receiver/transfer_single_file/CMakeLists.txt b/tests/automatic/producer_receiver/transfer_single_file/CMakeLists.txt
similarity index 69%
rename from tests/producer_receiver/transfer_single_file/CMakeLists.txt
rename to tests/automatic/producer_receiver/transfer_single_file/CMakeLists.txt
index 83a9f59580c5427848720b28aea7353d86335d23..f745213b85ef22c898bce3130b1ae24dcfa33a13 100644
--- a/tests/producer_receiver/transfer_single_file/CMakeLists.txt
+++ b/tests/automatic/producer_receiver/transfer_single_file/CMakeLists.txt
@@ -3,4 +3,5 @@ set(TARGET_NAME transfer-single-file)
 ################################
 # Testing
 ################################
+configure_file(${CMAKE_SOURCE_DIR}/tests/automatic/settings/receiver.json receiver.json COPYONLY)
 add_script_test("${TARGET_NAME}" "$<TARGET_FILE:dummy-data-producer> $<TARGET_FILE:receiver-bin>" nomem)
diff --git a/tests/producer_receiver/transfer_single_file/check_linux.sh b/tests/automatic/producer_receiver/transfer_single_file/check_linux.sh
similarity index 66%
rename from tests/producer_receiver/transfer_single_file/check_linux.sh
rename to tests/automatic/producer_receiver/transfer_single_file/check_linux.sh
index 163d4ab08b8144ea133ca0f6f48899b304234e63..d7c69e6a25b6189b0d84220d635a15b386f29449 100644
--- a/tests/producer_receiver/transfer_single_file/check_linux.sh
+++ b/tests/automatic/producer_receiver/transfer_single_file/check_linux.sh
@@ -6,11 +6,11 @@ trap Cleanup EXIT
 
 Cleanup() {
 	echo cleanup
-    kill $receiverid
 	rm -rf files
+    kill $receiverid
 }
 
-nohup $2 &>/dev/null &
+nohup $2 receiver.json &>/dev/null &
 sleep 0.3
 receiverid=`echo $!`
 
@@ -18,4 +18,4 @@ mkdir files
 
 $1 localhost:4200 100 1
 
-ls -ln files/0.bin | awk '{ print $5 }'| grep 100
+ls -ln files/0.bin | awk '{ print $5 }'| grep 102400
diff --git a/tests/producer_receiver/transfer_single_file/check_windows.bat b/tests/automatic/producer_receiver/transfer_single_file/check_windows.bat
similarity index 80%
rename from tests/producer_receiver/transfer_single_file/check_windows.bat
rename to tests/automatic/producer_receiver/transfer_single_file/check_windows.bat
index 9413a82a228200e5a50ed87135e10450ab471714..ee15242cd4354e178f16d6e4059b635cf01b3db4 100644
--- a/tests/producer_receiver/transfer_single_file/check_windows.bat
+++ b/tests/automatic/producer_receiver/transfer_single_file/check_windows.bat
@@ -1,7 +1,7 @@
 set full_recv_name="%2"
 set short_recv_name="%~nx2"
 
-start /B "" "%full_recv_name%"
+start /B "" "%full_recv_name%" receiver.json
 
 ping 1.0.0.0 -n 1 -w 100 > nul
 
@@ -13,7 +13,7 @@ ping 1.0.0.0 -n 1 -w 100 > nul
 
 FOR /F "usebackq" %%A IN ('files\0.bin') DO set size=%%~zA
 
-if %size% NEQ 100 goto :error
+if %size% NEQ 102400 goto :error
 
 goto :clean
 
diff --git a/tests/automatic/settings/broker_settings.json b/tests/automatic/settings/broker_settings.json
new file mode 100644
index 0000000000000000000000000000000000000000..56bf15f52fa9666103ac542903195def198733d7
--- /dev/null
+++ b/tests/automatic/settings/broker_settings.json
@@ -0,0 +1,6 @@
+{
+  "BrokerDbAddress":"127.0.0.1:27017",
+  "MonitorDbAddress": "localhost:8086",
+  "MonitorDbName": "db_test",
+  "port":5005
+}
\ No newline at end of file
diff --git a/tests/automatic/settings/receiver.json b/tests/automatic/settings/receiver.json
new file mode 100644
index 0000000000000000000000000000000000000000..e23a556d1cd5a8c03ce0c32b37b7b73161bfa89d
--- /dev/null
+++ b/tests/automatic/settings/receiver.json
@@ -0,0 +1,6 @@
+{
+  "MonitorDbAddress":"localhost:8086",
+  "MonitorDbName": "db_test",
+  "ListenPort":4200,
+  "WriteToDisk":true
+}
\ No newline at end of file
diff --git a/tests/system_io/CMakeLists.txt b/tests/automatic/system_io/CMakeLists.txt
similarity index 100%
rename from tests/system_io/CMakeLists.txt
rename to tests/automatic/system_io/CMakeLists.txt
diff --git a/tests/system_io/ip_tcp_network/CMakeLists.txt b/tests/automatic/system_io/ip_tcp_network/CMakeLists.txt
similarity index 100%
rename from tests/system_io/ip_tcp_network/CMakeLists.txt
rename to tests/automatic/system_io/ip_tcp_network/CMakeLists.txt
diff --git a/tests/system_io/ip_tcp_network/ip_tcp_network.cpp b/tests/automatic/system_io/ip_tcp_network/ip_tcp_network.cpp
similarity index 100%
rename from tests/system_io/ip_tcp_network/ip_tcp_network.cpp
rename to tests/automatic/system_io/ip_tcp_network/ip_tcp_network.cpp
diff --git a/tests/system_io/read_file_content/CMakeLists.txt b/tests/automatic/system_io/read_file_content/CMakeLists.txt
similarity index 100%
rename from tests/system_io/read_file_content/CMakeLists.txt
rename to tests/automatic/system_io/read_file_content/CMakeLists.txt
diff --git a/tests/system_io/read_file_content/cleanup_linux.sh b/tests/automatic/system_io/read_file_content/cleanup_linux.sh
similarity index 100%
rename from tests/system_io/read_file_content/cleanup_linux.sh
rename to tests/automatic/system_io/read_file_content/cleanup_linux.sh
diff --git a/tests/system_io/read_file_content/cleanup_windows.bat b/tests/automatic/system_io/read_file_content/cleanup_windows.bat
similarity index 100%
rename from tests/system_io/read_file_content/cleanup_windows.bat
rename to tests/automatic/system_io/read_file_content/cleanup_windows.bat
diff --git a/tests/system_io/read_file_content/read_file_content.cpp b/tests/automatic/system_io/read_file_content/read_file_content.cpp
similarity index 100%
rename from tests/system_io/read_file_content/read_file_content.cpp
rename to tests/automatic/system_io/read_file_content/read_file_content.cpp
diff --git a/tests/system_io/read_file_content/setup_linux.sh b/tests/automatic/system_io/read_file_content/setup_linux.sh
similarity index 100%
rename from tests/system_io/read_file_content/setup_linux.sh
rename to tests/automatic/system_io/read_file_content/setup_linux.sh
diff --git a/tests/system_io/read_file_content/setup_windows.bat b/tests/automatic/system_io/read_file_content/setup_windows.bat
similarity index 100%
rename from tests/system_io/read_file_content/setup_windows.bat
rename to tests/automatic/system_io/read_file_content/setup_windows.bat
diff --git a/tests/system_io/read_folder_content/CMakeLists.txt b/tests/automatic/system_io/read_folder_content/CMakeLists.txt
similarity index 100%
rename from tests/system_io/read_folder_content/CMakeLists.txt
rename to tests/automatic/system_io/read_folder_content/CMakeLists.txt
diff --git a/tests/system_io/read_folder_content/cleanup_linux.sh b/tests/automatic/system_io/read_folder_content/cleanup_linux.sh
similarity index 100%
rename from tests/system_io/read_folder_content/cleanup_linux.sh
rename to tests/automatic/system_io/read_folder_content/cleanup_linux.sh
diff --git a/tests/system_io/read_folder_content/cleanup_windows.bat b/tests/automatic/system_io/read_folder_content/cleanup_windows.bat
similarity index 100%
rename from tests/system_io/read_folder_content/cleanup_windows.bat
rename to tests/automatic/system_io/read_folder_content/cleanup_windows.bat
diff --git a/tests/system_io/read_folder_content/read_folder_content.cpp b/tests/automatic/system_io/read_folder_content/read_folder_content.cpp
similarity index 100%
rename from tests/system_io/read_folder_content/read_folder_content.cpp
rename to tests/automatic/system_io/read_folder_content/read_folder_content.cpp
diff --git a/tests/system_io/read_folder_content/setup_linux.sh b/tests/automatic/system_io/read_folder_content/setup_linux.sh
similarity index 100%
rename from tests/system_io/read_folder_content/setup_linux.sh
rename to tests/automatic/system_io/read_folder_content/setup_linux.sh
diff --git a/tests/system_io/read_folder_content/setup_windows.bat b/tests/automatic/system_io/read_folder_content/setup_windows.bat
similarity index 100%
rename from tests/system_io/read_folder_content/setup_windows.bat
rename to tests/automatic/system_io/read_folder_content/setup_windows.bat
diff --git a/tests/system_io/read_string_from_file/CMakeLists.txt b/tests/automatic/system_io/read_string_from_file/CMakeLists.txt
similarity index 100%
rename from tests/system_io/read_string_from_file/CMakeLists.txt
rename to tests/automatic/system_io/read_string_from_file/CMakeLists.txt
diff --git a/tests/system_io/read_string_from_file/cleanup_linux.sh b/tests/automatic/system_io/read_string_from_file/cleanup_linux.sh
similarity index 100%
rename from tests/system_io/read_string_from_file/cleanup_linux.sh
rename to tests/automatic/system_io/read_string_from_file/cleanup_linux.sh
diff --git a/tests/system_io/read_string_from_file/cleanup_windows.bat b/tests/automatic/system_io/read_string_from_file/cleanup_windows.bat
similarity index 100%
rename from tests/system_io/read_string_from_file/cleanup_windows.bat
rename to tests/automatic/system_io/read_string_from_file/cleanup_windows.bat
diff --git a/tests/system_io/read_string_from_file/read_string_from_file.cpp b/tests/automatic/system_io/read_string_from_file/read_string_from_file.cpp
similarity index 100%
rename from tests/system_io/read_string_from_file/read_string_from_file.cpp
rename to tests/automatic/system_io/read_string_from_file/read_string_from_file.cpp
diff --git a/tests/system_io/read_string_from_file/setup_linux.sh b/tests/automatic/system_io/read_string_from_file/setup_linux.sh
similarity index 100%
rename from tests/system_io/read_string_from_file/setup_linux.sh
rename to tests/automatic/system_io/read_string_from_file/setup_linux.sh
diff --git a/tests/system_io/read_string_from_file/setup_windows.bat b/tests/automatic/system_io/read_string_from_file/setup_windows.bat
similarity index 100%
rename from tests/system_io/read_string_from_file/setup_windows.bat
rename to tests/automatic/system_io/read_string_from_file/setup_windows.bat
diff --git a/tests/system_io/resolve_hostname_to_ip/CMakeLists.txt b/tests/automatic/system_io/resolve_hostname_to_ip/CMakeLists.txt
similarity index 100%
rename from tests/system_io/resolve_hostname_to_ip/CMakeLists.txt
rename to tests/automatic/system_io/resolve_hostname_to_ip/CMakeLists.txt
diff --git a/tests/system_io/resolve_hostname_to_ip/resolve_hostname_to_ip.cpp b/tests/automatic/system_io/resolve_hostname_to_ip/resolve_hostname_to_ip.cpp
similarity index 100%
rename from tests/system_io/resolve_hostname_to_ip/resolve_hostname_to_ip.cpp
rename to tests/automatic/system_io/resolve_hostname_to_ip/resolve_hostname_to_ip.cpp
diff --git a/tests/system_io/write_data_to_file/CMakeLists.txt b/tests/automatic/system_io/write_data_to_file/CMakeLists.txt
similarity index 100%
rename from tests/system_io/write_data_to_file/CMakeLists.txt
rename to tests/automatic/system_io/write_data_to_file/CMakeLists.txt
diff --git a/tests/system_io/write_data_to_file/cleanup_linux.sh b/tests/automatic/system_io/write_data_to_file/cleanup_linux.sh
similarity index 100%
rename from tests/system_io/write_data_to_file/cleanup_linux.sh
rename to tests/automatic/system_io/write_data_to_file/cleanup_linux.sh
diff --git a/tests/system_io/write_data_to_file/cleanup_windows.bat b/tests/automatic/system_io/write_data_to_file/cleanup_windows.bat
similarity index 100%
rename from tests/system_io/write_data_to_file/cleanup_windows.bat
rename to tests/automatic/system_io/write_data_to_file/cleanup_windows.bat
diff --git a/tests/system_io/write_data_to_file/setup_linux.sh b/tests/automatic/system_io/write_data_to_file/setup_linux.sh
similarity index 100%
rename from tests/system_io/write_data_to_file/setup_linux.sh
rename to tests/automatic/system_io/write_data_to_file/setup_linux.sh
diff --git a/tests/system_io/write_data_to_file/setup_windows.bat b/tests/automatic/system_io/write_data_to_file/setup_windows.bat
similarity index 100%
rename from tests/system_io/write_data_to_file/setup_windows.bat
rename to tests/automatic/system_io/write_data_to_file/setup_windows.bat
diff --git a/tests/system_io/write_data_to_file/write_data_to_file.cpp b/tests/automatic/system_io/write_data_to_file/write_data_to_file.cpp
similarity index 100%
rename from tests/system_io/write_data_to_file/write_data_to_file.cpp
rename to tests/automatic/system_io/write_data_to_file/write_data_to_file.cpp
diff --git a/tests/worker/CMakeLists.txt b/tests/automatic/worker/CMakeLists.txt
similarity index 86%
rename from tests/worker/CMakeLists.txt
rename to tests/automatic/worker/CMakeLists.txt
index e5145961a17f9444a92f8f471c1cc5380c7f6363..f8a2dd31597411f2a2297fc6436cce592cfbd8e9 100644
--- a/tests/worker/CMakeLists.txt
+++ b/tests/automatic/worker/CMakeLists.txt
@@ -4,8 +4,6 @@ add_subdirectory(next_multithread_folder)
 add_subdirectory(next_multithread_broker)
 add_subdirectory(connect_multithread)
 
-add_subdirectory(curl_http_client_get)
-
 if(BUILD_WORKER_TOOLS)
     add_subdirectory(folder_to_db)
 endif()
diff --git a/tests/worker/connect_multithread/CMakeLists.txt b/tests/automatic/worker/connect_multithread/CMakeLists.txt
similarity index 89%
rename from tests/worker/connect_multithread/CMakeLists.txt
rename to tests/automatic/worker/connect_multithread/CMakeLists.txt
index abe83367dc6b2796d7ca4d734b6ed4d37b8ed167..64224018bc011daf620bca4a96fcc5730004d9b4 100644
--- a/tests/worker/connect_multithread/CMakeLists.txt
+++ b/tests/automatic/worker/connect_multithread/CMakeLists.txt
@@ -7,7 +7,6 @@ set(SOURCE_FILES content_multithread.cpp)
 ################################
 add_executable(${TARGET_NAME} ${SOURCE_FILES})
 target_link_libraries(${TARGET_NAME} test_common hidra2-worker ${HIDRA2_COMMON_IO_LIBRARIES})
-set_target_properties(${TARGET_NAME} PROPERTIES LINKER_LANGUAGE CXX)
 if (CMAKE_COMPILER_IS_GNUCXX)
     set_target_properties(${TARGET_NAME} PROPERTIES LINK_FLAGS_DEBUG "--coverage")
 endif()
diff --git a/tests/worker/connect_multithread/cleanup_linux.sh b/tests/automatic/worker/connect_multithread/cleanup_linux.sh
similarity index 100%
rename from tests/worker/connect_multithread/cleanup_linux.sh
rename to tests/automatic/worker/connect_multithread/cleanup_linux.sh
diff --git a/tests/worker/connect_multithread/cleanup_windows.bat b/tests/automatic/worker/connect_multithread/cleanup_windows.bat
similarity index 100%
rename from tests/worker/connect_multithread/cleanup_windows.bat
rename to tests/automatic/worker/connect_multithread/cleanup_windows.bat
diff --git a/tests/worker/connect_multithread/content_multithread.cpp b/tests/automatic/worker/connect_multithread/content_multithread.cpp
similarity index 100%
rename from tests/worker/connect_multithread/content_multithread.cpp
rename to tests/automatic/worker/connect_multithread/content_multithread.cpp
diff --git a/tests/worker/connect_multithread/setup_linux.sh b/tests/automatic/worker/connect_multithread/setup_linux.sh
similarity index 100%
rename from tests/worker/connect_multithread/setup_linux.sh
rename to tests/automatic/worker/connect_multithread/setup_linux.sh
diff --git a/tests/worker/connect_multithread/setup_windows.bat b/tests/automatic/worker/connect_multithread/setup_windows.bat
similarity index 100%
rename from tests/worker/connect_multithread/setup_windows.bat
rename to tests/automatic/worker/connect_multithread/setup_windows.bat
diff --git a/tests/worker/folder_to_db/CMakeLists.txt b/tests/automatic/worker/folder_to_db/CMakeLists.txt
similarity index 100%
rename from tests/worker/folder_to_db/CMakeLists.txt
rename to tests/automatic/worker/folder_to_db/CMakeLists.txt
diff --git a/tests/worker/folder_to_db/check_linux.sh b/tests/automatic/worker/folder_to_db/check_linux.sh
similarity index 100%
rename from tests/worker/folder_to_db/check_linux.sh
rename to tests/automatic/worker/folder_to_db/check_linux.sh
diff --git a/tests/worker/folder_to_db/check_windows.bat b/tests/automatic/worker/folder_to_db/check_windows.bat
similarity index 100%
rename from tests/worker/folder_to_db/check_windows.bat
rename to tests/automatic/worker/folder_to_db/check_windows.bat
diff --git a/tests/worker/next_multithread_broker/CMakeLists.txt b/tests/automatic/worker/next_multithread_broker/CMakeLists.txt
similarity index 69%
rename from tests/worker/next_multithread_broker/CMakeLists.txt
rename to tests/automatic/worker/next_multithread_broker/CMakeLists.txt
index e97403117f3b1c7e2a9f5708484ec6b71831c069..9c22693a5cbb40f0778ce0ff145ef428fc3e1d8f 100644
--- a/tests/worker/next_multithread_broker/CMakeLists.txt
+++ b/tests/automatic/worker/next_multithread_broker/CMakeLists.txt
@@ -6,12 +6,12 @@ set(SOURCE_FILES next_multithread_broker.cpp)
 # Executable and link
 ################################
 add_executable(${TARGET_NAME} ${SOURCE_FILES})
-target_link_libraries(${TARGET_NAME} test_common hidra2-worker  ${CMAKE_THREAD_LIBS_INIT})
+target_link_libraries(${TARGET_NAME} test_common hidra2-worker)
 
 ################################
 # Testing
 ################################
-configure_file(${CMAKE_SOURCE_DIR}/tests/settings/broker_settings.json settings.json COPYONLY)
+configure_file(${CMAKE_SOURCE_DIR}/tests/automatic/settings/broker_settings.json settings.json COPYONLY)
 add_script_test("${TARGET_NAME}" "$<TARGET_FILE:${TARGET_NAME}> $<TARGET_PROPERTY:hidra2-broker,EXENAME>"
         )
 
diff --git a/tests/worker/next_multithread_broker/check_linux.sh b/tests/automatic/worker/next_multithread_broker/check_linux.sh
similarity index 100%
rename from tests/worker/next_multithread_broker/check_linux.sh
rename to tests/automatic/worker/next_multithread_broker/check_linux.sh
diff --git a/tests/worker/next_multithread_broker/check_windows.bat b/tests/automatic/worker/next_multithread_broker/check_windows.bat
similarity index 100%
rename from tests/worker/next_multithread_broker/check_windows.bat
rename to tests/automatic/worker/next_multithread_broker/check_windows.bat
diff --git a/tests/worker/next_multithread_broker/next_multithread_broker.cpp b/tests/automatic/worker/next_multithread_broker/next_multithread_broker.cpp
similarity index 100%
rename from tests/worker/next_multithread_broker/next_multithread_broker.cpp
rename to tests/automatic/worker/next_multithread_broker/next_multithread_broker.cpp
diff --git a/tests/worker/next_multithread_folder/CMakeLists.txt b/tests/automatic/worker/next_multithread_folder/CMakeLists.txt
similarity index 91%
rename from tests/worker/next_multithread_folder/CMakeLists.txt
rename to tests/automatic/worker/next_multithread_folder/CMakeLists.txt
index eed3cc9b92840d9f2fe0aebda644df863acb553a..b0920c971f868faacd93ed7fbd60e630651f87c4 100644
--- a/tests/worker/next_multithread_folder/CMakeLists.txt
+++ b/tests/automatic/worker/next_multithread_folder/CMakeLists.txt
@@ -12,7 +12,6 @@ GET_PROPERTY(HIDRA2_COMMON_IO_LIBRARIES GLOBAL PROPERTY HIDRA2_COMMON_IO_LIBRARI
 target_link_libraries(${TARGET_NAME} ${HIDRA2_COMMON_IO_LIBRARIES})
 
 target_link_libraries(${TARGET_NAME} test_common hidra2-worker)
-set_target_properties(${TARGET_NAME} PROPERTIES LINKER_LANGUAGE CXX)
 if (CMAKE_COMPILER_IS_GNUCXX)
     set_target_properties(${TARGET_NAME} PROPERTIES LINK_FLAGS_DEBUG "--coverage")
 endif()
diff --git a/tests/worker/next_multithread_folder/cleanup_linux.sh b/tests/automatic/worker/next_multithread_folder/cleanup_linux.sh
similarity index 100%
rename from tests/worker/next_multithread_folder/cleanup_linux.sh
rename to tests/automatic/worker/next_multithread_folder/cleanup_linux.sh
diff --git a/tests/worker/next_multithread_folder/cleanup_windows.bat b/tests/automatic/worker/next_multithread_folder/cleanup_windows.bat
similarity index 100%
rename from tests/worker/next_multithread_folder/cleanup_windows.bat
rename to tests/automatic/worker/next_multithread_folder/cleanup_windows.bat
diff --git a/tests/worker/next_multithread_folder/next_multithread_folder.cpp b/tests/automatic/worker/next_multithread_folder/next_multithread_folder.cpp
similarity index 100%
rename from tests/worker/next_multithread_folder/next_multithread_folder.cpp
rename to tests/automatic/worker/next_multithread_folder/next_multithread_folder.cpp
diff --git a/tests/worker/next_multithread_folder/setup_linux.sh b/tests/automatic/worker/next_multithread_folder/setup_linux.sh
similarity index 100%
rename from tests/worker/next_multithread_folder/setup_linux.sh
rename to tests/automatic/worker/next_multithread_folder/setup_linux.sh
diff --git a/tests/worker/next_multithread_folder/setup_windows.bat b/tests/automatic/worker/next_multithread_folder/setup_windows.bat
similarity index 100%
rename from tests/worker/next_multithread_folder/setup_windows.bat
rename to tests/automatic/worker/next_multithread_folder/setup_windows.bat
diff --git a/tests/broker/read_config/settings_bad.json b/tests/broker/read_config/settings_bad.json
deleted file mode 100644
index 01598594a92d5cbcd01cacc8e8c27e27cf2c42ea..0000000000000000000000000000000000000000
--- a/tests/broker/read_config/settings_bad.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
-  "dbaddress":"127.0.0.1:27017",
-  "port":
-}
\ No newline at end of file
diff --git a/tests/manual/performance_broker/settings.json b/tests/manual/performance_broker/settings.json
new file mode 100644
index 0000000000000000000000000000000000000000..c45d16f2f7b59b7966ad9d2d406ef530da720a2b
--- /dev/null
+++ b/tests/manual/performance_broker/settings.json
@@ -0,0 +1,6 @@
+{
+  "BrokerDbAddress":"localhost:27017",
+  "MonitorDbAddress": "localhost:8086",
+  "MonitorDbName": "db_test",
+  "port":5005
+}
\ No newline at end of file
diff --git a/tests/manual/performance_broker/test.sh b/tests/manual/performance_broker/test.sh
new file mode 100755
index 0000000000000000000000000000000000000000..3083e4e001d2898a99d52ac368b0a66e02ac4cad
--- /dev/null
+++ b/tests/manual/performance_broker/test.sh
@@ -0,0 +1,58 @@
+#!/usr/bin/env bash
+
+# starts broker, mongodb on $service_node
+# reads fileset into database
+# calls getnext_broker example from $worker_node
+
+nthreads=16
+# a directory with many files in it
+dir=/gpfs/petra3/scratch/yakubov/test
+run_name=test
+service_node=max-wgs
+
+monitor_node=zitpcx27016
+monitor_port=8086
+
+worker_node=max-display
+#worker_node=max-wgs
+
+worker_dir=~/broker_test
+service_dir=~/broker_test
+
+
+cat settings.json |
+  jq "to_entries |
+       map(if .key == \"MonitorDbAddress\"
+          then . + {value:\"${monitor_node}:${monitor_port}\"}
+          else .
+          end
+         ) |
+      from_entries" > settings_tmp.json
+
+ssh ${monitor_node} influx -execute \"create database db_test\"
+
+ssh ${service_node} docker run -d -p 27017:27017 --name mongo mongo
+#ssh ${service_node} docker run -d -p 8086 -p 8086 --name influxdb influxdb
+
+ssh ${service_node} mkdir ${service_dir}
+ssh ${worker_node} mkdir ${worker_dir}
+
+
+scp settings_tmp.json ${service_node}:${service_dir}/settings.json
+rm settings_tmp.json
+scp ../../../cmake-build-release/broker/hidra2-broker ${service_node}:${service_dir}
+ssh ${service_node} "bash -c 'cd ${service_dir}; nohup ./hidra2-broker settings.json &> ${service_dir}/broker.log &'"
+sleep 0.3
+scp ../../../cmake-build-release/worker/tools/folder_to_db/folder2db ${worker_node}:${worker_dir}
+ssh ${worker_node} ${worker_dir}/folder2db -n ${nthreads} ${dir} ${run_name} ${service_node}
+
+sleep 3
+
+scp ../../../cmake-build-release/examples/worker/getnext_broker/getnext_broker ${worker_node}:${worker_dir}
+ssh ${worker_node} ${worker_dir}/getnext_broker ${service_node}:5005 ${run_name} ${nthreads}
+
+
+
+ssh ${service_node} killall hidra2-broker
+ssh ${service_node} docker rm -f mongo
+#ssh ${service_node} docker rm -f influxdb
diff --git a/tests/manual/performance_producer_receiver/receiver.json b/tests/manual/performance_producer_receiver/receiver.json
new file mode 100644
index 0000000000000000000000000000000000000000..e23a556d1cd5a8c03ce0c32b37b7b73161bfa89d
--- /dev/null
+++ b/tests/manual/performance_producer_receiver/receiver.json
@@ -0,0 +1,6 @@
+{
+  "MonitorDbAddress":"localhost:8086",
+  "MonitorDbName": "db_test",
+  "ListenPort":4200,
+  "WriteToDisk":true
+}
\ No newline at end of file
diff --git a/tests/manual/performance_producer_receiver/settings_tmp.json b/tests/manual/performance_producer_receiver/settings_tmp.json
new file mode 100644
index 0000000000000000000000000000000000000000..35b3649bcb182f9b0121d9e85cac59e9c710e679
--- /dev/null
+++ b/tests/manual/performance_producer_receiver/settings_tmp.json
@@ -0,0 +1,6 @@
+{
+  "MonitorDbAddress": "zitpcx27016:8086",
+  "MonitorDbName": "db_test",
+  "ListenPort": 4201,
+  "WriteToDisk": false
+}
diff --git a/tests/manual/performance_producer_receiver/test.sh b/tests/manual/performance_producer_receiver/test.sh
new file mode 100755
index 0000000000000000000000000000000000000000..317f32d77331e639f2d1ed564fd5ba4fc31aa70c
--- /dev/null
+++ b/tests/manual/performance_producer_receiver/test.sh
@@ -0,0 +1,69 @@
+#!/usr/bin/env bash
+
+set -e
+
+# starts receiver on $service_node
+# runs producer with various file sizes from $worker_node and measures performance
+
+# a working directory
+service_node=max-wgs
+service_ip=`resolveip -s ${service_node}`
+service_port=4201
+
+monitor_node=zitpcx27016
+monitor_port=8086
+
+worker_node=max-display
+#worker_node=max-wgs
+
+worker_dir=~/producer_tests
+service_dir=/gpfs/petra3/scratch/yakubov/receiver_tests
+
+ssh ${monitor_node} influx -execute \"create database db_test\"
+
+#ssh ${monitor_node} docker run -d -p 8086 -p 8086 --name influxdb influxdb
+
+ssh ${service_node} mkdir -p ${service_dir}
+ssh ${service_node} mkdir -p ${service_dir}/files
+ssh ${worker_node} mkdir -p ${worker_dir}
+
+scp ../../../cmake-build-release/receiver/receiver ${service_node}:${service_dir}
+scp ../../../cmake-build-release/examples/producer/dummy-data-producer/dummy-data-producer ${worker_node}:${worker_dir}
+
+
+function do_work {
+cat receiver.json |
+  jq "to_entries |
+       map(if .key == \"MonitorDbAddress\"
+          then . + {value:\"${monitor_node}:${monitor_port}\"}
+          elif .key == \"ListenPort\"
+          then . + {value:${service_port}}
+          elif .key == \"WriteToDisk\"
+          then . + {value:$1}
+          else .
+          end
+         ) |
+      from_entries" > settings_tmp.json
+scp settings_tmp.json ${service_node}:${service_dir}/settings.json
+ssh ${service_node} "bash -c 'cd ${service_dir}; nohup ./receiver settings.json &> ${service_dir}/receiver.log &'"
+sleep 0.3
+for size  in 100 1000 10000
+do
+echo ===================================================================
+ssh ${worker_node} ${worker_dir}/dummy-data-producer ${service_ip}:${service_port} ${size} 1000
+ssh ${service_node} rm -f ${service_dir}/files/*
+done
+ssh ${service_node} killall receiver
+}
+
+echo
+echo "With write to disk:"
+do_work true
+
+echo
+echo "Without write to disk:"
+do_work false
+
+
+#rm settings_tmp.json
+#ssh ${service_node} docker rm -f influxdb
diff --git a/tests/producer_receiver/CMakeLists.txt b/tests/producer_receiver/CMakeLists.txt
deleted file mode 100644
index c4da20d000bc7375e799fcd9b063a640d75c506a..0000000000000000000000000000000000000000
--- a/tests/producer_receiver/CMakeLists.txt
+++ /dev/null
@@ -1 +0,0 @@
-add_subdirectory(transfer_single_file)
diff --git a/tests/settings/broker_settings.json b/tests/settings/broker_settings.json
deleted file mode 100644
index 841f1994677df8ab075b7e71859991527f438b67..0000000000000000000000000000000000000000
--- a/tests/settings/broker_settings.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
-  "dbaddress":"127.0.0.1:27017",
-  "port":5005
-}
\ No newline at end of file
diff --git a/tests/worker/curl_http_client_get/CMakeLists.txt b/tests/worker/curl_http_client_get/CMakeLists.txt
deleted file mode 100644
index e233d8a7893f9126dfd81a27a739936d11be1f6b..0000000000000000000000000000000000000000
--- a/tests/worker/curl_http_client_get/CMakeLists.txt
+++ /dev/null
@@ -1,25 +0,0 @@
-set(TARGET_NAME curl_httpclient_get)
-set(SOURCE_FILES curl_httpclient_get.cpp)
-
-
-################################
-# Executable and link
-################################
-add_executable(${TARGET_NAME} ${SOURCE_FILES})
-target_link_libraries(${TARGET_NAME} test_common hidra2-worker)
-
-#set_target_properties(${TARGET_NAME} PROPERTIES LINKER_LANGUAGE CXX)
-#if (CMAKE_COMPILER_IS_GNUCXX)
-#    set_target_properties(${TARGET_NAME} PROPERTIES LINK_FLAGS_DEBUG "--coverage")
-#endif()
-
-
-################################
-# Testing
-################################
-
-#add_test_setup_cleanup(${TARGET_NAME})
-add_integration_test(${TARGET_NAME} get_google "google.com moved 302")
-add_integration_test(${TARGET_NAME} get_badaddress "google.com/badaddress found 404")
-add_integration_test(${TARGET_NAME} get_badaddress2 "111 clienterror 404")
-
diff --git a/worker/api/cpp/CMakeLists.txt b/worker/api/cpp/CMakeLists.txt
index 914d87840dff2058631c83a683401fb10bf9f959..f60a3268647407b76e3c858f0baaa73ec59df718 100644
--- a/worker/api/cpp/CMakeLists.txt
+++ b/worker/api/cpp/CMakeLists.txt
@@ -2,33 +2,23 @@ set(TARGET_NAME hidra2-worker)
 
 set(SOURCE_FILES
         src/data_broker.cpp
-        src/http_client.cpp
         src/folder_data_broker.cpp
         src/server_data_broker.cpp
-        src/curl_http_client.cpp
-        src/http_error.cpp)
+)
 
 
 ################################
 # Library
 ################################
 add_library(${TARGET_NAME} STATIC ${SOURCE_FILES} $<TARGET_OBJECTS:system_io>
-            $<TARGET_OBJECTS:json_parser> $<TARGET_OBJECTS:data_structs> )
-
-set (CMAKE_PREFIX_PATH "${LIBCURL_DIR}")
-find_package (CURL REQUIRED)
-message (STATUS "Found cURL libraries: ${CURL_LIBRARIES}")
-message (STATUS "cURL include: ${CURL_INCLUDE_DIRS}")
-
+            $<TARGET_OBJECTS:json_parser> $<TARGET_OBJECTS:data_structs> $<TARGET_OBJECTS:curl_http_client> )
 
 target_include_directories(${TARGET_NAME} PUBLIC include ${HIDRA2_CXX_COMMON_INCLUDE_DIR}
         ${CURL_INCLUDE_DIRS})
 
-IF(WIN32)
-    target_compile_definitions(${TARGET_NAME} PUBLIC -DCURL_STATICLIB)
-ELSEIF(CMAKE_C_COMPILER_ID STREQUAL "GNU")
+IF(CMAKE_C_COMPILER_ID STREQUAL "GNU")
     SET( CMAKE_EXE_LINKER_FLAGS  "${CMAKE_EXE_LINKER_FLAGS} -static-libgcc -static-libstdc++")
-ENDIF(WIN32)
+ENDIF()
 
 target_link_libraries(${TARGET_NAME} ${CURL_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
 
diff --git a/worker/api/cpp/src/http_client.cpp b/worker/api/cpp/src/http_client.cpp
deleted file mode 100644
index d5c86c6c07343e163714d36a2b22053138bec407..0000000000000000000000000000000000000000
--- a/worker/api/cpp/src/http_client.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-#include "http_client.h"
-#include "http_error.h"
-namespace hidra2 {
-
-Error HttpCodeToWorkerError(const HttpCode& code) {
-    const char* message;
-    switch (code) {
-    case HttpCode::OK:
-        return nullptr;
-    case HttpCode::BadRequest:
-        message = WorkerErrorMessage::kWrongInput;
-        break;
-    case HttpCode::InternalServerError:
-        message = WorkerErrorMessage::kErrorReadingSource;
-        break;
-    case HttpCode::NoContent:
-        message = WorkerErrorMessage::kNoData;
-        return TextErrorWithType(message, ErrorType::kEndOfFile);
-    case HttpCode::NotFound:
-        message = WorkerErrorMessage::kSourceNotFound;
-        break;
-    default:
-        message = WorkerErrorMessage::kErrorReadingSource;
-        break;
-    }
-    return Error{new HttpError(message, code)};
-}
-
-}
diff --git a/worker/api/cpp/src/http_error.cpp b/worker/api/cpp/src/http_error.cpp
deleted file mode 100644
index 72189fc37a93b34175987bac04642cc283b570c3..0000000000000000000000000000000000000000
--- a/worker/api/cpp/src/http_error.cpp
+++ /dev/null
@@ -1,7 +0,0 @@
-#include "http_error.h"
-
-namespace hidra2 {
-
-
-
-}
\ No newline at end of file
diff --git a/worker/api/cpp/src/server_data_broker.cpp b/worker/api/cpp/src/server_data_broker.cpp
index 0298c82db4d538a0c0202ff7efb82fa10b16752a..0b739e6e7d720e713c91a8623ea9153d08f4678a 100644
--- a/worker/api/cpp/src/server_data_broker.cpp
+++ b/worker/api/cpp/src/server_data_broker.cpp
@@ -1,9 +1,37 @@
 #include "server_data_broker.h"
 #include "io/io_factory.h"
-#include "curl_http_client.h"
+#include "http_client/curl_http_client.h"
+#include "http_client/http_error.h"
+
 
 namespace hidra2 {
 
+Error HttpCodeToWorkerError(const HttpCode& code) {
+    const char* message;
+    switch (code) {
+    case HttpCode::OK:
+        return nullptr;
+    case HttpCode::BadRequest:
+        message = WorkerErrorMessage::kWrongInput;
+        break;
+    case HttpCode::InternalServerError:
+        message = WorkerErrorMessage::kErrorReadingSource;
+        break;
+    case HttpCode::NoContent:
+        message = WorkerErrorMessage::kNoData;
+        return TextErrorWithType(message, ErrorType::kEndOfFile);
+    case HttpCode::NotFound:
+        message = WorkerErrorMessage::kSourceNotFound;
+        break;
+    default:
+        message = WorkerErrorMessage::kErrorReadingSource;
+        break;
+    }
+    return Error{new HttpError(message, code)};
+}
+
+
+
 ServerDataBroker::ServerDataBroker(const std::string& server_uri,
                                    const std::string& source_name):
     io__{GenerateDefaultIO()}, httpclient__{new CurlHttpClient},
diff --git a/worker/api/cpp/src/server_data_broker.h b/worker/api/cpp/src/server_data_broker.h
index 2081261b832c9884e8c762983f58c43adf91579e..5be1b4236e818fa303cde1829811219bc7e099d9 100644
--- a/worker/api/cpp/src/server_data_broker.h
+++ b/worker/api/cpp/src/server_data_broker.h
@@ -3,18 +3,20 @@
 
 #include "worker/data_broker.h"
 #include "io/io.h"
-#include "http_client.h"
+#include "http_client/http_client.h"
 
 
 namespace hidra2 {
 
+Error HttpCodeToWorkerError(const HttpCode& code);
+
 class ServerDataBroker final : public hidra2::DataBroker {
   public:
     explicit ServerDataBroker(const std::string& server_uri, const std::string& source_name);
     Error Connect() override;
     Error GetNext(FileInfo* info, FileData* data) override;
-    std::unique_ptr<hidra2::IO> io__; // modified in testings to mock system calls,otherwise do not touch
-    std::unique_ptr<hidra2::HttpClient> httpclient__;
+    std::unique_ptr<IO> io__; // modified in testings to mock system calls,otherwise do not touch
+    std::unique_ptr<HttpClient> httpclient__;
   private:
     Error GetFileInfoFromServer(FileInfo* info, const std::string& operation);
     std::string server_uri_;
diff --git a/worker/api/cpp/unittests/MockHttpClient.h b/worker/api/cpp/unittests/MockHttpClient.h
deleted file mode 100644
index a386321db331ea9549646faff4fa4302c17e5434..0000000000000000000000000000000000000000
--- a/worker/api/cpp/unittests/MockHttpClient.h
+++ /dev/null
@@ -1,25 +0,0 @@
-#ifndef HIDRA2_MOCKHTTPCLIENT_H
-#define HIDRA2_MOCKHTTPCLIENT_H
-
-#include <gtest/gtest.h>
-#include <gmock/gmock.h>
-
-#include "../src/http_client.h"
-#include "worker/data_broker.h"
-
-
-namespace hidra2 {
-
-class MockHttpClient : public HttpClient {
-  public:
-    std::string Get(const std::string& uri, HttpCode* code, Error* err) const noexcept override {
-        return Get_t(uri, code, err);
-    }
-    MOCK_CONST_METHOD3(Get_t,
-                       std::string(const std::string& uri, HttpCode* code, Error* err));
-};
-
-
-}
-
-#endif //HIDRA2_MOCKHTTPCLIENT_H
diff --git a/worker/api/cpp/unittests/test_server_broker.cpp b/worker/api/cpp/unittests/test_server_broker.cpp
index 48296c46fdb53baa0cdca421aa692b6e22c34de8..7987769fdd31e72e715f1959375ec806525e94aa 100644
--- a/worker/api/cpp/unittests/test_server_broker.cpp
+++ b/worker/api/cpp/unittests/test_server_broker.cpp
@@ -5,10 +5,10 @@
 #include "io/io.h"
 #include "../../../../common/cpp/src/system_io/system_io.h"
 #include "../src/server_data_broker.h"
-#include "../src/curl_http_client.h"
+#include "http_client/curl_http_client.h"
 #include "unittests/MockIO.h"
-#include "MockHttpClient.h"
-#include "../src/http_error.h"
+#include "unittests/MockHttpClient.h"
+#include "http_client/http_error.h"
 
 using hidra2::DataBrokerFactory;
 using hidra2::DataBroker;