Skip to content

Commit f2439d2

Browse files
Non blocking http (#87)
* namespace added on countly classes * request logic added * remote config send http handled. * code styling * changes, * Update CHANGELOG.md * - return -> break - typo fixed - methods comments added * sdk version updated * warning added * - remote config exta function removed - remote config helper function renamed Co-authored-by: ArtursKadikis <[email protected]>
1 parent 48c1ff0 commit f2439d2

File tree

4 files changed

+99
-53
lines changed

4 files changed

+99
-53
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
* !! Major breaking change !! We are adding the 'cly' namespace on 'Countly' class which will change how that class can be referenced. 'Countly::' will not work, you will have to use 'cly::Countly::' instead.
33
* !! Major breaking change !! We are extracting the 'Event' class out of the 'Countly' class which will change how that class can be referenced. 'Countly::Event' will not work, you will have to use 'cly::Event' instead.
44
* !! Major breaking change !! Increased the compiler version required to compile the SDK. It's increased from version C++11 to C++14.
5+
* Making network requests has been reworked. They will now be sent on a separate thread. Requests will also be added in an internal queue and will be sent one at a time.
56

67
22.02.0
78
* Added 10-second time-outs for all windows HTTP transactions.

include/countly.hpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,16 @@ class Countly : public cly::CountlyDelegates {
212212
void _sendIndependantLocationRequest();
213213
void log(LogLevel level, const std::string &message);
214214

215+
/**
216+
* Helper methods to fetch remote config from the server.
217+
*/
218+
#pragma region Remote_Config_Helper_Methods
219+
void _fetchRemoteConfig(std::map<std::string, std::string> &data);
220+
void _updateRemoteConfigWithSpecificValues(std::map<std::string, std::string> &data);
221+
#pragma endregion Remote_Config_Helper_Methods
222+
223+
void processRequestQueue();
224+
void addToRequestQueue(std::string &data);
215225
HTTPResponse sendHTTP(std::string path, std::string data);
216226

217227
void _changeDeviceIdWithMerge(const std::string &value);
@@ -248,12 +258,14 @@ class Countly : public cly::CountlyDelegates {
248258
std::shared_ptr<cly::LoggerModule> logger;
249259

250260
std::mutex mutex;
261+
bool enable_automatic_session = false;
251262
bool stop_thread = false;
252263
bool running = false;
253264
size_t wait_milliseconds = COUNTLY_KEEPALIVE_INTERVAL;
254265
unsigned short _auto_session_update_interval = 60; // value is in seconds;
255266

256267
size_t max_events = COUNTLY_MAX_EVENTS_DEFAULT;
268+
std::deque<std::string> request_queue;
257269
#ifndef COUNTLY_USE_SQLITE
258270
std::deque<std::string> event_queue;
259271
#else

include/countly/constants.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
#define COUNTLY_SDK_NAME "cpp-native-unknown"
1414
#define COUNTLY_SDK_VERSION "0.1.0"
15-
#define COUNTLY_API_VERSION "22.02.0"
15+
#define COUNTLY_API_VERSION "22.06.0"
1616
#define COUNTLY_POST_THRESHOLD 2000
1717
#define COUNTLY_KEEPALIVE_INTERVAL 3000
1818
#define COUNTLY_MAX_EVENTS_DEFAULT 200

src/countly.cpp

Lines changed: 85 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ void Countly::setUserDetails(const std::map<std::string, std::string> &value) {
136136

137137
std::map<std::string, std::string> data = {{"app_key", session_params["app_key"].get<std::string>()}, {"device_id", session_params["device_id"].get<std::string>()}, {"user_details", session_params["user_details"].dump()}};
138138

139-
sendHTTP("/i", Countly::serializeForm(data));
139+
addToRequestQueue(Countly::serializeForm(data));
140140
mutex.unlock();
141141
}
142142

@@ -152,7 +152,7 @@ void Countly::setCustomUserDetails(const std::map<std::string, std::string> &val
152152

153153
std::map<std::string, std::string> data = {{"app_key", session_params["app_key"].get<std::string>()}, {"device_id", session_params["device_id"].get<std::string>()}, {"user_details", session_params["user_details"].dump()}};
154154

155-
sendHTTP("/i", Countly::serializeForm(data));
155+
addToRequestQueue(Countly::serializeForm(data));
156156
mutex.unlock();
157157
}
158158

@@ -229,7 +229,7 @@ void Countly::_sendIndependantLocationRequest() {
229229
data["app_key"] = session_params["app_key"].get<std::string>();
230230
data["device_id"] = session_params["device_id"].get<std::string>();
231231
data["timestamp"] = std::to_string(timestamp.count());
232-
sendHTTP("/i", Countly::serializeForm(data));
232+
addToRequestQueue(Countly::serializeForm(data));
233233
}
234234

235235
mutex.unlock();
@@ -285,7 +285,7 @@ void Countly::_changeDeviceIdWithMerge(const std::string &value) {
285285
{"old_device_id", session_params["old_device_id"].get<std::string>()},
286286
{"timestamp", std::to_string(timestamp.count())},
287287
};
288-
sendHTTP("/i", Countly::serializeForm(data));
288+
addToRequestQueue(Countly::serializeForm(data));
289289

290290
session_params.erase("old_device_id");
291291
mutex.unlock();
@@ -313,6 +313,8 @@ void Countly::_changeDeviceIdWithoutMerge(const std::string &value) {
313313

314314
void Countly::start(const std::string &app_key, const std::string &host, int port, bool start_thread) {
315315
mutex.lock();
316+
enable_automatic_session = start_thread;
317+
start_thread = true;
316318
log(Countly::LogLevel::INFO, "[Countly][start]");
317319
this->host = host;
318320
if (host.find("http://") == 0) {
@@ -552,19 +554,15 @@ bool Countly::beginSession() {
552554
data["metrics"] = session_params["metrics"].dump();
553555
}
554556

555-
if (sendHTTP("/i", Countly::serializeForm(data)).success) {
556-
session_params.erase("user_details");
557-
last_sent_session_request = Countly::getTimestamp();
558-
began_session = true;
559-
}
557+
addToRequestQueue(Countly::serializeForm(data));
558+
session_params.erase("user_details");
559+
last_sent_session_request = Countly::getTimestamp();
560+
began_session = true;
561+
mutex.unlock();
560562

561563
if (remote_config_enabled) {
562-
mutex.unlock();
563564
updateRemoteConfig();
564-
} else {
565-
mutex.unlock();
566565
}
567-
568566
return began_session;
569567
}
570568

@@ -639,10 +637,7 @@ bool Countly::updateSession() {
639637
if (duration.count() >= _auto_session_update_interval) {
640638
log(Countly::LogLevel::DEBUG, "[Countly][updateSession] sending session update.");
641639
std::map<std::string, std::string> data = {{"app_key", session_params["app_key"].get<std::string>()}, {"device_id", session_params["device_id"].get<std::string>()}, {"session_duration", std::to_string(duration.count())}};
642-
if (!sendHTTP("/i", Countly::serializeForm(data)).success) {
643-
mutex.unlock();
644-
return false;
645-
}
640+
addToRequestQueue(Countly::serializeForm(data));
646641

647642
last_sent_session_request += duration;
648643
}
@@ -651,10 +646,7 @@ bool Countly::updateSession() {
651646
log(Countly::LogLevel::DEBUG, "[Countly][updateSession] sending event.");
652647
std::map<std::string, std::string> data = {{"app_key", session_params["app_key"].get<std::string>()}, {"device_id", session_params["device_id"].get<std::string>()}, {"events", events.dump()}};
653648

654-
if (!sendHTTP("/i", Countly::serializeForm(data)).success) {
655-
mutex.unlock();
656-
return false;
657-
}
649+
addToRequestQueue(Countly::serializeForm(data));
658650
}
659651

660652
#ifndef COUNTLY_USE_SQLITE
@@ -697,15 +689,12 @@ bool Countly::endSession() {
697689
return false;
698690
}
699691

700-
if (sendHTTP("/i", Countly::serializeForm(data)).success) {
701-
last_sent_session_request = now;
702-
began_session = false;
703-
mutex.unlock();
704-
return true;
705-
}
692+
addToRequestQueue(Countly::serializeForm(data));
706693

694+
last_sent_session_request = now;
695+
began_session = false;
707696
mutex.unlock();
708-
return false;
697+
return true;
709698
}
710699

711700
std::chrono::system_clock::time_point Countly::getTimestamp() { return std::chrono::system_clock::now(); }
@@ -798,6 +787,32 @@ std::string Countly::calculateChecksum(const std::string &salt, const std::strin
798787
#endif
799788
}
800789

790+
void Countly::processRequestQueue() {
791+
792+
while (!request_queue.empty()) {
793+
mutex.lock();
794+
std::string data = request_queue.front();
795+
HTTPResponse response = sendHTTP("/i", data);
796+
797+
if (!response.success) {
798+
mutex.unlock();
799+
break;
800+
}
801+
802+
request_queue.pop_front();
803+
mutex.unlock();
804+
}
805+
}
806+
807+
void Countly::addToRequestQueue(std::string &data) {
808+
if (request_queue.size() >= 1000) {
809+
log(Countly::LogLevel::WARNING, "[Countly][addToRequestQueue] Request Queue is full. Dropping the oldest request.");
810+
request_queue.pop_front();
811+
}
812+
813+
request_queue.push_back(data);
814+
}
815+
801816
Countly::HTTPResponse Countly::sendHTTP(std::string path, std::string data) {
802817
bool use_post = always_use_post || (data.size() > COUNTLY_POST_THRESHOLD);
803818
log(Countly::LogLevel::DEBUG, "[Countly][sendHTTP] data: " + data);
@@ -996,7 +1011,11 @@ void Countly::updateLoop() {
9961011
size_t last_wait_milliseconds = wait_milliseconds;
9971012
mutex.unlock();
9981013
std::this_thread::sleep_for(std::chrono::milliseconds(last_wait_milliseconds));
999-
updateSession();
1014+
if (enable_automatic_session) {
1015+
updateSession();
1016+
}
1017+
1018+
processRequestQueue();
10001019
}
10011020
mutex.lock();
10021021
running = false;
@@ -1009,20 +1028,29 @@ void Countly::enableRemoteConfig() {
10091028
mutex.unlock();
10101029
}
10111030

1031+
void Countly::_fetchRemoteConfig(std::map<std::string, std::string> &data) {
1032+
mutex.lock();
1033+
HTTPResponse response = sendHTTP("/o/sdk", serializeForm(data));
1034+
if (response.success) {
1035+
remote_config = response.data;
1036+
}
1037+
mutex.unlock();
1038+
}
1039+
10121040
void Countly::updateRemoteConfig() {
1041+
mutex.lock();
10131042
if (!session_params["app_key"].is_string() || !session_params["device_id"].is_string()) {
10141043
log(Countly::LogLevel::ERROR, "Error updating remote config, app key or device id is missing");
1044+
mutex.unlock();
10151045
return;
10161046
}
1017-
10181047
std::map<std::string, std::string> data = {{"method", "fetch_remote_config"}, {"app_key", session_params["app_key"].get<std::string>()}, {"device_id", session_params["device_id"].get<std::string>()}};
10191048

1020-
HTTPResponse response = sendHTTP("/o/sdk", serializeForm(data));
1021-
if (response.success) {
1022-
mutex.lock();
1023-
remote_config = response.data;
1024-
mutex.unlock();
1025-
}
1049+
mutex.unlock();
1050+
1051+
// Fetch remote config asynchronously
1052+
std::thread _thread(&Countly::_fetchRemoteConfig, this, data);
1053+
_thread.detach();
10261054
}
10271055

10281056
nlohmann::json Countly::getRemoteConfigValue(const std::string &key) {
@@ -1032,7 +1060,19 @@ nlohmann::json Countly::getRemoteConfigValue(const std::string &key) {
10321060
return value;
10331061
}
10341062

1063+
void Countly::_updateRemoteConfigWithSpecificValues(std::map<std::string, std::string> &data) {
1064+
mutex.lock();
1065+
HTTPResponse response = sendHTTP("/o/sdk", serializeForm(data));
1066+
if (response.success) {
1067+
for (auto it = response.data.begin(); it != response.data.end(); ++it) {
1068+
remote_config[it.key()] = it.value();
1069+
}
1070+
}
1071+
mutex.unlock();
1072+
}
1073+
10351074
void Countly::updateRemoteConfigFor(std::string *keys, size_t key_count) {
1075+
mutex.lock();
10361076
std::map<std::string, std::string> data = {{"method", "fetch_remote_config"}, {"app_key", session_params["app_key"].get<std::string>()}, {"device_id", session_params["device_id"].get<std::string>()}};
10371077

10381078
{
@@ -1042,18 +1082,15 @@ void Countly::updateRemoteConfigFor(std::string *keys, size_t key_count) {
10421082
}
10431083
data["keys"] = keys_json.dump();
10441084
}
1085+
mutex.unlock();
10451086

1046-
HTTPResponse response = sendHTTP("/o/sdk", serializeForm(data));
1047-
if (response.success) {
1048-
mutex.lock();
1049-
for (auto it = response.data.begin(); it != response.data.end(); ++it) {
1050-
remote_config[it.key()] = it.value();
1051-
}
1052-
mutex.unlock();
1053-
}
1087+
// Fetch remote config asynchronously
1088+
std::thread _thread(&Countly::_updateRemoteConfigWithSpecificValues, this, data);
1089+
_thread.detach();
10541090
}
10551091

10561092
void Countly::updateRemoteConfigExcept(std::string *keys, size_t key_count) {
1093+
mutex.lock();
10571094
std::map<std::string, std::string> data = {{"method", "fetch_remote_config"}, {"app_key", session_params["app_key"].get<std::string>()}, {"device_id", session_params["device_id"].get<std::string>()}};
10581095

10591096
{
@@ -1063,14 +1100,10 @@ void Countly::updateRemoteConfigExcept(std::string *keys, size_t key_count) {
10631100
}
10641101
data["omit_keys"] = keys_json.dump();
10651102
}
1103+
mutex.unlock();
10661104

1067-
HTTPResponse response = sendHTTP("/o/sdk", serializeForm(data));
1068-
if (response.success) {
1069-
mutex.lock();
1070-
for (auto it = response.data.begin(); it != response.data.end(); ++it) {
1071-
remote_config[it.key()] = it.value();
1072-
}
1073-
mutex.unlock();
1074-
}
1105+
// Fetch remote config asynchronously
1106+
std::thread _thread(&Countly::_updateRemoteConfigWithSpecificValues, this, data);
1107+
_thread.detach();
10751108
}
10761109
} // namespace cly

0 commit comments

Comments
 (0)