Network: Add WebService
This commit is contained in:
parent
b5206ebdb7
commit
c65daba072
|
|
@ -0,0 +1,26 @@
|
|||
#include <Nazara/Core.hpp>
|
||||
#include <Nazara/Network.hpp>
|
||||
#include <iostream>
|
||||
|
||||
int main()
|
||||
{
|
||||
Nz::Application<Nz::Network> app;
|
||||
|
||||
std::unique_ptr<Nz::WebService> webService = Nz::Network::Instance()->InstantiateWebService();
|
||||
|
||||
std::unique_ptr<Nz::WebRequest> webRequest = webService->CreateGetRequest("https://google.com", [&](const Nz::WebRequestResult& result)
|
||||
{
|
||||
if (result)
|
||||
std::cout << "Got a response (" << result.GetDownloadedSize() << " bytes, " << result.GetDownloadSpeed() << " bytes/s" << ")" << std::endl;
|
||||
else
|
||||
std::cout << "Web request failed (code " << result.GetReponseCode() << "): " << result.GetErrorMessage() << std::endl;
|
||||
|
||||
Nz::ApplicationBase::Instance()->Quit();
|
||||
});
|
||||
|
||||
webService->QueueRequest(std::move(webRequest));
|
||||
|
||||
app.AddUpdaterFunc([&] { webService->Poll(); });
|
||||
|
||||
return app.Run();
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
target("WebServices")
|
||||
add_deps("NazaraNetwork")
|
||||
add_files("main.cpp")
|
||||
|
|
@ -47,5 +47,8 @@
|
|||
#include <Nazara/Network/TcpClient.hpp>
|
||||
#include <Nazara/Network/TcpServer.hpp>
|
||||
#include <Nazara/Network/UdpSocket.hpp>
|
||||
#include <Nazara/Network/WebRequest.hpp>
|
||||
#include <Nazara/Network/WebRequestResult.hpp>
|
||||
#include <Nazara/Network/WebService.hpp>
|
||||
|
||||
#endif // NAZARA_GLOBAL_NETWORK_HPP
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
// Copyright (C) 2023 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
|
||||
// This file is part of the "Nazara Engine - Network module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
// no header guards
|
||||
|
||||
#if !defined(NAZARA_CURL_FUNCTION)
|
||||
#error You must define NAZARA_CURL_FUNCTION before including this file
|
||||
#endif
|
||||
|
||||
#ifndef NAZARA_CURL_FUNCTION_LAST
|
||||
#define NAZARA_CURL_FUNCTION_LAST(F) NAZARA_CURL_FUNCTION(F)
|
||||
#endif
|
||||
|
||||
NAZARA_CURL_FUNCTION(easy_cleanup)
|
||||
NAZARA_CURL_FUNCTION(easy_getinfo)
|
||||
NAZARA_CURL_FUNCTION(easy_init)
|
||||
NAZARA_CURL_FUNCTION(easy_setopt)
|
||||
NAZARA_CURL_FUNCTION(easy_strerror)
|
||||
NAZARA_CURL_FUNCTION(global_cleanup)
|
||||
NAZARA_CURL_FUNCTION(global_init)
|
||||
NAZARA_CURL_FUNCTION(multi_add_handle)
|
||||
NAZARA_CURL_FUNCTION(multi_cleanup)
|
||||
NAZARA_CURL_FUNCTION(multi_info_read)
|
||||
NAZARA_CURL_FUNCTION(multi_init)
|
||||
NAZARA_CURL_FUNCTION(multi_perform)
|
||||
NAZARA_CURL_FUNCTION(multi_remove_handle)
|
||||
NAZARA_CURL_FUNCTION(multi_strerror)
|
||||
NAZARA_CURL_FUNCTION(slist_append)
|
||||
NAZARA_CURL_FUNCTION(slist_free_all)
|
||||
NAZARA_CURL_FUNCTION_LAST(version_info)
|
||||
|
||||
#undef NAZARA_CURL_FUNCTION
|
||||
#undef NAZARA_CURL_FUNCTION_LAST
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
// Copyright (C) 2023 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
|
||||
// This file is part of the "Nazara Engine - Network module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef NAZARA_NETWORK_CURLLIBRARY_HPP
|
||||
#define NAZARA_NETWORK_CURLLIBRARY_HPP
|
||||
|
||||
#include <Nazara/Core/DynLib.hpp>
|
||||
#include <Nazara/Network/Config.hpp>
|
||||
#include <curl/curl.h>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
class NAZARA_NETWORK_API CurlLibrary
|
||||
{
|
||||
public:
|
||||
inline CurlLibrary();
|
||||
CurlLibrary(const CurlLibrary&) = delete;
|
||||
CurlLibrary(CurlLibrary&&) = delete;
|
||||
inline ~CurlLibrary();
|
||||
|
||||
inline bool IsLoaded() const;
|
||||
|
||||
bool Load();
|
||||
|
||||
void Unload();
|
||||
|
||||
CurlLibrary& operator=(const CurlLibrary&) = delete;
|
||||
CurlLibrary& operator=(CurlLibrary&&) = delete;
|
||||
|
||||
#define NAZARA_CURL_FUNCTION(name) decltype(&::curl_##name) name;
|
||||
#include <Nazara/Network/CurlFunctions.hpp>
|
||||
|
||||
private:
|
||||
DynLib m_library;
|
||||
bool m_isInitialized;
|
||||
};
|
||||
}
|
||||
|
||||
#include <Nazara/Network/CurlLibrary.inl>
|
||||
|
||||
#endif // NAZARA_NETWORK_CURLLIBRARY_HPP
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
// Copyright (C) 2023 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
|
||||
// This file is part of the "Nazara Engine - Network module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Network/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
inline CurlLibrary::CurlLibrary() :
|
||||
m_isInitialized(false)
|
||||
{
|
||||
}
|
||||
|
||||
inline CurlLibrary::~CurlLibrary()
|
||||
{
|
||||
Unload();
|
||||
}
|
||||
|
||||
inline bool CurlLibrary::IsLoaded() const
|
||||
{
|
||||
return m_isInitialized;
|
||||
}
|
||||
}
|
||||
|
||||
#include <Nazara/Network/DebugOff.hpp>
|
||||
|
|
@ -10,9 +10,12 @@
|
|||
#include <NazaraUtils/Prerequisites.hpp>
|
||||
#include <Nazara/Core/Core.hpp>
|
||||
#include <Nazara/Network/Config.hpp>
|
||||
#include <memory>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
class WebService;
|
||||
|
||||
class NAZARA_NETWORK_API Network : public ModuleBase<Network>
|
||||
{
|
||||
friend ModuleBase;
|
||||
|
|
@ -20,12 +23,22 @@ namespace Nz
|
|||
public:
|
||||
using Dependencies = TypeList<Core>;
|
||||
|
||||
struct Config {};
|
||||
struct Config;
|
||||
|
||||
Network(Config /*config*/);
|
||||
Network(Config config);
|
||||
~Network();
|
||||
|
||||
std::unique_ptr<WebService> InstantiateWebService();
|
||||
|
||||
struct Config
|
||||
{
|
||||
// Initialize web services and fails module initialization if it failed to initialize them
|
||||
bool webServices = false;
|
||||
};
|
||||
|
||||
private:
|
||||
std::unique_ptr<class CurlLibrary> m_curlLibrary;
|
||||
|
||||
static Network* s_instance;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,75 @@
|
|||
// Copyright (C) 2023 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
|
||||
// This file is part of the "Nazara Engine - Network module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef NAZARA_NETWORK_WEBREQUEST_HPP
|
||||
#define NAZARA_NETWORK_WEBREQUEST_HPP
|
||||
|
||||
#include <Nazara/Network/Config.hpp>
|
||||
#include <Nazara/Network/Enums.hpp>
|
||||
#include <Nazara/Network/WebRequestResult.hpp>
|
||||
#include <NazaraUtils/MovablePtr.hpp>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
struct curl_slist;
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
class WebService;
|
||||
|
||||
class NAZARA_NETWORK_API WebRequest
|
||||
{
|
||||
friend WebService;
|
||||
|
||||
public:
|
||||
using DataCallback = std::function<bool(const void* data, std::size_t length)>;
|
||||
using ResultCallback = std::function<void(WebRequestResult&& result)>;
|
||||
|
||||
WebRequest(WebService& owner);
|
||||
WebRequest(const WebRequest&) = delete;
|
||||
WebRequest(WebRequest&&) = default;
|
||||
~WebRequest();
|
||||
|
||||
void ForceProtocol(NetProtocol protocol);
|
||||
|
||||
inline void SetDataCallback(DataCallback callback);
|
||||
inline void SetHeader(std::string header, std::string value);
|
||||
void SetJSonContent(std::string_view encodedJSon);
|
||||
void SetMaximumFileSize(UInt64 maxFileSize);
|
||||
inline void SetResultCallback(ResultCallback callback);
|
||||
void SetServiceName(const std::string_view& serviceName);
|
||||
void SetURL(const std::string& url);
|
||||
|
||||
void SetupGet();
|
||||
void SetupPost();
|
||||
|
||||
WebRequest& operator=(const WebRequest&) = delete;
|
||||
WebRequest& operator=(WebRequest&&) = default;
|
||||
|
||||
static std::unique_ptr<WebRequest> Get(const std::string& url, ResultCallback callback = nullptr);
|
||||
static std::unique_ptr<WebRequest> Post(const std::string& url, ResultCallback callback = nullptr);
|
||||
|
||||
private:
|
||||
inline bool OnBodyResponse(const char* data, std::size_t length);
|
||||
CURL* Prepare();
|
||||
inline void TriggerCallback();
|
||||
inline void TriggerCallback(std::string errorMessage);
|
||||
|
||||
std::string m_responseBody;
|
||||
std::unordered_map<std::string, std::string> m_headers;
|
||||
WebService& m_webService;
|
||||
DataCallback m_dataCallback;
|
||||
MovablePtr<CURL> m_curlHandle;
|
||||
MovablePtr<curl_slist> m_headerList;
|
||||
ResultCallback m_resultCallback;
|
||||
bool m_isUserAgentSet;
|
||||
};
|
||||
}
|
||||
|
||||
#include <Nazara/Network/WebRequest.inl>
|
||||
|
||||
#endif // NAZARA_NETWORK_WEBREQUEST_HPP
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
// Copyright (C) 2023 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
|
||||
// This file is part of the "Nazara Engine - Network module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Network/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
inline void WebRequest::SetDataCallback(DataCallback callback)
|
||||
{
|
||||
m_dataCallback = std::move(callback);
|
||||
}
|
||||
|
||||
inline void WebRequest::SetResultCallback(ResultCallback callback)
|
||||
{
|
||||
m_resultCallback = std::move(callback);
|
||||
}
|
||||
|
||||
inline void WebRequest::SetHeader(std::string header, std::string value)
|
||||
{
|
||||
m_headers.insert_or_assign(std::move(header), std::move(value));
|
||||
}
|
||||
|
||||
inline bool WebRequest::OnBodyResponse(const char* data, std::size_t length)
|
||||
{
|
||||
if (!m_dataCallback)
|
||||
{
|
||||
m_responseBody.append(data, length);
|
||||
return true;
|
||||
}
|
||||
|
||||
return m_dataCallback(data, length);
|
||||
}
|
||||
|
||||
inline void WebRequest::TriggerCallback()
|
||||
{
|
||||
m_resultCallback(WebRequestResult(m_webService, m_curlHandle.Get(), std::move(m_responseBody)));
|
||||
m_responseBody.clear();
|
||||
}
|
||||
|
||||
inline void WebRequest::TriggerCallback(std::string errorMessage)
|
||||
{
|
||||
m_resultCallback(WebRequestResult(m_webService, std::move(errorMessage)));
|
||||
}
|
||||
}
|
||||
|
||||
#include <Nazara/Network/DebugOff.hpp>
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
// Copyright (C) 2023 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
|
||||
// This file is part of the "Nazara Engine - Network module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef NAZARA_NETWORK_WEBREQUESTRESULT_HPP
|
||||
#define NAZARA_NETWORK_WEBREQUESTRESULT_HPP
|
||||
|
||||
#include <Nazara/Network/Config.hpp>
|
||||
#include <NazaraUtils/MovablePtr.hpp>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
using CURL = void;
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
class WebService;
|
||||
|
||||
class NAZARA_NETWORK_API WebRequestResult
|
||||
{
|
||||
friend class WebRequest;
|
||||
|
||||
public:
|
||||
WebRequestResult(const WebRequestResult&) = delete;
|
||||
WebRequestResult(WebRequestResult&&) = delete;
|
||||
~WebRequestResult() = default;
|
||||
|
||||
inline std::string& GetBody();
|
||||
inline const std::string& GetBody() const;
|
||||
Nz::UInt64 GetDownloadedSize() const;
|
||||
Nz::UInt64 GetDownloadSpeed() const;
|
||||
inline const std::string& GetErrorMessage() const;
|
||||
long GetReponseCode() const;
|
||||
|
||||
inline bool HasSucceeded() const;
|
||||
|
||||
inline explicit operator bool() const;
|
||||
|
||||
WebRequestResult& operator=(const WebRequestResult&) = delete;
|
||||
WebRequestResult& operator=(WebRequestResult&&) = delete;
|
||||
|
||||
private:
|
||||
inline WebRequestResult(WebService& webService, CURL* curl, std::string body);
|
||||
inline WebRequestResult(WebService& webService, std::string errMessage);
|
||||
|
||||
CURL* m_curlHandle;
|
||||
WebService& m_webService;
|
||||
std::string m_bodyOrErr;
|
||||
};
|
||||
}
|
||||
|
||||
#include <Nazara/Network/WebRequestResult.inl>
|
||||
|
||||
#endif // NAZARA_NETWORK_WEBREQUESTRESULT_HPP
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
// Copyright (C) 2023 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
|
||||
// This file is part of the "Nazara Engine - Network module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Network/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
inline WebRequestResult::WebRequestResult(WebService& webService, CURL* curl, std::string body) :
|
||||
m_curlHandle(curl),
|
||||
m_webService(webService),
|
||||
m_bodyOrErr(std::move(body))
|
||||
{
|
||||
}
|
||||
|
||||
inline WebRequestResult::WebRequestResult(WebService& webService, std::string errMessage) :
|
||||
m_curlHandle(nullptr),
|
||||
m_webService(webService),
|
||||
m_bodyOrErr(std::move(errMessage))
|
||||
{
|
||||
}
|
||||
|
||||
inline std::string& WebRequestResult::GetBody()
|
||||
{
|
||||
assert(HasSucceeded());
|
||||
return m_bodyOrErr;
|
||||
}
|
||||
|
||||
inline const std::string& WebRequestResult::GetBody() const
|
||||
{
|
||||
assert(HasSucceeded());
|
||||
return m_bodyOrErr;
|
||||
}
|
||||
|
||||
inline const std::string& WebRequestResult::GetErrorMessage() const
|
||||
{
|
||||
assert(!HasSucceeded());
|
||||
return m_bodyOrErr;
|
||||
}
|
||||
|
||||
inline bool WebRequestResult::HasSucceeded() const
|
||||
{
|
||||
return m_curlHandle != nullptr;
|
||||
}
|
||||
|
||||
inline WebRequestResult::operator bool() const
|
||||
{
|
||||
return HasSucceeded();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include <Nazara/Network/DebugOff.hpp>
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
// Copyright (C) 2023 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
|
||||
// This file is part of the "Nazara Engine - Network module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef NAZARA_NETWORK_WEBSERVICE_HPP
|
||||
#define NAZARA_NETWORK_WEBSERVICE_HPP
|
||||
|
||||
#include <Nazara/Network/Config.hpp>
|
||||
#include <Nazara/Network/WebRequest.hpp>
|
||||
#include <NazaraUtils/MovablePtr.hpp>
|
||||
|
||||
using CURLM = void;
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
class CurlLibrary;
|
||||
class Logger;
|
||||
|
||||
class NAZARA_NETWORK_API WebService
|
||||
{
|
||||
friend WebRequest;
|
||||
friend WebRequestResult;
|
||||
|
||||
public:
|
||||
WebService(const CurlLibrary& library);
|
||||
WebService(const WebService&) = delete;
|
||||
WebService(WebService&&) = delete;
|
||||
~WebService();
|
||||
|
||||
inline std::unique_ptr<WebRequest> AllocateRequest();
|
||||
|
||||
inline std::unique_ptr<WebRequest> CreateGetRequest(const std::string& url, WebRequest::ResultCallback callback);
|
||||
inline std::unique_ptr<WebRequest> CreatePostRequest(const std::string& url, WebRequest::ResultCallback callback);
|
||||
|
||||
inline const std::string& GetUserAgent() const;
|
||||
|
||||
void Poll();
|
||||
|
||||
void QueueRequest(std::unique_ptr<WebRequest>&& request);
|
||||
|
||||
WebService& operator=(const WebService&) = delete;
|
||||
WebService& operator=(WebService&&) = delete;
|
||||
|
||||
private:
|
||||
inline const CurlLibrary& GetCurlLibrary() const;
|
||||
|
||||
std::string m_userAgent;
|
||||
std::unordered_map<CURL*, std::unique_ptr<WebRequest>> m_activeRequests;
|
||||
const CurlLibrary& m_curl;
|
||||
MovablePtr<CURLM> m_curlMulti;
|
||||
};
|
||||
}
|
||||
|
||||
#include <Nazara/Network/WebService.inl>
|
||||
|
||||
#endif // NAZARA_NETWORK_WEBSERVICE_HPP
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
// Copyright (C) 2023 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
|
||||
// This file is part of the "Nazara Engine - Network module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Network/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
inline std::unique_ptr<WebRequest> WebService::AllocateRequest()
|
||||
{
|
||||
return std::make_unique<WebRequest>(*this);
|
||||
}
|
||||
|
||||
inline std::unique_ptr<WebRequest> WebService::CreateGetRequest(const std::string& url, WebRequest::ResultCallback callback)
|
||||
{
|
||||
std::unique_ptr<WebRequest> request = AllocateRequest();
|
||||
request->SetURL(url);
|
||||
request->SetResultCallback(std::move(callback));
|
||||
request->SetupGet();
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
inline std::unique_ptr<WebRequest> WebService::CreatePostRequest(const std::string& url, WebRequest::ResultCallback callback)
|
||||
{
|
||||
std::unique_ptr<WebRequest> request = AllocateRequest();
|
||||
request->SetURL(url);
|
||||
request->SetResultCallback(std::move(callback));
|
||||
request->SetupPost();
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
inline const std::string& WebService::GetUserAgent() const
|
||||
{
|
||||
return m_userAgent;
|
||||
}
|
||||
|
||||
const CurlLibrary& WebService::GetCurlLibrary() const
|
||||
{
|
||||
return m_curl;
|
||||
}
|
||||
}
|
||||
|
||||
#include <Nazara/Network/DebugOff.hpp>
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
// Copyright (C) 2023 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
|
||||
// This file is part of the "Nazara Engine - Network module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Network/CurlLibrary.hpp>
|
||||
#include <Nazara/Core/Error.hpp>
|
||||
#include <Nazara/Core/ErrorFlags.hpp>
|
||||
#include <NazaraUtils/CallOnExit.hpp>
|
||||
#include <Nazara/Network/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
bool CurlLibrary::Load()
|
||||
{
|
||||
Unload();
|
||||
|
||||
CallOnExit unloadOnFailure([this] { Unload(); });
|
||||
|
||||
auto PostLoad = [&]
|
||||
{
|
||||
if (global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK)
|
||||
{
|
||||
NazaraError("failed to initialize libcurl");
|
||||
return false;
|
||||
}
|
||||
m_isInitialized = true;
|
||||
|
||||
unloadOnFailure.Reset();
|
||||
return true;
|
||||
};
|
||||
|
||||
#ifndef NAZARA_NETWORK_LIBCURL_LINK
|
||||
for (const char* libname : { "libcurl" NAZARA_DYNLIB_EXTENSION, "libcurl-d" NAZARA_DYNLIB_EXTENSION })
|
||||
{
|
||||
ErrorFlags errFlags(Nz::ErrorMode::Silent);
|
||||
if (m_library.Load(libname))
|
||||
break;
|
||||
}
|
||||
|
||||
if (!m_library.IsLoaded())
|
||||
{
|
||||
NazaraError("failed to load libcurl: " + m_library.GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
auto LoadSymbol = [this](const char* name, bool optional)
|
||||
{
|
||||
DynLibFunc funcPtr = m_library.GetSymbol(name);
|
||||
if (!funcPtr && !optional)
|
||||
throw std::runtime_error(std::string("failed to load ") + name);
|
||||
|
||||
return funcPtr;
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
#define NAZARA_CURL_FUNCTION(name) name = reinterpret_cast<decltype(&::curl_##name)>(LoadSymbol("curl_" #name, false));
|
||||
#include <Nazara/Network/CurlFunctions.hpp>
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
NazaraError(e.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
return PostLoad();
|
||||
#else
|
||||
// libcurl is linked to the executable
|
||||
|
||||
#define NAZARA_CURL_FUNCTION(name) name = &::curl_##name;
|
||||
#include <Nazara/Network/CurlFunctions.hpp>
|
||||
|
||||
return PostLoad();
|
||||
#endif
|
||||
}
|
||||
|
||||
void CurlLibrary::Unload()
|
||||
{
|
||||
if (!m_library.IsLoaded())
|
||||
return;
|
||||
|
||||
if (m_isInitialized)
|
||||
global_cleanup();
|
||||
|
||||
#define NAZARA_CURL_FUNCTION(name) name = nullptr;
|
||||
#include <Nazara/Network/CurlFunctions.hpp>
|
||||
|
||||
m_library.Unload();
|
||||
}
|
||||
}
|
||||
|
|
@ -7,7 +7,9 @@
|
|||
#include <Nazara/Core/Error.hpp>
|
||||
#include <Nazara/Core/Log.hpp>
|
||||
#include <Nazara/Network/Config.hpp>
|
||||
#include <Nazara/Network/CurlLibrary.hpp>
|
||||
#include <Nazara/Network/NetPacket.hpp>
|
||||
#include <Nazara/Network/WebService.hpp>
|
||||
#include <NazaraUtils/CallOnExit.hpp>
|
||||
|
||||
#if defined(NAZARA_PLATFORM_WINDOWS)
|
||||
|
|
@ -30,7 +32,7 @@ namespace Nz
|
|||
* \brief Network class that represents the module initializer of Network
|
||||
*/
|
||||
|
||||
Network::Network(Config /*config*/) :
|
||||
Network::Network(Config config) :
|
||||
ModuleBase("Network", this)
|
||||
{
|
||||
// Initialize module here
|
||||
|
|
@ -39,14 +41,37 @@ namespace Nz
|
|||
|
||||
if (!NetPacket::Initialize())
|
||||
throw std::runtime_error("failed to initialize packets");
|
||||
|
||||
if (config.webServices)
|
||||
{
|
||||
m_curlLibrary = std::make_unique<CurlLibrary>();
|
||||
if (!m_curlLibrary->Load())
|
||||
throw std::runtime_error("failed to initialize curl");
|
||||
}
|
||||
}
|
||||
|
||||
Network::~Network()
|
||||
{
|
||||
// Uninitialize module here
|
||||
m_curlLibrary.reset();
|
||||
|
||||
NetPacket::Uninitialize();
|
||||
SocketImpl::Uninitialize();
|
||||
}
|
||||
|
||||
std::unique_ptr<WebService> Network::InstantiateWebService()
|
||||
{
|
||||
if (!m_curlLibrary)
|
||||
{
|
||||
std::unique_ptr<CurlLibrary> curlLibrary = std::make_unique<CurlLibrary>();
|
||||
if (!curlLibrary->Load())
|
||||
throw std::runtime_error("failed to initialize curl");
|
||||
|
||||
m_curlLibrary = std::move(curlLibrary);
|
||||
}
|
||||
|
||||
return std::make_unique<WebService>(*m_curlLibrary);
|
||||
}
|
||||
|
||||
Network* Network::s_instance = nullptr;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,133 @@
|
|||
// Copyright (C) 2023 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
|
||||
// This file is part of the "Nazara Engine - Network module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Network/WebRequest.hpp>
|
||||
#include <Nazara/Network/CurlLibrary.hpp> //< include last because of curl/curl.h
|
||||
#include <Nazara/Network/WebService.hpp>
|
||||
#include <Nazara/Network/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
WebRequest::WebRequest(WebService& webService) :
|
||||
m_webService(webService),
|
||||
m_isUserAgentSet(false)
|
||||
{
|
||||
auto& libcurl = m_webService.GetCurlLibrary();
|
||||
|
||||
m_curlHandle = libcurl.easy_init();
|
||||
}
|
||||
|
||||
WebRequest::~WebRequest()
|
||||
{
|
||||
auto& libcurl = m_webService.GetCurlLibrary();
|
||||
if (m_curlHandle)
|
||||
libcurl.easy_cleanup(m_curlHandle);
|
||||
|
||||
if (m_headerList)
|
||||
libcurl.slist_free_all(m_headerList);
|
||||
}
|
||||
|
||||
void WebRequest::ForceProtocol(Nz::NetProtocol protocol)
|
||||
{
|
||||
assert(protocol != Nz::NetProtocol::Unknown);
|
||||
|
||||
auto& libcurl = m_webService.GetCurlLibrary();
|
||||
switch (protocol)
|
||||
{
|
||||
case Nz::NetProtocol::Any:
|
||||
libcurl.easy_setopt(m_curlHandle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_WHATEVER);
|
||||
break;
|
||||
|
||||
case Nz::NetProtocol::IPv4:
|
||||
libcurl.easy_setopt(m_curlHandle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
|
||||
break;
|
||||
|
||||
case Nz::NetProtocol::IPv6:
|
||||
libcurl.easy_setopt(m_curlHandle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6);
|
||||
break;
|
||||
|
||||
case Nz::NetProtocol::Unknown:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void WebRequest::SetJSonContent(std::string_view encodedJSon)
|
||||
{
|
||||
auto& libcurl = m_webService.GetCurlLibrary();
|
||||
|
||||
SetHeader("Content-Type", "application/json");
|
||||
libcurl.easy_setopt(m_curlHandle, CURLOPT_POSTFIELDSIZE_LARGE, curl_off_t(encodedJSon.size()));
|
||||
libcurl.easy_setopt(m_curlHandle, CURLOPT_COPYPOSTFIELDS, encodedJSon.data());
|
||||
}
|
||||
|
||||
void WebRequest::SetMaximumFileSize(Nz::UInt64 maxFileSize)
|
||||
{
|
||||
auto& libcurl = m_webService.GetCurlLibrary();
|
||||
|
||||
curl_off_t maxSize = maxFileSize;
|
||||
libcurl.easy_setopt(m_curlHandle, CURLOPT_MAXFILESIZE_LARGE, maxSize);
|
||||
}
|
||||
|
||||
void WebRequest::SetServiceName(const std::string_view& serviceName)
|
||||
{
|
||||
auto& libcurl = m_webService.GetCurlLibrary();
|
||||
|
||||
if (!serviceName.empty())
|
||||
{
|
||||
//TODO Nz::StackString?
|
||||
std::string userAgent = m_webService.GetUserAgent();
|
||||
userAgent.reserve(userAgent.size() + 3 + serviceName.size());
|
||||
userAgent.append(" - ");
|
||||
userAgent.append(serviceName.data(), serviceName.size());
|
||||
|
||||
libcurl.easy_setopt(m_curlHandle, CURLOPT_USERAGENT, userAgent.data());
|
||||
}
|
||||
else
|
||||
libcurl.easy_setopt(m_curlHandle, CURLOPT_USERAGENT, m_webService.GetUserAgent().c_str());
|
||||
|
||||
m_isUserAgentSet = true;
|
||||
}
|
||||
|
||||
void WebRequest::SetURL(const std::string& url)
|
||||
{
|
||||
auto& libcurl = m_webService.GetCurlLibrary();
|
||||
|
||||
libcurl.easy_setopt(m_curlHandle, CURLOPT_URL, url.data());
|
||||
}
|
||||
|
||||
void WebRequest::SetupGet()
|
||||
{
|
||||
auto& libcurl = m_webService.GetCurlLibrary();
|
||||
|
||||
libcurl.easy_setopt(m_curlHandle, CURLOPT_HTTPGET, long(1));
|
||||
}
|
||||
|
||||
void WebRequest::SetupPost()
|
||||
{
|
||||
auto& libcurl = m_webService.GetCurlLibrary();
|
||||
|
||||
libcurl.easy_setopt(m_curlHandle, CURLOPT_POST, long(1));
|
||||
}
|
||||
|
||||
CURL* WebRequest::Prepare()
|
||||
{
|
||||
auto& libcurl = m_webService.GetCurlLibrary();
|
||||
|
||||
if (!m_headers.empty())
|
||||
{
|
||||
for (auto&& [header, value] : m_headers)
|
||||
{
|
||||
std::string headerValue = (!value.empty()) ? header + ": " + value : header + ";";
|
||||
m_headerList = libcurl.slist_append(m_headerList, headerValue.c_str());
|
||||
}
|
||||
|
||||
libcurl.easy_setopt(m_curlHandle, CURLOPT_HTTPHEADER, m_headerList.Get());
|
||||
}
|
||||
|
||||
if (!m_isUserAgentSet)
|
||||
libcurl.easy_setopt(m_curlHandle, CURLOPT_USERAGENT, m_webService.GetUserAgent().c_str());
|
||||
|
||||
return m_curlHandle;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
// Copyright (C) 2023 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
|
||||
// This file is part of the "Nazara Engine - Network module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Network/WebRequestResult.hpp>
|
||||
#include <Nazara/Network/CurlLibrary.hpp> //< include last because of curl/curl.h
|
||||
#include <Nazara/Network/WebService.hpp>
|
||||
#include <Nazara/Network/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
Nz::UInt64 WebRequestResult::GetDownloadedSize() const
|
||||
{
|
||||
assert(HasSucceeded());
|
||||
|
||||
auto& libcurl = m_webService.GetCurlLibrary();
|
||||
|
||||
curl_off_t downloadedSize = 0;
|
||||
libcurl.easy_getinfo(m_curlHandle, CURLINFO_SIZE_DOWNLOAD_T, &downloadedSize);
|
||||
|
||||
return downloadedSize;
|
||||
}
|
||||
|
||||
std::size_t WebRequestResult::GetDownloadSpeed() const
|
||||
{
|
||||
assert(HasSucceeded());
|
||||
|
||||
auto& libcurl = m_webService.GetCurlLibrary();
|
||||
|
||||
curl_off_t downloadSpeed = 0;
|
||||
libcurl.easy_getinfo(m_curlHandle, CURLINFO_SPEED_DOWNLOAD_T, &downloadSpeed);
|
||||
|
||||
return downloadSpeed;
|
||||
}
|
||||
|
||||
long WebRequestResult::GetReponseCode() const
|
||||
{
|
||||
assert(HasSucceeded());
|
||||
|
||||
auto& libcurl = m_webService.GetCurlLibrary();
|
||||
|
||||
long responseCode;
|
||||
libcurl.easy_getinfo(m_curlHandle, CURLINFO_RESPONSE_CODE, &responseCode);
|
||||
|
||||
return responseCode;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
// Copyright (C) 2023 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
|
||||
// This file is part of the "Nazara Engine - Network module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Network/WebService.hpp>
|
||||
#include <Nazara/Core/Error.hpp>
|
||||
#include <Nazara/Core/ErrorFlags.hpp>
|
||||
#include <Nazara/Network/CurlLibrary.hpp> //< include last because of curl/curl.h
|
||||
#include <fmt/format.h>
|
||||
#include <Nazara/Network/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
WebService::WebService(const CurlLibrary& curl) :
|
||||
m_curl(curl)
|
||||
{
|
||||
curl_version_info_data* curlVersionData = m_curl.version_info(CURLVERSION_NOW);
|
||||
|
||||
m_userAgent = fmt::format("Nazara WebService - curl/{}", curlVersionData->version);
|
||||
|
||||
m_curlMulti = m_curl.multi_init();
|
||||
}
|
||||
|
||||
WebService::~WebService()
|
||||
{
|
||||
if (m_curlMulti)
|
||||
{
|
||||
for (auto&& [handle, request] : m_activeRequests)
|
||||
m_curl.multi_remove_handle(m_curlMulti, handle);
|
||||
|
||||
m_curl.multi_cleanup(m_curlMulti);
|
||||
}
|
||||
}
|
||||
|
||||
void WebService::Poll()
|
||||
{
|
||||
assert(m_curlMulti);
|
||||
|
||||
int reportedActiveRequest;
|
||||
CURLMcode err = m_curl.multi_perform(m_curlMulti, &reportedActiveRequest);
|
||||
if (err != CURLM_OK)
|
||||
{
|
||||
NazaraError(fmt::format("[WebService] curl_multi_perform failed with {0}: {1}", UnderlyingCast(err), m_curl.multi_strerror(err)));
|
||||
return;
|
||||
}
|
||||
|
||||
CURLMsg* m;
|
||||
do
|
||||
{
|
||||
int msgq;
|
||||
m = m_curl.multi_info_read(m_curlMulti, &msgq);
|
||||
if (m && (m->msg == CURLMSG_DONE))
|
||||
{
|
||||
CURL* handle = m->easy_handle;
|
||||
|
||||
auto it = m_activeRequests.find(handle);
|
||||
assert(it != m_activeRequests.end());
|
||||
|
||||
WebRequest& request = *it->second;
|
||||
|
||||
if (m->data.result == CURLE_OK)
|
||||
request.TriggerCallback();
|
||||
else
|
||||
request.TriggerCallback(m_curl.easy_strerror(m->data.result));
|
||||
|
||||
m_curl.multi_remove_handle(m_curlMulti, handle);
|
||||
|
||||
m_activeRequests.erase(handle);
|
||||
}
|
||||
}
|
||||
while (m);
|
||||
}
|
||||
|
||||
void WebService::QueueRequest(std::unique_ptr<WebRequest>&& request)
|
||||
{
|
||||
assert(m_curlMulti);
|
||||
assert(request);
|
||||
|
||||
CURL* handle = request->Prepare();
|
||||
|
||||
curl_write_callback writeCallback = [](char* ptr, std::size_t size, std::size_t nmemb, void* userdata) -> std::size_t
|
||||
{
|
||||
WebRequest* request = static_cast<WebRequest*>(userdata);
|
||||
|
||||
std::size_t totalSize = size * nmemb;
|
||||
if (!request->OnBodyResponse(ptr, totalSize))
|
||||
return 0;
|
||||
|
||||
return totalSize;
|
||||
};
|
||||
|
||||
m_curl.easy_setopt(handle, CURLOPT_WRITEFUNCTION, writeCallback);
|
||||
m_curl.easy_setopt(handle, CURLOPT_WRITEDATA, request.get());
|
||||
|
||||
m_activeRequests.emplace(handle, std::move(request));
|
||||
|
||||
m_curl.multi_add_handle(m_curlMulti, handle);
|
||||
}
|
||||
}
|
||||
19
xmake.lua
19
xmake.lua
|
|
@ -127,7 +127,15 @@ local modules = {
|
|||
Network = {
|
||||
Option = "network",
|
||||
Deps = {"NazaraCore"},
|
||||
Custom = function()
|
||||
Packages = { "fmt" },
|
||||
Custom = function ()
|
||||
if has_config("link_curl") then
|
||||
add_defines("NAZARA_NETWORK_CURL_LINK")
|
||||
add_packages("libcurl")
|
||||
else
|
||||
add_packages("libcurl", { links = {} })
|
||||
end
|
||||
|
||||
if is_plat("windows", "mingw") then
|
||||
add_syslinks("ws2_32")
|
||||
end
|
||||
|
|
@ -216,6 +224,7 @@ option("compile_shaders", { description = "Compile nzsl shaders into an includab
|
|||
option("embed_rendererbackends", { description = "Embed renderer backend code into NazaraRenderer instead of loading them dynamically", default = is_plat("wasm") or false })
|
||||
option("embed_resources", { description = "Turn builtin resources into includable headers", default = true })
|
||||
option("embed_plugins", { description = "Embed enabled plugins code as static libraries", default = is_plat("wasm") or false })
|
||||
option("link_curl", { description = "Link libcurl in the executable instead of dynamically loading it", default = false })
|
||||
option("link_openal", { description = "Link OpenAL in the executable instead of dynamically loading it", default = is_plat("wasm") or false })
|
||||
option("override_runtime", { description = "Override vs runtime to MD in release and MDd in debug", default = true })
|
||||
option("usepch", { description = "Use precompiled headers to speedup compilation", default = false })
|
||||
|
|
@ -258,6 +267,14 @@ if has_config("joltphysics") then
|
|||
add_requires("ordered_map")
|
||||
end
|
||||
|
||||
if has_config("network") then
|
||||
if has_config("link_curl") then
|
||||
add_requires("libcurl")
|
||||
else
|
||||
add_requires("libcurl", { configs = { shared = true }})
|
||||
end
|
||||
end
|
||||
|
||||
if has_config("platform") then
|
||||
add_requires("libsdl >=2.26.0")
|
||||
end
|
||||
|
|
|
|||
|
|
@ -76,6 +76,8 @@ on_run(function ()
|
|||
paths["Core"].Excludes["AppEntitySystemComponent.hpp"] = { Define = "NAZARA_ENTT" }
|
||||
paths["Core"].Excludes["EnttSystemGraph.hpp"] = { Define = "NAZARA_ENTT" }
|
||||
paths["Core"].Excludes["EnttWorld.hpp"] = { Define = "NAZARA_ENTT" }
|
||||
paths["Network"].Excludes["CurlLibrary.hpp"] = true
|
||||
paths["Network"].Excludes["CurlFunctions.hpp"] = true
|
||||
paths["OpenGLRenderer"].Excludes["Wrapper.hpp"] = true
|
||||
paths["VulkanRenderer"].Excludes["Wrapper.hpp"] = true
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue