mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-17 21:48:19 +00:00
Use curl-multi
This commit is contained in:
parent
f298b28026
commit
1dc8496470
|
@ -1883,6 +1883,7 @@ set_src(ENGINE_INTERFACE GLOB src/engine
|
|||
friends.h
|
||||
ghost.h
|
||||
graphics.h
|
||||
http.h
|
||||
input.h
|
||||
kernel.h
|
||||
keys.h
|
||||
|
|
|
@ -2524,9 +2524,9 @@ int kill_process(PROCESS process);
|
|||
|
||||
/**
|
||||
* Checks if a process is alive.
|
||||
*
|
||||
*
|
||||
* @param process Handle/PID of the process.
|
||||
*
|
||||
*
|
||||
* @return bool Returns true if the process is currently running, false if the process is not running (dead).
|
||||
*/
|
||||
bool is_process_alive(PROCESS process);
|
||||
|
|
|
@ -1447,7 +1447,7 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket, int Conn, bool Dummy)
|
|||
m_pMapdownloadTask->Timeout(CTimeout{g_Config.m_ClMapDownloadConnectTimeoutMs, 0, g_Config.m_ClMapDownloadLowSpeedLimit, g_Config.m_ClMapDownloadLowSpeedTime});
|
||||
m_pMapdownloadTask->MaxResponseSize(1024 * 1024 * 1024); // 1 GiB
|
||||
m_pMapdownloadTask->ExpectSha256(*pMapSha256);
|
||||
Engine()->AddJob(m_pMapdownloadTask);
|
||||
Http()->Run(m_pMapdownloadTask);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -2664,6 +2664,7 @@ void CClient::RegisterInterfaces()
|
|||
#endif
|
||||
Kernel()->RegisterInterface(static_cast<IFriends *>(&m_Friends), false);
|
||||
Kernel()->ReregisterInterface(static_cast<IFriends *>(&m_Foes));
|
||||
Kernel()->RegisterInterface(static_cast<IHttp *>(&m_Http), false);
|
||||
}
|
||||
|
||||
void CClient::InitInterfaces()
|
||||
|
@ -2688,12 +2689,12 @@ void CClient::InitInterfaces()
|
|||
|
||||
m_DemoEditor.Init(m_pGameClient->NetVersion(), &m_SnapshotDelta, m_pConsole, m_pStorage);
|
||||
|
||||
m_Http.Init(std::chrono::seconds{1});
|
||||
|
||||
m_ServerBrowser.SetBaseInfo(&m_aNetClient[CONN_CONTACT], m_pGameClient->NetVersion());
|
||||
|
||||
HttpInit(m_pStorage);
|
||||
|
||||
#if defined(CONF_AUTOUPDATE)
|
||||
m_Updater.Init();
|
||||
m_Updater.Init(&m_Http);
|
||||
#endif
|
||||
|
||||
m_pConfigManager->RegisterCallback(IFavorites::ConfigSaveCallback, m_pFavorites);
|
||||
|
@ -4582,7 +4583,7 @@ void CClient::RequestDDNetInfo()
|
|||
m_pDDNetInfoTask = HttpGetFile(aUrl, Storage(), m_aDDNetInfoTmp, IStorage::TYPE_SAVE);
|
||||
m_pDDNetInfoTask->Timeout(CTimeout{10000, 0, 500, 10});
|
||||
m_pDDNetInfoTask->IpResolve(IPRESOLVE::V4);
|
||||
Engine()->AddJob(m_pDDNetInfoTask);
|
||||
Http()->Run(m_pDDNetInfoTask);
|
||||
}
|
||||
|
||||
int CClient::GetPredictionTime()
|
||||
|
|
|
@ -76,6 +76,7 @@ class CClient : public IClient, public CDemoPlayer::IListener
|
|||
IStorage *m_pStorage = nullptr;
|
||||
IEngineTextRender *m_pTextRender = nullptr;
|
||||
IUpdater *m_pUpdater = nullptr;
|
||||
CHttp m_Http;
|
||||
|
||||
CNetClient m_aNetClient[NUM_CONNS];
|
||||
CDemoPlayer m_DemoPlayer;
|
||||
|
@ -266,6 +267,7 @@ public:
|
|||
IStorage *Storage() { return m_pStorage; }
|
||||
IEngineTextRender *TextRender() { return m_pTextRender; }
|
||||
IUpdater *Updater() { return m_pUpdater; }
|
||||
IHttp *Http() { return &m_Http; }
|
||||
|
||||
CClient();
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <engine/engine.h>
|
||||
#include <engine/favorites.h>
|
||||
#include <engine/friends.h>
|
||||
#include <engine/http.h>
|
||||
#include <engine/storage.h>
|
||||
|
||||
class CSortWrap
|
||||
|
@ -95,6 +96,8 @@ void CServerBrowser::SetBaseInfo(class CNetClient *pClient, const char *pNetVers
|
|||
m_pFavorites = Kernel()->RequestInterface<IFavorites>();
|
||||
m_pFriends = Kernel()->RequestInterface<IFriends>();
|
||||
m_pStorage = Kernel()->RequestInterface<IStorage>();
|
||||
m_pHttpClient = Kernel()->RequestInterface<IHttp>();
|
||||
dbg_msg("cserverbrowser", "%p", m_pHttpClient);
|
||||
m_pPingCache = CreateServerBrowserPingCache(m_pConsole, m_pStorage);
|
||||
|
||||
RegisterCommands();
|
||||
|
@ -102,7 +105,7 @@ void CServerBrowser::SetBaseInfo(class CNetClient *pClient, const char *pNetVers
|
|||
|
||||
void CServerBrowser::OnInit()
|
||||
{
|
||||
m_pHttp = CreateServerBrowserHttp(m_pEngine, m_pConsole, m_pStorage, g_Config.m_BrCachedBestServerinfoUrl);
|
||||
m_pHttp = CreateServerBrowserHttp(m_pEngine, m_pConsole, m_pStorage, m_pHttpClient, g_Config.m_BrCachedBestServerinfoUrl);
|
||||
}
|
||||
|
||||
void CServerBrowser::RegisterCommands()
|
||||
|
|
|
@ -21,6 +21,7 @@ class IFriends;
|
|||
class IServerBrowserHttp;
|
||||
class IServerBrowserPingCache;
|
||||
class IStorage;
|
||||
class IHttp;
|
||||
|
||||
class CCommunityServer
|
||||
{
|
||||
|
@ -143,6 +144,7 @@ private:
|
|||
IFriends *m_pFriends = nullptr;
|
||||
IFavorites *m_pFavorites = nullptr;
|
||||
IStorage *m_pStorage = nullptr;
|
||||
IHttp *m_pHttpClient = nullptr;
|
||||
char m_aNetVersion[128];
|
||||
|
||||
bool m_RefreshingHttp = false;
|
||||
|
|
|
@ -29,7 +29,7 @@ public:
|
|||
{
|
||||
MAX_URLS = 16,
|
||||
};
|
||||
CChooseMaster(IEngine *pEngine, VALIDATOR pfnValidator, const char **ppUrls, int NumUrls, int PreviousBestIndex);
|
||||
CChooseMaster(IEngine *pEngine, IHttp *pHttp, VALIDATOR pfnValidator, const char **ppUrls, int NumUrls, int PreviousBestIndex);
|
||||
virtual ~CChooseMaster();
|
||||
|
||||
bool GetBestUrl(const char **pBestUrl) const;
|
||||
|
@ -51,26 +51,29 @@ private:
|
|||
};
|
||||
class CJob : public IJob
|
||||
{
|
||||
CChooseMaster *m_pParent;
|
||||
CLock m_Lock;
|
||||
std::shared_ptr<CData> m_pData;
|
||||
std::unique_ptr<CHttpRequest> m_pHead PT_GUARDED_BY(m_Lock);
|
||||
std::unique_ptr<CHttpRequest> m_pGet PT_GUARDED_BY(m_Lock);
|
||||
std::shared_ptr<CHttpRequest> m_pHead PT_GUARDED_BY(m_Lock);
|
||||
std::shared_ptr<CHttpRequest> m_pGet PT_GUARDED_BY(m_Lock);
|
||||
void Run() override REQUIRES(!m_Lock);
|
||||
|
||||
public:
|
||||
CJob(std::shared_ptr<CData> pData) :
|
||||
m_pData(std::move(pData)) {}
|
||||
CJob(CChooseMaster *pParent, std::shared_ptr<CData> pData) :
|
||||
m_pParent(pParent), m_pData(std::move(pData)) {}
|
||||
void Abort() REQUIRES(!m_Lock);
|
||||
};
|
||||
|
||||
IEngine *m_pEngine;
|
||||
IHttp *m_pHttp;
|
||||
int m_PreviousBestIndex;
|
||||
std::shared_ptr<CData> m_pData;
|
||||
std::shared_ptr<CJob> m_pJob;
|
||||
};
|
||||
|
||||
CChooseMaster::CChooseMaster(IEngine *pEngine, VALIDATOR pfnValidator, const char **ppUrls, int NumUrls, int PreviousBestIndex) :
|
||||
CChooseMaster::CChooseMaster(IEngine *pEngine, IHttp *pHttp, VALIDATOR pfnValidator, const char **ppUrls, int NumUrls, int PreviousBestIndex) :
|
||||
m_pEngine(pEngine),
|
||||
m_pHttp(pHttp),
|
||||
m_PreviousBestIndex(PreviousBestIndex)
|
||||
{
|
||||
dbg_assert(NumUrls >= 0, "no master URLs");
|
||||
|
@ -128,7 +131,7 @@ void CChooseMaster::Reset()
|
|||
void CChooseMaster::Refresh()
|
||||
{
|
||||
if(m_pJob == nullptr || m_pJob->Status() == IJob::STATE_DONE)
|
||||
m_pEngine->AddJob(m_pJob = std::make_shared<CJob>(m_pData));
|
||||
m_pEngine->AddJob(m_pJob = std::make_shared<CJob>(this, m_pData));
|
||||
}
|
||||
|
||||
void CChooseMaster::CJob::Abort()
|
||||
|
@ -171,14 +174,16 @@ void CChooseMaster::CJob::Run()
|
|||
{
|
||||
aTimeMs[i] = -1;
|
||||
const char *pUrl = m_pData->m_aaUrls[aRandomized[i]];
|
||||
CHttpRequest *pHead = HttpHead(pUrl).release();
|
||||
std::shared_ptr<CHttpRequest> pHead = std::move(HttpHead(pUrl));
|
||||
pHead->Timeout(Timeout);
|
||||
pHead->LogProgress(HTTPLOG::FAILURE);
|
||||
{
|
||||
CLockScope ls(m_Lock);
|
||||
m_pHead = std::unique_ptr<CHttpRequest>(pHead);
|
||||
m_pHead = pHead;
|
||||
}
|
||||
IEngine::RunJobBlocking(pHead);
|
||||
|
||||
m_pParent->m_pHttp->Run(pHead);
|
||||
pHead->Wait();
|
||||
if(pHead->State() == HTTP_ABORTED)
|
||||
{
|
||||
dbg_msg("serverbrowse_http", "master chooser aborted");
|
||||
|
@ -188,15 +193,19 @@ void CChooseMaster::CJob::Run()
|
|||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
auto StartTime = time_get_nanoseconds();
|
||||
CHttpRequest *pGet = HttpGet(pUrl).release();
|
||||
std::shared_ptr<CHttpRequest> pGet = std::move(HttpGet(pUrl));
|
||||
pGet->Timeout(Timeout);
|
||||
pGet->LogProgress(HTTPLOG::FAILURE);
|
||||
{
|
||||
CLockScope ls(m_Lock);
|
||||
m_pGet = std::unique_ptr<CHttpRequest>(pGet);
|
||||
m_pGet = pGet;
|
||||
}
|
||||
IEngine::RunJobBlocking(pGet);
|
||||
|
||||
m_pParent->m_pHttp->Run(pGet);
|
||||
pGet->Wait();
|
||||
|
||||
auto Time = std::chrono::duration_cast<std::chrono::milliseconds>(time_get_nanoseconds() - StartTime);
|
||||
if(pHead->State() == HTTP_ABORTED)
|
||||
{
|
||||
|
@ -212,6 +221,7 @@ void CChooseMaster::CJob::Run()
|
|||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
bool ParseFailure = m_pData->m_pfnValidator(pJson);
|
||||
json_value_free(pJson);
|
||||
if(ParseFailure)
|
||||
|
@ -221,6 +231,7 @@ void CChooseMaster::CJob::Run()
|
|||
dbg_msg("serverbrowse_http", "found master, url='%s' time=%dms", pUrl, (int)Time.count());
|
||||
aTimeMs[i] = Time.count();
|
||||
}
|
||||
|
||||
// Determine index of the minimum time.
|
||||
int BestIndex = -1;
|
||||
int BestTime = 0;
|
||||
|
@ -241,6 +252,7 @@ void CChooseMaster::CJob::Run()
|
|||
dbg_msg("serverbrowse_http", "WARNING: no usable masters found");
|
||||
return;
|
||||
}
|
||||
|
||||
dbg_msg("serverbrowse_http", "determined best master, url='%s' time=%dms", m_pData->m_aaUrls[BestIndex], BestTime);
|
||||
m_pData->m_BestIndex.store(BestIndex);
|
||||
}
|
||||
|
@ -248,7 +260,7 @@ void CChooseMaster::CJob::Run()
|
|||
class CServerBrowserHttp : public IServerBrowserHttp
|
||||
{
|
||||
public:
|
||||
CServerBrowserHttp(IEngine *pEngine, IConsole *pConsole, const char **ppUrls, int NumUrls, int PreviousBestIndex);
|
||||
CServerBrowserHttp(IEngine *pEngine, IConsole *pConsole, IHttp *pHttp, const char **ppUrls, int NumUrls, int PreviousBestIndex);
|
||||
~CServerBrowserHttp() override;
|
||||
void Update() override;
|
||||
bool IsRefreshing() override { return m_State != STATE_DONE; }
|
||||
|
@ -286,6 +298,7 @@ private:
|
|||
|
||||
IEngine *m_pEngine;
|
||||
IConsole *m_pConsole;
|
||||
IHttp *m_pHttp;
|
||||
|
||||
int m_State = STATE_DONE;
|
||||
std::shared_ptr<CHttpRequest> m_pGetServers;
|
||||
|
@ -295,10 +308,11 @@ private:
|
|||
std::vector<NETADDR> m_vLegacyServers;
|
||||
};
|
||||
|
||||
CServerBrowserHttp::CServerBrowserHttp(IEngine *pEngine, IConsole *pConsole, const char **ppUrls, int NumUrls, int PreviousBestIndex) :
|
||||
CServerBrowserHttp::CServerBrowserHttp(IEngine *pEngine, IConsole *pConsole, IHttp *pHttp, const char **ppUrls, int NumUrls, int PreviousBestIndex) :
|
||||
m_pEngine(pEngine),
|
||||
m_pConsole(pConsole),
|
||||
m_pChooseMaster(new CChooseMaster(pEngine, Validate, ppUrls, NumUrls, PreviousBestIndex))
|
||||
m_pHttp(pHttp),
|
||||
m_pChooseMaster(new CChooseMaster(pEngine, pHttp, Validate, ppUrls, NumUrls, PreviousBestIndex))
|
||||
{
|
||||
m_pChooseMaster->Refresh();
|
||||
}
|
||||
|
@ -328,7 +342,7 @@ void CServerBrowserHttp::Update()
|
|||
m_pGetServers = HttpGet(pBestUrl);
|
||||
// 10 seconds connection timeout, lower than 8KB/s for 10 seconds to fail.
|
||||
m_pGetServers->Timeout(CTimeout{10000, 0, 8000, 10});
|
||||
m_pEngine->AddJob(m_pGetServers);
|
||||
m_pHttp->Run(m_pGetServers);
|
||||
m_State = STATE_REFRESHING;
|
||||
}
|
||||
else if(m_State == STATE_REFRESHING)
|
||||
|
@ -469,7 +483,7 @@ static const char *DEFAULT_SERVERLIST_URLS[] = {
|
|||
"https://master4.ddnet.org/ddnet/15/servers.json",
|
||||
};
|
||||
|
||||
IServerBrowserHttp *CreateServerBrowserHttp(IEngine *pEngine, IConsole *pConsole, IStorage *pStorage, const char *pPreviousBestUrl)
|
||||
IServerBrowserHttp *CreateServerBrowserHttp(IEngine *pEngine, IConsole *pConsole, IStorage *pStorage, IHttp *pHttp, const char *pPreviousBestUrl)
|
||||
{
|
||||
char aaUrls[CChooseMaster::MAX_URLS][256];
|
||||
const char *apUrls[CChooseMaster::MAX_URLS] = {0};
|
||||
|
@ -506,5 +520,5 @@ IServerBrowserHttp *CreateServerBrowserHttp(IEngine *pEngine, IConsole *pConsole
|
|||
break;
|
||||
}
|
||||
}
|
||||
return new CServerBrowserHttp(pEngine, pConsole, ppUrls, NumUrls, PreviousBestIndex);
|
||||
return new CServerBrowserHttp(pEngine, pConsole, pHttp, ppUrls, NumUrls, PreviousBestIndex);
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ class CServerInfo;
|
|||
class IConsole;
|
||||
class IEngine;
|
||||
class IStorage;
|
||||
class IHttp;
|
||||
|
||||
class IServerBrowserHttp
|
||||
{
|
||||
|
@ -25,5 +26,5 @@ public:
|
|||
virtual const NETADDR &LegacyServer(int Index) const = 0;
|
||||
};
|
||||
|
||||
IServerBrowserHttp *CreateServerBrowserHttp(IEngine *pEngine, IConsole *pConsole, IStorage *pStorage, const char *pPreviousBestUrl);
|
||||
IServerBrowserHttp *CreateServerBrowserHttp(IEngine *pEngine, IConsole *pConsole, IStorage *pStorage, IHttp *pHttp, const char *pPreviousBestUrl);
|
||||
#endif // ENGINE_CLIENT_SERVERBROWSER_HTTP_H
|
||||
|
|
|
@ -25,7 +25,7 @@ class CUpdaterFetchTask : public CHttpRequest
|
|||
void OnProgress() override;
|
||||
|
||||
protected:
|
||||
int OnCompletion(int State) override;
|
||||
void OnCompletion() override;
|
||||
|
||||
public:
|
||||
CUpdaterFetchTask(CUpdater *pUpdater, const char *pFile, const char *pDestPath);
|
||||
|
@ -61,10 +61,8 @@ void CUpdaterFetchTask::OnProgress()
|
|||
m_pUpdater->m_Percent = Progress();
|
||||
}
|
||||
|
||||
int CUpdaterFetchTask::OnCompletion(int State)
|
||||
void CUpdaterFetchTask::OnCompletion()
|
||||
{
|
||||
State = CHttpRequest::OnCompletion(State);
|
||||
|
||||
const char *pFileName = 0;
|
||||
for(const char *pPath = Dest(); *pPath; pPath++)
|
||||
if(*pPath == '/')
|
||||
|
@ -72,27 +70,26 @@ int CUpdaterFetchTask::OnCompletion(int State)
|
|||
pFileName = pFileName ? pFileName : Dest();
|
||||
if(!str_comp(pFileName, "update.json"))
|
||||
{
|
||||
if(State == HTTP_DONE)
|
||||
if(State() == HTTP_DONE)
|
||||
m_pUpdater->SetCurrentState(IUpdater::GOT_MANIFEST);
|
||||
else if(State == HTTP_ERROR)
|
||||
else if(State() == HTTP_ERROR)
|
||||
m_pUpdater->SetCurrentState(IUpdater::FAIL);
|
||||
}
|
||||
else if(!str_comp(pFileName, m_pUpdater->m_aLastFile))
|
||||
{
|
||||
if(State == HTTP_DONE)
|
||||
if(State() == HTTP_DONE)
|
||||
m_pUpdater->SetCurrentState(IUpdater::MOVE_FILES);
|
||||
else if(State == HTTP_ERROR)
|
||||
else if(State() == HTTP_ERROR)
|
||||
m_pUpdater->SetCurrentState(IUpdater::FAIL);
|
||||
}
|
||||
|
||||
return State;
|
||||
}
|
||||
|
||||
CUpdater::CUpdater()
|
||||
{
|
||||
m_pClient = NULL;
|
||||
m_pStorage = NULL;
|
||||
m_pEngine = NULL;
|
||||
m_pClient = nullptr;
|
||||
m_pStorage = nullptr;
|
||||
m_pEngine = nullptr;
|
||||
m_pHttp = nullptr;
|
||||
m_State = CLEAN;
|
||||
m_Percent = 0;
|
||||
|
||||
|
@ -100,11 +97,12 @@ CUpdater::CUpdater()
|
|||
IStorage::FormatTmpPath(m_aServerExecTmp, sizeof(m_aServerExecTmp), SERVER_EXEC);
|
||||
}
|
||||
|
||||
void CUpdater::Init()
|
||||
void CUpdater::Init(CHttp *pHttp)
|
||||
{
|
||||
m_pClient = Kernel()->RequestInterface<IClient>();
|
||||
m_pStorage = Kernel()->RequestInterface<IStorage>();
|
||||
m_pEngine = Kernel()->RequestInterface<IEngine>();
|
||||
m_pHttp = pHttp;
|
||||
}
|
||||
|
||||
void CUpdater::SetCurrentState(int NewState)
|
||||
|
@ -133,7 +131,7 @@ int CUpdater::GetCurrentPercent()
|
|||
|
||||
void CUpdater::FetchFile(const char *pFile, const char *pDestPath)
|
||||
{
|
||||
m_pEngine->AddJob(std::make_shared<CUpdaterFetchTask>(this, pFile, pDestPath));
|
||||
m_pHttp->Run(std::make_shared<CUpdaterFetchTask>(this, pFile, pDestPath));
|
||||
}
|
||||
|
||||
bool CUpdater::MoveFile(const char *pFile)
|
||||
|
|
|
@ -41,6 +41,7 @@ class CUpdater : public IUpdater
|
|||
class IClient *m_pClient;
|
||||
class IStorage *m_pStorage;
|
||||
class IEngine *m_pEngine;
|
||||
class CHttp *m_pHttp;
|
||||
|
||||
CLock m_Lock;
|
||||
|
||||
|
@ -77,7 +78,7 @@ public:
|
|||
int GetCurrentPercent() override REQUIRES(!m_Lock);
|
||||
|
||||
void InitiateUpdate() override;
|
||||
void Init();
|
||||
void Init(CHttp *pHttp);
|
||||
void Update() override;
|
||||
};
|
||||
|
||||
|
|
16
src/engine/http.h
Normal file
16
src/engine/http.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
#ifndef HTTP_H
|
||||
#define HTTP_H
|
||||
|
||||
#include "kernel.h"
|
||||
|
||||
class IHttpRequest {};
|
||||
|
||||
class IHttp : public IInterface
|
||||
{
|
||||
MACRO_INTERFACE("http", 0)
|
||||
|
||||
public:
|
||||
virtual void Run(std::shared_ptr<IHttpRequest> pRequest) = 0;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -8,6 +8,9 @@
|
|||
#include <engine/storage.h>
|
||||
#include <game/version.h>
|
||||
|
||||
#include <thread>
|
||||
#include <limits>
|
||||
|
||||
#if !defined(CONF_FAMILY_WINDOWS)
|
||||
#include <csignal>
|
||||
#endif
|
||||
|
@ -15,35 +18,6 @@
|
|||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <curl/curl.h>
|
||||
|
||||
// TODO: Non-global pls?
|
||||
static CURLSH *gs_pShare;
|
||||
static CLock gs_aLocks[CURL_LOCK_DATA_LAST + 1];
|
||||
static bool gs_Initialized = false;
|
||||
|
||||
static int GetLockIndex(int Data)
|
||||
{
|
||||
if(!(0 <= Data && Data < CURL_LOCK_DATA_LAST))
|
||||
{
|
||||
Data = CURL_LOCK_DATA_LAST;
|
||||
}
|
||||
return Data;
|
||||
}
|
||||
|
||||
static void CurlLock(CURL *pHandle, curl_lock_data Data, curl_lock_access Access, void *pUser) ACQUIRE(gs_aLocks[GetLockIndex(Data)])
|
||||
{
|
||||
(void)pHandle;
|
||||
(void)Access;
|
||||
(void)pUser;
|
||||
gs_aLocks[GetLockIndex(Data)].lock();
|
||||
}
|
||||
|
||||
static void CurlUnlock(CURL *pHandle, curl_lock_data Data, void *pUser) RELEASE(gs_aLocks[GetLockIndex(Data)])
|
||||
{
|
||||
(void)pHandle;
|
||||
(void)pUser;
|
||||
gs_aLocks[GetLockIndex(Data)].unlock();
|
||||
}
|
||||
|
||||
int CurlDebug(CURL *pHandle, curl_infotype Type, char *pData, size_t DataSize, void *pUser)
|
||||
{
|
||||
char TypeChar;
|
||||
|
@ -71,39 +45,6 @@ int CurlDebug(CURL *pHandle, curl_infotype Type, char *pData, size_t DataSize, v
|
|||
return 0;
|
||||
}
|
||||
|
||||
bool HttpInit(IStorage *pStorage)
|
||||
{
|
||||
if(curl_global_init(CURL_GLOBAL_DEFAULT))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
gs_pShare = curl_share_init();
|
||||
if(!gs_pShare)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// print curl version
|
||||
{
|
||||
curl_version_info_data *pVersion = curl_version_info(CURLVERSION_NOW);
|
||||
dbg_msg("http", "libcurl version %s (compiled = " LIBCURL_VERSION ")", pVersion->version);
|
||||
}
|
||||
|
||||
curl_share_setopt(gs_pShare, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);
|
||||
curl_share_setopt(gs_pShare, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION);
|
||||
curl_share_setopt(gs_pShare, CURLSHOPT_LOCKFUNC, CurlLock);
|
||||
curl_share_setopt(gs_pShare, CURLSHOPT_UNLOCKFUNC, CurlUnlock);
|
||||
|
||||
#if !defined(CONF_FAMILY_WINDOWS)
|
||||
// As a multithreaded application we have to tell curl to not install signal
|
||||
// handlers and instead ignore SIGPIPE from OpenSSL ourselves.
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
#endif
|
||||
|
||||
gs_Initialized = true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void EscapeUrl(char *pBuf, int Size, const char *pStr)
|
||||
{
|
||||
char *pEsc = curl_easy_escape(0, pStr, 0);
|
||||
|
@ -143,24 +84,6 @@ CHttpRequest::~CHttpRequest()
|
|||
}
|
||||
}
|
||||
|
||||
void CHttpRequest::Run()
|
||||
{
|
||||
dbg_assert(gs_Initialized, "must initialize HTTP before running HTTP requests");
|
||||
int FinalState;
|
||||
if(!BeforeInit())
|
||||
{
|
||||
FinalState = HTTP_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
CURL *pHandle = curl_easy_init();
|
||||
FinalState = RunImpl(pHandle);
|
||||
curl_easy_cleanup(pHandle);
|
||||
}
|
||||
|
||||
m_State = OnCompletion(FinalState);
|
||||
}
|
||||
|
||||
bool CHttpRequest::BeforeInit()
|
||||
{
|
||||
if(m_WriteToFile)
|
||||
|
@ -181,12 +104,12 @@ bool CHttpRequest::BeforeInit()
|
|||
return true;
|
||||
}
|
||||
|
||||
int CHttpRequest::RunImpl(CURL *pUser)
|
||||
bool CHttpRequest::ConfigureHandle(void *pUser)
|
||||
{
|
||||
CURL *pHandle = (CURL *)pUser;
|
||||
if(!pHandle)
|
||||
if(!BeforeInit())
|
||||
{
|
||||
return HTTP_ERROR;
|
||||
return false;
|
||||
}
|
||||
|
||||
if(g_Config.m_DbgCurl)
|
||||
|
@ -199,8 +122,8 @@ int CHttpRequest::RunImpl(CURL *pUser)
|
|||
{
|
||||
Protocols |= CURLPROTO_HTTP;
|
||||
}
|
||||
char aErr[CURL_ERROR_SIZE];
|
||||
curl_easy_setopt(pHandle, CURLOPT_ERRORBUFFER, aErr);
|
||||
|
||||
curl_easy_setopt(pHandle, CURLOPT_ERRORBUFFER, m_aErr);
|
||||
|
||||
curl_easy_setopt(pHandle, CURLOPT_CONNECTTIMEOUT_MS, m_Timeout.ConnectTimeoutMs);
|
||||
curl_easy_setopt(pHandle, CURLOPT_TIMEOUT_MS, m_Timeout.TimeoutMs);
|
||||
|
@ -211,7 +134,6 @@ int CHttpRequest::RunImpl(CURL *pUser)
|
|||
curl_easy_setopt(pHandle, CURLOPT_MAXFILESIZE_LARGE, (curl_off_t)m_MaxResponseSize);
|
||||
}
|
||||
|
||||
curl_easy_setopt(pHandle, CURLOPT_SHARE, gs_pShare);
|
||||
// ‘CURLOPT_PROTOCOLS’ is deprecated: since 7.85.0. Use CURLOPT_PROTOCOLS_STR
|
||||
// Wait until all platforms have 7.85.0
|
||||
#ifdef __GNUC__
|
||||
|
@ -285,22 +207,7 @@ int CHttpRequest::RunImpl(CURL *pUser)
|
|||
|
||||
curl_easy_setopt(pHandle, CURLOPT_HTTPHEADER, m_pHeaders);
|
||||
|
||||
if(g_Config.m_DbgCurl || m_LogProgress >= HTTPLOG::ALL)
|
||||
dbg_msg("http", "fetching %s", m_aUrl);
|
||||
m_State = HTTP_RUNNING;
|
||||
int Ret = curl_easy_perform(pHandle);
|
||||
if(Ret != CURLE_OK)
|
||||
{
|
||||
if(g_Config.m_DbgCurl || m_LogProgress >= HTTPLOG::FAILURE)
|
||||
dbg_msg("http", "%s failed. libcurl error (%d): %s", m_aUrl, (int)Ret, aErr);
|
||||
return (Ret == CURLE_ABORTED_BY_CALLBACK) ? HTTP_ABORTED : HTTP_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(g_Config.m_DbgCurl || m_LogProgress >= HTTPLOG::ALL)
|
||||
dbg_msg("http", "task done %s", m_aUrl);
|
||||
return HTTP_DONE;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t CHttpRequest::OnData(char *pData, size_t DataSize)
|
||||
|
@ -356,10 +263,29 @@ int CHttpRequest::ProgressCallback(void *pUser, double DlTotal, double DlCurr, d
|
|||
return pTask->m_Abort ? -1 : 0;
|
||||
}
|
||||
|
||||
int CHttpRequest::OnCompletion(int State)
|
||||
void CHttpRequest::OnCompletionInternal(std::optional<unsigned int> Result)
|
||||
{
|
||||
if(m_Abort)
|
||||
State = HTTP_ABORTED;
|
||||
int State;
|
||||
if(Result.has_value())
|
||||
{
|
||||
CURLcode Code = static_cast<CURLcode>(Result.value());
|
||||
if(Code != CURLE_OK)
|
||||
{
|
||||
if(g_Config.m_DbgCurl || m_LogProgress >= HTTPLOG::FAILURE)
|
||||
dbg_msg("http", "%s failed. libcurl error (%u): %s", m_aUrl, Code, m_aErr);
|
||||
State = (Code == CURLE_ABORTED_BY_CALLBACK) ? HTTP_ABORTED : HTTP_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(g_Config.m_DbgCurl || m_LogProgress >= HTTPLOG::ALL)
|
||||
dbg_msg("http", "task done: %s", m_aUrl);
|
||||
State = HTTP_DONE;
|
||||
}
|
||||
}
|
||||
else {
|
||||
dbg_msg("http", "%s failed. internal error: %s", m_aUrl, m_aErr);
|
||||
State = HTTP_ERROR;
|
||||
}
|
||||
|
||||
if(State == HTTP_DONE && m_ExpectedSha256 != SHA256_ZEROED)
|
||||
{
|
||||
|
@ -391,7 +317,9 @@ int CHttpRequest::OnCompletion(int State)
|
|||
fs_remove(m_aDestAbsolute);
|
||||
}
|
||||
}
|
||||
return State;
|
||||
|
||||
m_State = State;
|
||||
OnCompletion();
|
||||
}
|
||||
|
||||
void CHttpRequest::WriteToFile(IStorage *pStorage, const char *pDest, int StorageType)
|
||||
|
@ -413,6 +341,20 @@ void CHttpRequest::Header(const char *pNameColonValue)
|
|||
m_pHeaders = curl_slist_append((curl_slist *)m_pHeaders, pNameColonValue);
|
||||
}
|
||||
|
||||
void CHttpRequest::Wait()
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
// This is so uncommon that polling just might work
|
||||
for(;;) {
|
||||
int State = m_State.load(std::memory_order_seq_cst);
|
||||
if(State != HTTP_QUEUED && State != HTTP_RUNNING) {
|
||||
return;
|
||||
}
|
||||
std::this_thread::sleep_for(10ms);
|
||||
}
|
||||
}
|
||||
|
||||
void CHttpRequest::Result(unsigned char **ppResult, size_t *pResultLength) const
|
||||
{
|
||||
if(m_WriteToFile || State() != HTTP_DONE)
|
||||
|
@ -436,3 +378,192 @@ json_value *CHttpRequest::ResultJson() const
|
|||
}
|
||||
return json_parse((char *)pResult, ResultLength);
|
||||
}
|
||||
|
||||
bool CHttp::Init(std::chrono::milliseconds ShutdownDelay)
|
||||
{
|
||||
m_ShutdownDelay = ShutdownDelay;
|
||||
|
||||
#if !defined(CONF_FAMILY_WINDOWS)
|
||||
// As a multithreaded application we have to tell curl to not install signal
|
||||
// handlers and instead ignore SIGPIPE from OpenSSL ourselves.
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
#endif
|
||||
m_pThread = thread_init(CHttp::ThreadMain, this, "http");
|
||||
|
||||
std::unique_lock Lock(m_Lock);
|
||||
m_Cv.wait(Lock, [this](){ return m_State != UNINITIALIZED; });
|
||||
if(m_State != RUNNING)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CHttp::ThreadMain(void *pUser)
|
||||
{
|
||||
CHttp *pHttp = static_cast<CHttp *>(pUser);
|
||||
pHttp->RunLoop();
|
||||
}
|
||||
|
||||
void CHttp::RunLoop()
|
||||
{
|
||||
std::unique_lock Lock(m_Lock);
|
||||
if(curl_global_init(CURL_GLOBAL_DEFAULT))
|
||||
{
|
||||
dbg_msg("http", "curl_global_init failed");
|
||||
m_State = ERROR;
|
||||
m_Cv.notify_all();
|
||||
return;
|
||||
}
|
||||
|
||||
m_pMultiH = curl_multi_init();
|
||||
if(!m_pMultiH)
|
||||
{
|
||||
dbg_msg("http", "curl_multi_init failed");
|
||||
m_State = ERROR;
|
||||
m_Cv.notify_all();
|
||||
return;
|
||||
}
|
||||
|
||||
// print curl version
|
||||
{
|
||||
curl_version_info_data *pVersion = curl_version_info(CURLVERSION_NOW);
|
||||
dbg_msg("http", "libcurl version %s (compiled = " LIBCURL_VERSION ")", pVersion->version);
|
||||
}
|
||||
|
||||
m_State = RUNNING;
|
||||
m_Cv.notify_all();
|
||||
dbg_msg("http", "running");
|
||||
Lock.unlock();
|
||||
|
||||
while(m_State == RUNNING) {
|
||||
static int NextTimeout = std::numeric_limits<int>::max();
|
||||
int Events = 0;
|
||||
CURLMcode mc = curl_multi_poll(m_pMultiH, NULL, 0, NextTimeout, &Events);
|
||||
|
||||
// We may have been woken up for a shutdown
|
||||
if(m_Shutdown) {
|
||||
auto Now = std::chrono::steady_clock::now();
|
||||
if(!m_ShutdownTime.has_value()) {
|
||||
m_ShutdownTime = Now + m_ShutdownDelay;
|
||||
NextTimeout = m_ShutdownDelay.count();
|
||||
}
|
||||
else if(m_ShutdownTime < Now || m_RunningRequests.empty()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(mc != CURLM_OK) {
|
||||
Lock.lock();
|
||||
dbg_msg("http", "Failed multi wait: %s", curl_multi_strerror(mc));
|
||||
m_State = ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
mc = curl_multi_perform(m_pMultiH, &Events);
|
||||
if(mc != CURLM_OK) {
|
||||
Lock.lock();
|
||||
dbg_msg("http", "Failed multi perform: %s", curl_multi_strerror(mc));
|
||||
m_State = ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
struct CURLMsg *m;
|
||||
while((m = curl_multi_info_read(m_pMultiH, &Events))) {
|
||||
if(m->msg == CURLMSG_DONE) {
|
||||
auto RequestIt = m_RunningRequests.find(m->easy_handle);
|
||||
dbg_assert(RequestIt != m_RunningRequests.end(), "Running handle not added to map");
|
||||
auto pRequest = std::move(RequestIt->second);
|
||||
m_RunningRequests.erase(RequestIt);
|
||||
|
||||
pRequest->OnCompletionInternal(m->data.result);
|
||||
curl_multi_remove_handle(m_pMultiH, m->easy_handle);
|
||||
curl_easy_cleanup(m->easy_handle);
|
||||
}
|
||||
}
|
||||
|
||||
decltype(m_PendingRequests) NewRequests = {};
|
||||
Lock.lock();
|
||||
std::swap(m_PendingRequests, NewRequests);
|
||||
Lock.unlock();
|
||||
|
||||
while(!NewRequests.empty()) {
|
||||
auto &pRequest = NewRequests.front();
|
||||
dbg_msg("http", "task: %s %s", CHttpRequest::GetRequestType(pRequest->m_Type), pRequest->m_aUrl);
|
||||
|
||||
CURL *pEH = curl_easy_init();
|
||||
if(!pEH)
|
||||
goto error_init;
|
||||
|
||||
if(!pRequest->ConfigureHandle(pEH))
|
||||
goto error_configure;
|
||||
|
||||
mc = curl_multi_add_handle(m_pMultiH, pEH);
|
||||
if(mc != CURLM_OK)
|
||||
goto error_configure;
|
||||
|
||||
m_RunningRequests.emplace(pEH, std::move(pRequest));
|
||||
NewRequests.pop_front();
|
||||
|
||||
continue;
|
||||
|
||||
error_configure:
|
||||
curl_easy_cleanup(pEH);
|
||||
error_init:
|
||||
dbg_msg("http", "failed to start new request");
|
||||
Lock.lock();
|
||||
m_State = ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
// Only happens if m_State == ERROR, thus we already hold the lock
|
||||
if(!NewRequests.empty()) {
|
||||
m_PendingRequests.insert(m_PendingRequests.end(), std::make_move_iterator(NewRequests.begin()), std::make_move_iterator(NewRequests.end()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!Lock.owns_lock())
|
||||
Lock.lock();
|
||||
|
||||
bool Cleanup = m_State != ERROR;
|
||||
for(auto &pRequest : m_PendingRequests) {
|
||||
str_copy(pRequest->m_aErr, "Shutting down");
|
||||
pRequest->OnCompletionInternal(std::nullopt);
|
||||
}
|
||||
|
||||
for(auto &ReqPair : m_RunningRequests) {
|
||||
auto &[pHandle, pRequest] = ReqPair;
|
||||
if(Cleanup) {
|
||||
curl_multi_remove_handle(m_pMultiH, pHandle);
|
||||
curl_easy_cleanup(pHandle);
|
||||
}
|
||||
|
||||
str_copy(pRequest->m_aErr, "Shutting down");
|
||||
pRequest->OnCompletionInternal(std::nullopt);
|
||||
}
|
||||
|
||||
if(Cleanup) {
|
||||
curl_multi_cleanup(m_pMultiH);
|
||||
curl_global_cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
void CHttp::Run(std::shared_ptr<IHttpRequest> pRequest)
|
||||
{
|
||||
std::unique_lock Lock(m_Lock);
|
||||
m_Cv.wait(Lock, [this](){ return m_State != UNINITIALIZED; });
|
||||
m_PendingRequests.emplace_back(std::move(std::static_pointer_cast<CHttpRequest>(pRequest)));
|
||||
curl_multi_wakeup(m_pMultiH);
|
||||
}
|
||||
|
||||
void CHttp::Shutdown()
|
||||
{
|
||||
std::unique_lock Lock(m_Lock);
|
||||
if(m_Shutdown || m_State != RUNNING)
|
||||
return;
|
||||
|
||||
m_Shutdown = true;
|
||||
curl_multi_wakeup(m_pMultiH);
|
||||
}
|
||||
|
|
|
@ -7,6 +7,12 @@
|
|||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <deque>
|
||||
#include <optional>
|
||||
|
||||
#include <engine/http.h>
|
||||
|
||||
typedef struct _json_value json_value;
|
||||
class IStorage;
|
||||
|
@ -42,8 +48,10 @@ struct CTimeout
|
|||
long LowSpeedTime;
|
||||
};
|
||||
|
||||
class CHttpRequest : public IJob
|
||||
class CHttpRequest : public IHttpRequest
|
||||
{
|
||||
friend class CHttp;
|
||||
|
||||
enum class REQUEST
|
||||
{
|
||||
GET = 0,
|
||||
|
@ -51,6 +59,24 @@ class CHttpRequest : public IJob
|
|||
POST,
|
||||
POST_JSON,
|
||||
};
|
||||
|
||||
static constexpr const char *GetRequestType(REQUEST Type)
|
||||
{
|
||||
switch(Type)
|
||||
{
|
||||
case REQUEST::GET:
|
||||
return "GET";
|
||||
case REQUEST::HEAD:
|
||||
return "HEAD";
|
||||
case REQUEST::POST:
|
||||
case REQUEST::POST_JSON:
|
||||
return "POST";
|
||||
}
|
||||
|
||||
// Unreachable, maybe assert instead?
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
char m_aUrl[256] = {0};
|
||||
|
||||
void *m_pHeaders = nullptr;
|
||||
|
@ -83,13 +109,14 @@ class CHttpRequest : public IJob
|
|||
HTTPLOG m_LogProgress = HTTPLOG::ALL;
|
||||
IPRESOLVE m_IpResolve = IPRESOLVE::WHATEVER;
|
||||
|
||||
char m_aErr[256]; // 256 == CURL_ERROR_SIZE
|
||||
std::atomic<int> m_State{HTTP_QUEUED};
|
||||
std::atomic<bool> m_Abort{false};
|
||||
|
||||
void Run() override;
|
||||
// Abort the request with an error if `BeforeInit()` returns false.
|
||||
bool BeforeInit();
|
||||
int RunImpl(void *pUser);
|
||||
bool ConfigureHandle(void *pHandle); // void * == CURL *
|
||||
void OnCompletionInternal(std::optional<unsigned int> Result); // unsigned int == CURLcode
|
||||
|
||||
// Abort the request if `OnData()` returns something other than
|
||||
// `DataSize`.
|
||||
|
@ -99,8 +126,9 @@ class CHttpRequest : public IJob
|
|||
static size_t WriteCallback(char *pData, size_t Size, size_t Number, void *pUser);
|
||||
|
||||
protected:
|
||||
virtual void OnProgress() {}
|
||||
virtual int OnCompletion(int State);
|
||||
// These run on the curl thread now, DO NOT STALL THE THREAD
|
||||
virtual void OnProgress() {};
|
||||
virtual void OnCompletion() {};
|
||||
|
||||
public:
|
||||
CHttpRequest(const char *pUrl);
|
||||
|
@ -157,8 +185,11 @@ public:
|
|||
double Size() const { return m_Size.load(std::memory_order_relaxed); }
|
||||
int Progress() const { return m_Progress.load(std::memory_order_relaxed); }
|
||||
int State() const { return m_State; }
|
||||
bool Done() const { int State = m_State; return State != HTTP_QUEUED && State != HTTP_DONE; }
|
||||
void Abort() { m_Abort = true; }
|
||||
|
||||
void Wait();
|
||||
|
||||
void Result(unsigned char **ppResult, size_t *pResultLength) const;
|
||||
json_value *ResultJson() const;
|
||||
};
|
||||
|
@ -202,4 +233,41 @@ inline std::unique_ptr<CHttpRequest> HttpPostJson(const char *pUrl, const char *
|
|||
bool HttpInit(IStorage *pStorage);
|
||||
void EscapeUrl(char *pBuf, int Size, const char *pStr);
|
||||
bool HttpHasIpresolveBug();
|
||||
|
||||
// In an ideal world this would be a kernel interface
|
||||
class CHttp : public IHttp
|
||||
{
|
||||
enum EState {
|
||||
UNINITIALIZED,
|
||||
RUNNING,
|
||||
STOPPING,
|
||||
ERROR,
|
||||
};
|
||||
|
||||
void *m_pThread = nullptr;
|
||||
|
||||
std::mutex m_Lock{};
|
||||
std::condition_variable m_Cv{};
|
||||
std::atomic<EState> m_State = UNINITIALIZED;
|
||||
std::deque<std::shared_ptr<CHttpRequest>> m_PendingRequests{};
|
||||
std::unordered_map<void *, std::shared_ptr<CHttpRequest>> m_RunningRequests{}; // void * == CURL *
|
||||
std::chrono::milliseconds m_ShutdownDelay{};
|
||||
std::optional<std::chrono::time_point<std::chrono::steady_clock>> m_ShutdownTime{};
|
||||
std::atomic<bool> m_Shutdown = false;
|
||||
|
||||
// Only to be used with curl_multi_wakeup
|
||||
void *m_pMultiH = nullptr; // void * == CURLM *
|
||||
|
||||
static void ThreadMain(void *pUser);
|
||||
void RunLoop();
|
||||
|
||||
public:
|
||||
// Startup
|
||||
bool Init(std::chrono::milliseconds ShutdownDelay);
|
||||
|
||||
// User
|
||||
virtual void Run(std::shared_ptr<IHttpRequest> pRequest) override;
|
||||
void Shutdown() override;
|
||||
};
|
||||
|
||||
#endif // ENGINE_SHARED_HTTP_H
|
||||
|
|
|
@ -40,3 +40,5 @@ class IClient *CComponent::Client() const
|
|||
{
|
||||
return m_pClient->Client();
|
||||
}
|
||||
|
||||
class IHttp *CComponent::Http() const { return m_pClient->Http(); }
|
||||
|
|
|
@ -128,6 +128,11 @@ protected:
|
|||
*/
|
||||
float LocalTime() const;
|
||||
|
||||
/**
|
||||
* Get the http interface
|
||||
*/
|
||||
class IHttp *Http() const;
|
||||
|
||||
public:
|
||||
/**
|
||||
* The component virtual destructor.
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <engine/serverbrowser.h>
|
||||
#include <engine/shared/config.h>
|
||||
#include <engine/shared/http.h>
|
||||
#include <engine/shared/jobs.h>
|
||||
#include <engine/shared/linereader.h>
|
||||
#include <engine/textrender.h>
|
||||
|
||||
|
@ -515,34 +516,35 @@ protected:
|
|||
char m_aPath[IO_MAX_PATH_LENGTH];
|
||||
int m_StorageType;
|
||||
bool m_Success = false;
|
||||
CImageInfo m_ImageInfo;
|
||||
SHA256_DIGEST m_Sha256;
|
||||
|
||||
CAbstractCommunityIconJob(CMenus *pMenus, const char *pCommunityId, int StorageType);
|
||||
virtual ~CAbstractCommunityIconJob();
|
||||
virtual ~CAbstractCommunityIconJob() {};
|
||||
|
||||
public:
|
||||
const char *CommunityId() const { return m_aCommunityId; }
|
||||
bool Success() const { return m_Success; }
|
||||
CImageInfo &&ImageInfo() { return std::move(m_ImageInfo); }
|
||||
SHA256_DIGEST &&Sha256() { return std::move(m_Sha256); }
|
||||
};
|
||||
|
||||
class CCommunityIconLoadJob : public IJob, public CAbstractCommunityIconJob
|
||||
{
|
||||
CImageInfo m_ImageInfo;
|
||||
protected:
|
||||
void Run() override;
|
||||
|
||||
public:
|
||||
CCommunityIconLoadJob(CMenus *pMenus, const char *pCommunityId, int StorageType);
|
||||
|
||||
CImageInfo &&ImageInfo() { return std::move(m_ImageInfo); }
|
||||
};
|
||||
|
||||
class CCommunityIconDownloadJob : public CHttpRequest, public CAbstractCommunityIconJob
|
||||
{
|
||||
protected:
|
||||
int OnCompletion(int State) override;
|
||||
|
||||
public:
|
||||
CCommunityIconDownloadJob(CMenus *pMenus, const char *pCommunityId, const char *pUrl, const SHA256_DIGEST &Sha256);
|
||||
};
|
||||
|
||||
struct SCommunityIcon
|
||||
{
|
||||
char m_aCommunityId[CServerInfo::MAX_COMMUNITY_ID_LENGTH];
|
||||
|
|
|
@ -1802,25 +1802,6 @@ CMenus::CAbstractCommunityIconJob::CAbstractCommunityIconJob(CMenus *pMenus, con
|
|||
str_format(m_aPath, sizeof(m_aPath), "communityicons/%s.png", pCommunityId);
|
||||
}
|
||||
|
||||
CMenus::CAbstractCommunityIconJob::~CAbstractCommunityIconJob()
|
||||
{
|
||||
free(m_ImageInfo.m_pData);
|
||||
m_ImageInfo.m_pData = nullptr;
|
||||
}
|
||||
|
||||
int CMenus::CCommunityIconDownloadJob::OnCompletion(int State)
|
||||
{
|
||||
State = CHttpRequest::OnCompletion(State);
|
||||
if(State == HTTP_DONE)
|
||||
{
|
||||
if(m_pMenus->LoadCommunityIconFile(Dest(), IStorage::TYPE_SAVE, m_ImageInfo, m_Sha256))
|
||||
m_Success = true;
|
||||
else
|
||||
State = HTTP_ERROR;
|
||||
}
|
||||
return State;
|
||||
}
|
||||
|
||||
CMenus::CCommunityIconDownloadJob::CCommunityIconDownloadJob(CMenus *pMenus, const char *pCommunityId, const char *pUrl, const SHA256_DIGEST &Sha256) :
|
||||
CHttpRequest(pUrl),
|
||||
CAbstractCommunityIconJob(pMenus, pCommunityId, IStorage::TYPE_SAVE)
|
||||
|
@ -1964,10 +1945,14 @@ void CMenus::UpdateCommunityIcons()
|
|||
if(!m_CommunityIconDownloadJobs.empty())
|
||||
{
|
||||
std::shared_ptr<CCommunityIconDownloadJob> pJob = m_CommunityIconDownloadJobs.front();
|
||||
if(pJob->Status() == IJob::STATE_DONE)
|
||||
if(pJob->Done())
|
||||
{
|
||||
if(pJob->Success())
|
||||
LoadCommunityIconFinish(pJob->CommunityId(), pJob->ImageInfo(), pJob->Sha256());
|
||||
if(pJob->State() == HTTP_DONE)
|
||||
{
|
||||
std::shared_ptr<CCommunityIconLoadJob> pLoadJob = std::make_shared<CCommunityIconLoadJob>(this, pJob->CommunityId(), IStorage::TYPE_SAVE);
|
||||
Engine()->AddJob(pLoadJob);
|
||||
m_CommunityIconLoadJobs.emplace_back(std::move(pLoadJob));
|
||||
}
|
||||
m_CommunityIconDownloadJobs.pop_front();
|
||||
}
|
||||
}
|
||||
|
@ -2007,7 +1992,7 @@ void CMenus::UpdateCommunityIcons()
|
|||
if(pExistingDownload == m_CommunityIconDownloadJobs.end() && (ExistingIcon == m_vCommunityIcons.end() || ExistingIcon->m_Sha256 != Community.IconSha256()))
|
||||
{
|
||||
std::shared_ptr<CCommunityIconDownloadJob> pJob = std::make_shared<CCommunityIconDownloadJob>(this, Community.Id(), Community.IconUrl(), Community.IconSha256());
|
||||
Engine()->AddJob(pJob);
|
||||
Http()->Run(pJob);
|
||||
m_CommunityIconDownloadJobs.push_back(pJob);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,15 +21,13 @@ bool CSkins::IsVanillaSkin(const char *pName)
|
|||
return std::any_of(std::begin(VANILLA_SKINS), std::end(VANILLA_SKINS), [pName](const char *pVanillaSkin) { return str_comp(pName, pVanillaSkin) == 0; });
|
||||
}
|
||||
|
||||
int CSkins::CGetPngFile::OnCompletion(int State)
|
||||
void CSkins::CGetPngFile::OnCompletion()
|
||||
{
|
||||
State = CHttpRequest::OnCompletion(State);
|
||||
|
||||
if(State != HTTP_ERROR && State != HTTP_ABORTED && !m_pSkins->LoadSkinPNG(m_Info, Dest(), Dest(), IStorage::TYPE_SAVE))
|
||||
// Maybe this should start another thread to load the png in instead of stalling the curl thread
|
||||
if(State() != HTTP_ERROR && State() != HTTP_ABORTED)
|
||||
{
|
||||
State = HTTP_ERROR;
|
||||
m_pSkins->LoadSkinPNG(m_Info, Dest(), Dest(), IStorage::TYPE_SAVE);
|
||||
}
|
||||
return State;
|
||||
}
|
||||
|
||||
CSkins::CGetPngFile::CGetPngFile(CSkins *pSkins, const char *pUrl, IStorage *pStorage, const char *pDest) :
|
||||
|
@ -436,12 +434,16 @@ const CSkin *CSkins::FindImpl(const char *pName)
|
|||
char aEscapedName[256];
|
||||
EscapeUrl(aEscapedName, sizeof(aEscapedName), pName);
|
||||
str_format(aUrl, sizeof(aUrl), "%s%s.png", g_Config.m_ClDownloadCommunitySkins != 0 ? g_Config.m_ClSkinCommunityDownloadUrl : g_Config.m_ClSkinDownloadUrl, aEscapedName);
|
||||
|
||||
char aBuf[IO_MAX_PATH_LENGTH];
|
||||
str_format(Skin.m_aPath, sizeof(Skin.m_aPath), "downloadedskins/%s", IStorage::FormatTmpPath(aBuf, sizeof(aBuf), pName));
|
||||
|
||||
Skin.m_pTask = std::make_shared<CGetPngFile>(this, aUrl, Storage(), Skin.m_aPath);
|
||||
m_pClient->Engine()->AddJob(Skin.m_pTask);
|
||||
Http()->Run(Skin.m_pTask);
|
||||
|
||||
auto &&pDownloadSkin = std::make_unique<CDownloadSkin>(std::move(Skin));
|
||||
m_DownloadSkins.insert({pDownloadSkin->GetName(), std::move(pDownloadSkin)});
|
||||
++m_DownloadingSkins;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ public:
|
|||
CSkins *m_pSkins;
|
||||
|
||||
protected:
|
||||
virtual int OnCompletion(int State) override;
|
||||
virtual void OnCompletion() override;
|
||||
|
||||
public:
|
||||
CGetPngFile(CSkins *pSkins, const char *pUrl, IStorage *pStorage, const char *pDest);
|
||||
|
|
|
@ -100,6 +100,7 @@ void CGameClient::OnConsoleInit()
|
|||
#if defined(CONF_AUTOUPDATE)
|
||||
m_pUpdater = Kernel()->RequestInterface<IUpdater>();
|
||||
#endif
|
||||
m_pHttp = Kernel()->RequestInterface<IHttp>();
|
||||
|
||||
m_Menus.SetMenuBackground(&m_MenuBackground);
|
||||
|
||||
|
|
|
@ -177,6 +177,7 @@ private:
|
|||
#if defined(CONF_AUTOUPDATE)
|
||||
class IUpdater *m_pUpdater;
|
||||
#endif
|
||||
class IHttp *m_pHttp;
|
||||
|
||||
CLayers m_Layers;
|
||||
CCollision m_Collision;
|
||||
|
@ -251,6 +252,7 @@ public:
|
|||
return m_pUpdater;
|
||||
}
|
||||
#endif
|
||||
class IHttp *Http() { return m_pHttp; }
|
||||
|
||||
int NetobjNumCorrections()
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue