#include "Globals.h" #include "../TestHelpers.h" #include "HTTP/UrlClient.h" #include "OSSupport/NetworkSingleton.h" namespace { /** Track number of cCallbacks instances alive. */ std::atomic g_ActiveCallbacks{ 0 }; /** Simple callbacks that dump the events to the console and signalize a cEvent when the request is finished. */ class cCallbacks: public cUrlClient::cCallbacks { public: cCallbacks(std::shared_ptr a_Event): m_Event(std::move(a_Event)) { ++g_ActiveCallbacks; LOGD("Created a cCallbacks instance at %p", reinterpret_cast(this)); } virtual ~cCallbacks() override { --g_ActiveCallbacks; LOGD("Deleting the cCallbacks instance at %p", reinterpret_cast(this)); } virtual void OnConnected(cTCPLink & a_Link) override { LOG("Link connected to %s:%u", a_Link.GetRemoteIP().c_str(), a_Link.GetRemotePort()); } virtual bool OnCertificateReceived() override { LOG("Server certificate received"); return true; } virtual void OnTlsHandshakeCompleted() override { LOG("TLS handshake has completed."); } virtual void OnRequestSent() override { LOG("Request has been sent"); } virtual void OnHeader(const AString & a_Key, const AString & a_Value) override { LOG("HTTP Header: \"%s\" -> \"%s\"", a_Key.c_str(), a_Value.c_str()); } virtual void OnHeadersFinished() override { LOG("HTTP headers finished."); } virtual void OnBodyData(const void * a_Data, size_t a_Size) override { #if 0 // Output the whole received data blob: AString body(reinterpret_cast(a_Data), a_Size); LOG("Body part:\n%s", body.c_str()); #else // Output only the data size, to keep the log short: LOG("Body part: %u bytes", static_cast(a_Size)); #endif } virtual void OnBodyFinished() override { LOG("Body finished."); m_Event->Set(); } virtual void OnRedirecting(const AString & a_RedirectUrl) override { LOG("Redirecting to \"%s\".", a_RedirectUrl.c_str()); } virtual void OnError(const AString & a_ErrorMsg) override { LOG("Error: %s", a_ErrorMsg.c_str()); m_Event->Set(); } protected: std::shared_ptr m_Event; }; int TestRequest1() { LOG("Running test 1"); auto evtFinished = std::make_shared(); auto callbacks = std::make_unique(evtFinished); AStringMap options; options["MaxRedirects"] = "0"; auto res = cUrlClient::Get("http://github.com", std::move(callbacks), AStringMap(), AString(), options); if (res.first) { evtFinished->Wait(); } else { LOG("Immediate error: %s", res.second.c_str()); return 1; } return 0; } int TestRequest2() { LOG("Running test 2"); auto evtFinished = std::make_shared(); auto callbacks = std::make_unique(evtFinished); auto res = cUrlClient::Get("http://github.com", std::move(callbacks)); if (res.first) { evtFinished->Wait(); } else { LOG("Immediate error: %s", res.second.c_str()); return 1; } return 0; } int TestRequest3() { LOG("Running test 3"); auto evtFinished = std::make_shared(); auto callbacks = std::make_unique(evtFinished); AStringMap options; options["MaxRedirects"] = "0"; auto res = cUrlClient::Get("https://github.com", std::move(callbacks), AStringMap(), AString(), options); if (res.first) { evtFinished->Wait(); } else { LOG("Immediate error: %s", res.second.c_str()); return 1; } return 0; } int TestRequest4() { LOG("Running test 4"); auto evtFinished = std::make_shared(); auto callbacks = std::make_unique(evtFinished); auto res = cUrlClient::Get("https://github.com", std::move(callbacks)); if (res.first) { evtFinished->Wait(); } else { LOG("Immediate error: %s", res.second.c_str()); return 1; } return 0; } int TestRequests() { using func_t = int(void); func_t * tests[] = { &TestRequest1, &TestRequest2, &TestRequest3, &TestRequest4, }; for (auto test: tests) { LOG("%s", AString(60, '-').c_str()); auto res = test(); if (res != 0) { return res; } } return 0; } } // namespace (anonymous) IMPLEMENT_TEST_MAIN("UrlClient", LOG("Initializing cNetwork..."); cNetworkSingleton::Get().Initialise(); LOG("Testing..."); TEST_EQUAL(TestRequests(), 0); LOG("Terminating cNetwork..."); cNetworkSingleton::Get().Terminate(); // No leaked callback instances LOG("cCallback instances still alive: %d", g_ActiveCallbacks.load()); TEST_EQUAL(g_ActiveCallbacks, 0); )