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
|
friends.h
|
||||||
ghost.h
|
ghost.h
|
||||||
graphics.h
|
graphics.h
|
||||||
|
http.h
|
||||||
input.h
|
input.h
|
||||||
kernel.h
|
kernel.h
|
||||||
keys.h
|
keys.h
|
||||||
|
|
|
@ -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->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->MaxResponseSize(1024 * 1024 * 1024); // 1 GiB
|
||||||
m_pMapdownloadTask->ExpectSha256(*pMapSha256);
|
m_pMapdownloadTask->ExpectSha256(*pMapSha256);
|
||||||
Engine()->AddJob(m_pMapdownloadTask);
|
Http()->Run(m_pMapdownloadTask);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -2664,6 +2664,7 @@ void CClient::RegisterInterfaces()
|
||||||
#endif
|
#endif
|
||||||
Kernel()->RegisterInterface(static_cast<IFriends *>(&m_Friends), false);
|
Kernel()->RegisterInterface(static_cast<IFriends *>(&m_Friends), false);
|
||||||
Kernel()->ReregisterInterface(static_cast<IFriends *>(&m_Foes));
|
Kernel()->ReregisterInterface(static_cast<IFriends *>(&m_Foes));
|
||||||
|
Kernel()->RegisterInterface(static_cast<IHttp *>(&m_Http), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CClient::InitInterfaces()
|
void CClient::InitInterfaces()
|
||||||
|
@ -2688,12 +2689,12 @@ void CClient::InitInterfaces()
|
||||||
|
|
||||||
m_DemoEditor.Init(m_pGameClient->NetVersion(), &m_SnapshotDelta, m_pConsole, m_pStorage);
|
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());
|
m_ServerBrowser.SetBaseInfo(&m_aNetClient[CONN_CONTACT], m_pGameClient->NetVersion());
|
||||||
|
|
||||||
HttpInit(m_pStorage);
|
|
||||||
|
|
||||||
#if defined(CONF_AUTOUPDATE)
|
#if defined(CONF_AUTOUPDATE)
|
||||||
m_Updater.Init();
|
m_Updater.Init(&m_Http);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
m_pConfigManager->RegisterCallback(IFavorites::ConfigSaveCallback, m_pFavorites);
|
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 = HttpGetFile(aUrl, Storage(), m_aDDNetInfoTmp, IStorage::TYPE_SAVE);
|
||||||
m_pDDNetInfoTask->Timeout(CTimeout{10000, 0, 500, 10});
|
m_pDDNetInfoTask->Timeout(CTimeout{10000, 0, 500, 10});
|
||||||
m_pDDNetInfoTask->IpResolve(IPRESOLVE::V4);
|
m_pDDNetInfoTask->IpResolve(IPRESOLVE::V4);
|
||||||
Engine()->AddJob(m_pDDNetInfoTask);
|
Http()->Run(m_pDDNetInfoTask);
|
||||||
}
|
}
|
||||||
|
|
||||||
int CClient::GetPredictionTime()
|
int CClient::GetPredictionTime()
|
||||||
|
|
|
@ -76,6 +76,7 @@ class CClient : public IClient, public CDemoPlayer::IListener
|
||||||
IStorage *m_pStorage = nullptr;
|
IStorage *m_pStorage = nullptr;
|
||||||
IEngineTextRender *m_pTextRender = nullptr;
|
IEngineTextRender *m_pTextRender = nullptr;
|
||||||
IUpdater *m_pUpdater = nullptr;
|
IUpdater *m_pUpdater = nullptr;
|
||||||
|
CHttp m_Http;
|
||||||
|
|
||||||
CNetClient m_aNetClient[NUM_CONNS];
|
CNetClient m_aNetClient[NUM_CONNS];
|
||||||
CDemoPlayer m_DemoPlayer;
|
CDemoPlayer m_DemoPlayer;
|
||||||
|
@ -266,6 +267,7 @@ public:
|
||||||
IStorage *Storage() { return m_pStorage; }
|
IStorage *Storage() { return m_pStorage; }
|
||||||
IEngineTextRender *TextRender() { return m_pTextRender; }
|
IEngineTextRender *TextRender() { return m_pTextRender; }
|
||||||
IUpdater *Updater() { return m_pUpdater; }
|
IUpdater *Updater() { return m_pUpdater; }
|
||||||
|
IHttp *Http() { return &m_Http; }
|
||||||
|
|
||||||
CClient();
|
CClient();
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#include <engine/engine.h>
|
#include <engine/engine.h>
|
||||||
#include <engine/favorites.h>
|
#include <engine/favorites.h>
|
||||||
#include <engine/friends.h>
|
#include <engine/friends.h>
|
||||||
|
#include <engine/http.h>
|
||||||
#include <engine/storage.h>
|
#include <engine/storage.h>
|
||||||
|
|
||||||
class CSortWrap
|
class CSortWrap
|
||||||
|
@ -95,6 +96,8 @@ void CServerBrowser::SetBaseInfo(class CNetClient *pClient, const char *pNetVers
|
||||||
m_pFavorites = Kernel()->RequestInterface<IFavorites>();
|
m_pFavorites = Kernel()->RequestInterface<IFavorites>();
|
||||||
m_pFriends = Kernel()->RequestInterface<IFriends>();
|
m_pFriends = Kernel()->RequestInterface<IFriends>();
|
||||||
m_pStorage = Kernel()->RequestInterface<IStorage>();
|
m_pStorage = Kernel()->RequestInterface<IStorage>();
|
||||||
|
m_pHttpClient = Kernel()->RequestInterface<IHttp>();
|
||||||
|
dbg_msg("cserverbrowser", "%p", m_pHttpClient);
|
||||||
m_pPingCache = CreateServerBrowserPingCache(m_pConsole, m_pStorage);
|
m_pPingCache = CreateServerBrowserPingCache(m_pConsole, m_pStorage);
|
||||||
|
|
||||||
RegisterCommands();
|
RegisterCommands();
|
||||||
|
@ -102,7 +105,7 @@ void CServerBrowser::SetBaseInfo(class CNetClient *pClient, const char *pNetVers
|
||||||
|
|
||||||
void CServerBrowser::OnInit()
|
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()
|
void CServerBrowser::RegisterCommands()
|
||||||
|
|
|
@ -21,6 +21,7 @@ class IFriends;
|
||||||
class IServerBrowserHttp;
|
class IServerBrowserHttp;
|
||||||
class IServerBrowserPingCache;
|
class IServerBrowserPingCache;
|
||||||
class IStorage;
|
class IStorage;
|
||||||
|
class IHttp;
|
||||||
|
|
||||||
class CCommunityServer
|
class CCommunityServer
|
||||||
{
|
{
|
||||||
|
@ -143,6 +144,7 @@ private:
|
||||||
IFriends *m_pFriends = nullptr;
|
IFriends *m_pFriends = nullptr;
|
||||||
IFavorites *m_pFavorites = nullptr;
|
IFavorites *m_pFavorites = nullptr;
|
||||||
IStorage *m_pStorage = nullptr;
|
IStorage *m_pStorage = nullptr;
|
||||||
|
IHttp *m_pHttpClient = nullptr;
|
||||||
char m_aNetVersion[128];
|
char m_aNetVersion[128];
|
||||||
|
|
||||||
bool m_RefreshingHttp = false;
|
bool m_RefreshingHttp = false;
|
||||||
|
|
|
@ -29,7 +29,7 @@ public:
|
||||||
{
|
{
|
||||||
MAX_URLS = 16,
|
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();
|
virtual ~CChooseMaster();
|
||||||
|
|
||||||
bool GetBestUrl(const char **pBestUrl) const;
|
bool GetBestUrl(const char **pBestUrl) const;
|
||||||
|
@ -51,26 +51,29 @@ private:
|
||||||
};
|
};
|
||||||
class CJob : public IJob
|
class CJob : public IJob
|
||||||
{
|
{
|
||||||
|
CChooseMaster *m_pParent;
|
||||||
CLock m_Lock;
|
CLock m_Lock;
|
||||||
std::shared_ptr<CData> m_pData;
|
std::shared_ptr<CData> m_pData;
|
||||||
std::unique_ptr<CHttpRequest> m_pHead PT_GUARDED_BY(m_Lock);
|
std::shared_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_pGet PT_GUARDED_BY(m_Lock);
|
||||||
void Run() override REQUIRES(!m_Lock);
|
void Run() override REQUIRES(!m_Lock);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CJob(std::shared_ptr<CData> pData) :
|
CJob(CChooseMaster *pParent, std::shared_ptr<CData> pData) :
|
||||||
m_pData(std::move(pData)) {}
|
m_pParent(pParent), m_pData(std::move(pData)) {}
|
||||||
void Abort() REQUIRES(!m_Lock);
|
void Abort() REQUIRES(!m_Lock);
|
||||||
};
|
};
|
||||||
|
|
||||||
IEngine *m_pEngine;
|
IEngine *m_pEngine;
|
||||||
|
IHttp *m_pHttp;
|
||||||
int m_PreviousBestIndex;
|
int m_PreviousBestIndex;
|
||||||
std::shared_ptr<CData> m_pData;
|
std::shared_ptr<CData> m_pData;
|
||||||
std::shared_ptr<CJob> m_pJob;
|
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_pEngine(pEngine),
|
||||||
|
m_pHttp(pHttp),
|
||||||
m_PreviousBestIndex(PreviousBestIndex)
|
m_PreviousBestIndex(PreviousBestIndex)
|
||||||
{
|
{
|
||||||
dbg_assert(NumUrls >= 0, "no master URLs");
|
dbg_assert(NumUrls >= 0, "no master URLs");
|
||||||
|
@ -128,7 +131,7 @@ void CChooseMaster::Reset()
|
||||||
void CChooseMaster::Refresh()
|
void CChooseMaster::Refresh()
|
||||||
{
|
{
|
||||||
if(m_pJob == nullptr || m_pJob->Status() == IJob::STATE_DONE)
|
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()
|
void CChooseMaster::CJob::Abort()
|
||||||
|
@ -171,14 +174,16 @@ void CChooseMaster::CJob::Run()
|
||||||
{
|
{
|
||||||
aTimeMs[i] = -1;
|
aTimeMs[i] = -1;
|
||||||
const char *pUrl = m_pData->m_aaUrls[aRandomized[i]];
|
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->Timeout(Timeout);
|
||||||
pHead->LogProgress(HTTPLOG::FAILURE);
|
pHead->LogProgress(HTTPLOG::FAILURE);
|
||||||
{
|
{
|
||||||
CLockScope ls(m_Lock);
|
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)
|
if(pHead->State() == HTTP_ABORTED)
|
||||||
{
|
{
|
||||||
dbg_msg("serverbrowse_http", "master chooser aborted");
|
dbg_msg("serverbrowse_http", "master chooser aborted");
|
||||||
|
@ -188,15 +193,19 @@ void CChooseMaster::CJob::Run()
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto StartTime = time_get_nanoseconds();
|
auto StartTime = time_get_nanoseconds();
|
||||||
CHttpRequest *pGet = HttpGet(pUrl).release();
|
std::shared_ptr<CHttpRequest> pGet = std::move(HttpGet(pUrl));
|
||||||
pGet->Timeout(Timeout);
|
pGet->Timeout(Timeout);
|
||||||
pGet->LogProgress(HTTPLOG::FAILURE);
|
pGet->LogProgress(HTTPLOG::FAILURE);
|
||||||
{
|
{
|
||||||
CLockScope ls(m_Lock);
|
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);
|
auto Time = std::chrono::duration_cast<std::chrono::milliseconds>(time_get_nanoseconds() - StartTime);
|
||||||
if(pHead->State() == HTTP_ABORTED)
|
if(pHead->State() == HTTP_ABORTED)
|
||||||
{
|
{
|
||||||
|
@ -212,6 +221,7 @@ void CChooseMaster::CJob::Run()
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ParseFailure = m_pData->m_pfnValidator(pJson);
|
bool ParseFailure = m_pData->m_pfnValidator(pJson);
|
||||||
json_value_free(pJson);
|
json_value_free(pJson);
|
||||||
if(ParseFailure)
|
if(ParseFailure)
|
||||||
|
@ -221,6 +231,7 @@ void CChooseMaster::CJob::Run()
|
||||||
dbg_msg("serverbrowse_http", "found master, url='%s' time=%dms", pUrl, (int)Time.count());
|
dbg_msg("serverbrowse_http", "found master, url='%s' time=%dms", pUrl, (int)Time.count());
|
||||||
aTimeMs[i] = Time.count();
|
aTimeMs[i] = Time.count();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine index of the minimum time.
|
// Determine index of the minimum time.
|
||||||
int BestIndex = -1;
|
int BestIndex = -1;
|
||||||
int BestTime = 0;
|
int BestTime = 0;
|
||||||
|
@ -241,6 +252,7 @@ void CChooseMaster::CJob::Run()
|
||||||
dbg_msg("serverbrowse_http", "WARNING: no usable masters found");
|
dbg_msg("serverbrowse_http", "WARNING: no usable masters found");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
dbg_msg("serverbrowse_http", "determined best master, url='%s' time=%dms", m_pData->m_aaUrls[BestIndex], BestTime);
|
dbg_msg("serverbrowse_http", "determined best master, url='%s' time=%dms", m_pData->m_aaUrls[BestIndex], BestTime);
|
||||||
m_pData->m_BestIndex.store(BestIndex);
|
m_pData->m_BestIndex.store(BestIndex);
|
||||||
}
|
}
|
||||||
|
@ -248,7 +260,7 @@ void CChooseMaster::CJob::Run()
|
||||||
class CServerBrowserHttp : public IServerBrowserHttp
|
class CServerBrowserHttp : public IServerBrowserHttp
|
||||||
{
|
{
|
||||||
public:
|
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;
|
~CServerBrowserHttp() override;
|
||||||
void Update() override;
|
void Update() override;
|
||||||
bool IsRefreshing() override { return m_State != STATE_DONE; }
|
bool IsRefreshing() override { return m_State != STATE_DONE; }
|
||||||
|
@ -286,6 +298,7 @@ private:
|
||||||
|
|
||||||
IEngine *m_pEngine;
|
IEngine *m_pEngine;
|
||||||
IConsole *m_pConsole;
|
IConsole *m_pConsole;
|
||||||
|
IHttp *m_pHttp;
|
||||||
|
|
||||||
int m_State = STATE_DONE;
|
int m_State = STATE_DONE;
|
||||||
std::shared_ptr<CHttpRequest> m_pGetServers;
|
std::shared_ptr<CHttpRequest> m_pGetServers;
|
||||||
|
@ -295,10 +308,11 @@ private:
|
||||||
std::vector<NETADDR> m_vLegacyServers;
|
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_pEngine(pEngine),
|
||||||
m_pConsole(pConsole),
|
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();
|
m_pChooseMaster->Refresh();
|
||||||
}
|
}
|
||||||
|
@ -328,7 +342,7 @@ void CServerBrowserHttp::Update()
|
||||||
m_pGetServers = HttpGet(pBestUrl);
|
m_pGetServers = HttpGet(pBestUrl);
|
||||||
// 10 seconds connection timeout, lower than 8KB/s for 10 seconds to fail.
|
// 10 seconds connection timeout, lower than 8KB/s for 10 seconds to fail.
|
||||||
m_pGetServers->Timeout(CTimeout{10000, 0, 8000, 10});
|
m_pGetServers->Timeout(CTimeout{10000, 0, 8000, 10});
|
||||||
m_pEngine->AddJob(m_pGetServers);
|
m_pHttp->Run(m_pGetServers);
|
||||||
m_State = STATE_REFRESHING;
|
m_State = STATE_REFRESHING;
|
||||||
}
|
}
|
||||||
else if(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",
|
"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];
|
char aaUrls[CChooseMaster::MAX_URLS][256];
|
||||||
const char *apUrls[CChooseMaster::MAX_URLS] = {0};
|
const char *apUrls[CChooseMaster::MAX_URLS] = {0};
|
||||||
|
@ -506,5 +520,5 @@ IServerBrowserHttp *CreateServerBrowserHttp(IEngine *pEngine, IConsole *pConsole
|
||||||
break;
|
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 IConsole;
|
||||||
class IEngine;
|
class IEngine;
|
||||||
class IStorage;
|
class IStorage;
|
||||||
|
class IHttp;
|
||||||
|
|
||||||
class IServerBrowserHttp
|
class IServerBrowserHttp
|
||||||
{
|
{
|
||||||
|
@ -25,5 +26,5 @@ public:
|
||||||
virtual const NETADDR &LegacyServer(int Index) const = 0;
|
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
|
#endif // ENGINE_CLIENT_SERVERBROWSER_HTTP_H
|
||||||
|
|
|
@ -25,7 +25,7 @@ class CUpdaterFetchTask : public CHttpRequest
|
||||||
void OnProgress() override;
|
void OnProgress() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
int OnCompletion(int State) override;
|
void OnCompletion() override;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CUpdaterFetchTask(CUpdater *pUpdater, const char *pFile, const char *pDestPath);
|
CUpdaterFetchTask(CUpdater *pUpdater, const char *pFile, const char *pDestPath);
|
||||||
|
@ -61,10 +61,8 @@ void CUpdaterFetchTask::OnProgress()
|
||||||
m_pUpdater->m_Percent = Progress();
|
m_pUpdater->m_Percent = Progress();
|
||||||
}
|
}
|
||||||
|
|
||||||
int CUpdaterFetchTask::OnCompletion(int State)
|
void CUpdaterFetchTask::OnCompletion()
|
||||||
{
|
{
|
||||||
State = CHttpRequest::OnCompletion(State);
|
|
||||||
|
|
||||||
const char *pFileName = 0;
|
const char *pFileName = 0;
|
||||||
for(const char *pPath = Dest(); *pPath; pPath++)
|
for(const char *pPath = Dest(); *pPath; pPath++)
|
||||||
if(*pPath == '/')
|
if(*pPath == '/')
|
||||||
|
@ -72,27 +70,26 @@ int CUpdaterFetchTask::OnCompletion(int State)
|
||||||
pFileName = pFileName ? pFileName : Dest();
|
pFileName = pFileName ? pFileName : Dest();
|
||||||
if(!str_comp(pFileName, "update.json"))
|
if(!str_comp(pFileName, "update.json"))
|
||||||
{
|
{
|
||||||
if(State == HTTP_DONE)
|
if(State() == HTTP_DONE)
|
||||||
m_pUpdater->SetCurrentState(IUpdater::GOT_MANIFEST);
|
m_pUpdater->SetCurrentState(IUpdater::GOT_MANIFEST);
|
||||||
else if(State == HTTP_ERROR)
|
else if(State() == HTTP_ERROR)
|
||||||
m_pUpdater->SetCurrentState(IUpdater::FAIL);
|
m_pUpdater->SetCurrentState(IUpdater::FAIL);
|
||||||
}
|
}
|
||||||
else if(!str_comp(pFileName, m_pUpdater->m_aLastFile))
|
else if(!str_comp(pFileName, m_pUpdater->m_aLastFile))
|
||||||
{
|
{
|
||||||
if(State == HTTP_DONE)
|
if(State() == HTTP_DONE)
|
||||||
m_pUpdater->SetCurrentState(IUpdater::MOVE_FILES);
|
m_pUpdater->SetCurrentState(IUpdater::MOVE_FILES);
|
||||||
else if(State == HTTP_ERROR)
|
else if(State() == HTTP_ERROR)
|
||||||
m_pUpdater->SetCurrentState(IUpdater::FAIL);
|
m_pUpdater->SetCurrentState(IUpdater::FAIL);
|
||||||
}
|
}
|
||||||
|
|
||||||
return State;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CUpdater::CUpdater()
|
CUpdater::CUpdater()
|
||||||
{
|
{
|
||||||
m_pClient = NULL;
|
m_pClient = nullptr;
|
||||||
m_pStorage = NULL;
|
m_pStorage = nullptr;
|
||||||
m_pEngine = NULL;
|
m_pEngine = nullptr;
|
||||||
|
m_pHttp = nullptr;
|
||||||
m_State = CLEAN;
|
m_State = CLEAN;
|
||||||
m_Percent = 0;
|
m_Percent = 0;
|
||||||
|
|
||||||
|
@ -100,11 +97,12 @@ CUpdater::CUpdater()
|
||||||
IStorage::FormatTmpPath(m_aServerExecTmp, sizeof(m_aServerExecTmp), SERVER_EXEC);
|
IStorage::FormatTmpPath(m_aServerExecTmp, sizeof(m_aServerExecTmp), SERVER_EXEC);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CUpdater::Init()
|
void CUpdater::Init(CHttp *pHttp)
|
||||||
{
|
{
|
||||||
m_pClient = Kernel()->RequestInterface<IClient>();
|
m_pClient = Kernel()->RequestInterface<IClient>();
|
||||||
m_pStorage = Kernel()->RequestInterface<IStorage>();
|
m_pStorage = Kernel()->RequestInterface<IStorage>();
|
||||||
m_pEngine = Kernel()->RequestInterface<IEngine>();
|
m_pEngine = Kernel()->RequestInterface<IEngine>();
|
||||||
|
m_pHttp = pHttp;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CUpdater::SetCurrentState(int NewState)
|
void CUpdater::SetCurrentState(int NewState)
|
||||||
|
@ -133,7 +131,7 @@ int CUpdater::GetCurrentPercent()
|
||||||
|
|
||||||
void CUpdater::FetchFile(const char *pFile, const char *pDestPath)
|
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)
|
bool CUpdater::MoveFile(const char *pFile)
|
||||||
|
|
|
@ -41,6 +41,7 @@ class CUpdater : public IUpdater
|
||||||
class IClient *m_pClient;
|
class IClient *m_pClient;
|
||||||
class IStorage *m_pStorage;
|
class IStorage *m_pStorage;
|
||||||
class IEngine *m_pEngine;
|
class IEngine *m_pEngine;
|
||||||
|
class CHttp *m_pHttp;
|
||||||
|
|
||||||
CLock m_Lock;
|
CLock m_Lock;
|
||||||
|
|
||||||
|
@ -77,7 +78,7 @@ public:
|
||||||
int GetCurrentPercent() override REQUIRES(!m_Lock);
|
int GetCurrentPercent() override REQUIRES(!m_Lock);
|
||||||
|
|
||||||
void InitiateUpdate() override;
|
void InitiateUpdate() override;
|
||||||
void Init();
|
void Init(CHttp *pHttp);
|
||||||
void Update() override;
|
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 <engine/storage.h>
|
||||||
#include <game/version.h>
|
#include <game/version.h>
|
||||||
|
|
||||||
|
#include <thread>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
#if !defined(CONF_FAMILY_WINDOWS)
|
#if !defined(CONF_FAMILY_WINDOWS)
|
||||||
#include <csignal>
|
#include <csignal>
|
||||||
#endif
|
#endif
|
||||||
|
@ -15,35 +18,6 @@
|
||||||
#define WIN32_LEAN_AND_MEAN
|
#define WIN32_LEAN_AND_MEAN
|
||||||
#include <curl/curl.h>
|
#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)
|
int CurlDebug(CURL *pHandle, curl_infotype Type, char *pData, size_t DataSize, void *pUser)
|
||||||
{
|
{
|
||||||
char TypeChar;
|
char TypeChar;
|
||||||
|
@ -71,39 +45,6 @@ int CurlDebug(CURL *pHandle, curl_infotype Type, char *pData, size_t DataSize, v
|
||||||
return 0;
|
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)
|
void EscapeUrl(char *pBuf, int Size, const char *pStr)
|
||||||
{
|
{
|
||||||
char *pEsc = curl_easy_escape(0, pStr, 0);
|
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()
|
bool CHttpRequest::BeforeInit()
|
||||||
{
|
{
|
||||||
if(m_WriteToFile)
|
if(m_WriteToFile)
|
||||||
|
@ -181,12 +104,12 @@ bool CHttpRequest::BeforeInit()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int CHttpRequest::RunImpl(CURL *pUser)
|
bool CHttpRequest::ConfigureHandle(void *pUser)
|
||||||
{
|
{
|
||||||
CURL *pHandle = (CURL *)pUser;
|
CURL *pHandle = (CURL *)pUser;
|
||||||
if(!pHandle)
|
if(!BeforeInit())
|
||||||
{
|
{
|
||||||
return HTTP_ERROR;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(g_Config.m_DbgCurl)
|
if(g_Config.m_DbgCurl)
|
||||||
|
@ -199,8 +122,8 @@ int CHttpRequest::RunImpl(CURL *pUser)
|
||||||
{
|
{
|
||||||
Protocols |= CURLPROTO_HTTP;
|
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_CONNECTTIMEOUT_MS, m_Timeout.ConnectTimeoutMs);
|
||||||
curl_easy_setopt(pHandle, CURLOPT_TIMEOUT_MS, m_Timeout.TimeoutMs);
|
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_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
|
// ‘CURLOPT_PROTOCOLS’ is deprecated: since 7.85.0. Use CURLOPT_PROTOCOLS_STR
|
||||||
// Wait until all platforms have 7.85.0
|
// Wait until all platforms have 7.85.0
|
||||||
#ifdef __GNUC__
|
#ifdef __GNUC__
|
||||||
|
@ -285,22 +207,7 @@ int CHttpRequest::RunImpl(CURL *pUser)
|
||||||
|
|
||||||
curl_easy_setopt(pHandle, CURLOPT_HTTPHEADER, m_pHeaders);
|
curl_easy_setopt(pHandle, CURLOPT_HTTPHEADER, m_pHeaders);
|
||||||
|
|
||||||
if(g_Config.m_DbgCurl || m_LogProgress >= HTTPLOG::ALL)
|
return true;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t CHttpRequest::OnData(char *pData, size_t DataSize)
|
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;
|
return pTask->m_Abort ? -1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int CHttpRequest::OnCompletion(int State)
|
void CHttpRequest::OnCompletionInternal(std::optional<unsigned int> Result)
|
||||||
{
|
{
|
||||||
if(m_Abort)
|
int State;
|
||||||
State = HTTP_ABORTED;
|
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)
|
if(State == HTTP_DONE && m_ExpectedSha256 != SHA256_ZEROED)
|
||||||
{
|
{
|
||||||
|
@ -391,7 +317,9 @@ int CHttpRequest::OnCompletion(int State)
|
||||||
fs_remove(m_aDestAbsolute);
|
fs_remove(m_aDestAbsolute);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return State;
|
|
||||||
|
m_State = State;
|
||||||
|
OnCompletion();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CHttpRequest::WriteToFile(IStorage *pStorage, const char *pDest, int StorageType)
|
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);
|
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
|
void CHttpRequest::Result(unsigned char **ppResult, size_t *pResultLength) const
|
||||||
{
|
{
|
||||||
if(m_WriteToFile || State() != HTTP_DONE)
|
if(m_WriteToFile || State() != HTTP_DONE)
|
||||||
|
@ -436,3 +378,192 @@ json_value *CHttpRequest::ResultJson() const
|
||||||
}
|
}
|
||||||
return json_parse((char *)pResult, ResultLength);
|
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 <algorithm>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
#include <mutex>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <deque>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
#include <engine/http.h>
|
||||||
|
|
||||||
typedef struct _json_value json_value;
|
typedef struct _json_value json_value;
|
||||||
class IStorage;
|
class IStorage;
|
||||||
|
@ -42,8 +48,10 @@ struct CTimeout
|
||||||
long LowSpeedTime;
|
long LowSpeedTime;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CHttpRequest : public IJob
|
class CHttpRequest : public IHttpRequest
|
||||||
{
|
{
|
||||||
|
friend class CHttp;
|
||||||
|
|
||||||
enum class REQUEST
|
enum class REQUEST
|
||||||
{
|
{
|
||||||
GET = 0,
|
GET = 0,
|
||||||
|
@ -51,6 +59,24 @@ class CHttpRequest : public IJob
|
||||||
POST,
|
POST,
|
||||||
POST_JSON,
|
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};
|
char m_aUrl[256] = {0};
|
||||||
|
|
||||||
void *m_pHeaders = nullptr;
|
void *m_pHeaders = nullptr;
|
||||||
|
@ -83,13 +109,14 @@ class CHttpRequest : public IJob
|
||||||
HTTPLOG m_LogProgress = HTTPLOG::ALL;
|
HTTPLOG m_LogProgress = HTTPLOG::ALL;
|
||||||
IPRESOLVE m_IpResolve = IPRESOLVE::WHATEVER;
|
IPRESOLVE m_IpResolve = IPRESOLVE::WHATEVER;
|
||||||
|
|
||||||
|
char m_aErr[256]; // 256 == CURL_ERROR_SIZE
|
||||||
std::atomic<int> m_State{HTTP_QUEUED};
|
std::atomic<int> m_State{HTTP_QUEUED};
|
||||||
std::atomic<bool> m_Abort{false};
|
std::atomic<bool> m_Abort{false};
|
||||||
|
|
||||||
void Run() override;
|
|
||||||
// Abort the request with an error if `BeforeInit()` returns false.
|
// Abort the request with an error if `BeforeInit()` returns false.
|
||||||
bool BeforeInit();
|
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
|
// Abort the request if `OnData()` returns something other than
|
||||||
// `DataSize`.
|
// `DataSize`.
|
||||||
|
@ -99,8 +126,9 @@ class CHttpRequest : public IJob
|
||||||
static size_t WriteCallback(char *pData, size_t Size, size_t Number, void *pUser);
|
static size_t WriteCallback(char *pData, size_t Size, size_t Number, void *pUser);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void OnProgress() {}
|
// These run on the curl thread now, DO NOT STALL THE THREAD
|
||||||
virtual int OnCompletion(int State);
|
virtual void OnProgress() {};
|
||||||
|
virtual void OnCompletion() {};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CHttpRequest(const char *pUrl);
|
CHttpRequest(const char *pUrl);
|
||||||
|
@ -157,8 +185,11 @@ public:
|
||||||
double Size() const { return m_Size.load(std::memory_order_relaxed); }
|
double Size() const { return m_Size.load(std::memory_order_relaxed); }
|
||||||
int Progress() const { return m_Progress.load(std::memory_order_relaxed); }
|
int Progress() const { return m_Progress.load(std::memory_order_relaxed); }
|
||||||
int State() const { return m_State; }
|
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 Abort() { m_Abort = true; }
|
||||||
|
|
||||||
|
void Wait();
|
||||||
|
|
||||||
void Result(unsigned char **ppResult, size_t *pResultLength) const;
|
void Result(unsigned char **ppResult, size_t *pResultLength) const;
|
||||||
json_value *ResultJson() const;
|
json_value *ResultJson() const;
|
||||||
};
|
};
|
||||||
|
@ -202,4 +233,41 @@ inline std::unique_ptr<CHttpRequest> HttpPostJson(const char *pUrl, const char *
|
||||||
bool HttpInit(IStorage *pStorage);
|
bool HttpInit(IStorage *pStorage);
|
||||||
void EscapeUrl(char *pBuf, int Size, const char *pStr);
|
void EscapeUrl(char *pBuf, int Size, const char *pStr);
|
||||||
bool HttpHasIpresolveBug();
|
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
|
#endif // ENGINE_SHARED_HTTP_H
|
||||||
|
|
|
@ -40,3 +40,5 @@ class IClient *CComponent::Client() const
|
||||||
{
|
{
|
||||||
return m_pClient->Client();
|
return m_pClient->Client();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class IHttp *CComponent::Http() const { return m_pClient->Http(); }
|
||||||
|
|
|
@ -128,6 +128,11 @@ protected:
|
||||||
*/
|
*/
|
||||||
float LocalTime() const;
|
float LocalTime() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the http interface
|
||||||
|
*/
|
||||||
|
class IHttp *Http() const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* The component virtual destructor.
|
* The component virtual destructor.
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include <engine/serverbrowser.h>
|
#include <engine/serverbrowser.h>
|
||||||
#include <engine/shared/config.h>
|
#include <engine/shared/config.h>
|
||||||
#include <engine/shared/http.h>
|
#include <engine/shared/http.h>
|
||||||
|
#include <engine/shared/jobs.h>
|
||||||
#include <engine/shared/linereader.h>
|
#include <engine/shared/linereader.h>
|
||||||
#include <engine/textrender.h>
|
#include <engine/textrender.h>
|
||||||
|
|
||||||
|
@ -515,34 +516,35 @@ protected:
|
||||||
char m_aPath[IO_MAX_PATH_LENGTH];
|
char m_aPath[IO_MAX_PATH_LENGTH];
|
||||||
int m_StorageType;
|
int m_StorageType;
|
||||||
bool m_Success = false;
|
bool m_Success = false;
|
||||||
CImageInfo m_ImageInfo;
|
|
||||||
SHA256_DIGEST m_Sha256;
|
SHA256_DIGEST m_Sha256;
|
||||||
|
|
||||||
CAbstractCommunityIconJob(CMenus *pMenus, const char *pCommunityId, int StorageType);
|
CAbstractCommunityIconJob(CMenus *pMenus, const char *pCommunityId, int StorageType);
|
||||||
virtual ~CAbstractCommunityIconJob();
|
virtual ~CAbstractCommunityIconJob() {};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
const char *CommunityId() const { return m_aCommunityId; }
|
const char *CommunityId() const { return m_aCommunityId; }
|
||||||
bool Success() const { return m_Success; }
|
bool Success() const { return m_Success; }
|
||||||
CImageInfo &&ImageInfo() { return std::move(m_ImageInfo); }
|
|
||||||
SHA256_DIGEST &&Sha256() { return std::move(m_Sha256); }
|
SHA256_DIGEST &&Sha256() { return std::move(m_Sha256); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class CCommunityIconLoadJob : public IJob, public CAbstractCommunityIconJob
|
class CCommunityIconLoadJob : public IJob, public CAbstractCommunityIconJob
|
||||||
{
|
{
|
||||||
|
CImageInfo m_ImageInfo;
|
||||||
protected:
|
protected:
|
||||||
void Run() override;
|
void Run() override;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CCommunityIconLoadJob(CMenus *pMenus, const char *pCommunityId, int StorageType);
|
CCommunityIconLoadJob(CMenus *pMenus, const char *pCommunityId, int StorageType);
|
||||||
|
|
||||||
|
CImageInfo &&ImageInfo() { return std::move(m_ImageInfo); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class CCommunityIconDownloadJob : public CHttpRequest, public CAbstractCommunityIconJob
|
class CCommunityIconDownloadJob : public CHttpRequest, public CAbstractCommunityIconJob
|
||||||
{
|
{
|
||||||
protected:
|
|
||||||
int OnCompletion(int State) override;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CCommunityIconDownloadJob(CMenus *pMenus, const char *pCommunityId, const char *pUrl, const SHA256_DIGEST &Sha256);
|
CCommunityIconDownloadJob(CMenus *pMenus, const char *pCommunityId, const char *pUrl, const SHA256_DIGEST &Sha256);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SCommunityIcon
|
struct SCommunityIcon
|
||||||
{
|
{
|
||||||
char m_aCommunityId[CServerInfo::MAX_COMMUNITY_ID_LENGTH];
|
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);
|
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) :
|
CMenus::CCommunityIconDownloadJob::CCommunityIconDownloadJob(CMenus *pMenus, const char *pCommunityId, const char *pUrl, const SHA256_DIGEST &Sha256) :
|
||||||
CHttpRequest(pUrl),
|
CHttpRequest(pUrl),
|
||||||
CAbstractCommunityIconJob(pMenus, pCommunityId, IStorage::TYPE_SAVE)
|
CAbstractCommunityIconJob(pMenus, pCommunityId, IStorage::TYPE_SAVE)
|
||||||
|
@ -1964,10 +1945,14 @@ void CMenus::UpdateCommunityIcons()
|
||||||
if(!m_CommunityIconDownloadJobs.empty())
|
if(!m_CommunityIconDownloadJobs.empty())
|
||||||
{
|
{
|
||||||
std::shared_ptr<CCommunityIconDownloadJob> pJob = m_CommunityIconDownloadJobs.front();
|
std::shared_ptr<CCommunityIconDownloadJob> pJob = m_CommunityIconDownloadJobs.front();
|
||||||
if(pJob->Status() == IJob::STATE_DONE)
|
if(pJob->Done())
|
||||||
{
|
{
|
||||||
if(pJob->Success())
|
if(pJob->State() == HTTP_DONE)
|
||||||
LoadCommunityIconFinish(pJob->CommunityId(), pJob->ImageInfo(), pJob->Sha256());
|
{
|
||||||
|
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();
|
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()))
|
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());
|
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);
|
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; });
|
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);
|
// Maybe this should start another thread to load the png in instead of stalling the curl thread
|
||||||
|
if(State() != HTTP_ERROR && State() != HTTP_ABORTED)
|
||||||
if(State != HTTP_ERROR && State != HTTP_ABORTED && !m_pSkins->LoadSkinPNG(m_Info, Dest(), Dest(), IStorage::TYPE_SAVE))
|
|
||||||
{
|
{
|
||||||
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) :
|
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];
|
char aEscapedName[256];
|
||||||
EscapeUrl(aEscapedName, sizeof(aEscapedName), pName);
|
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);
|
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];
|
char aBuf[IO_MAX_PATH_LENGTH];
|
||||||
str_format(Skin.m_aPath, sizeof(Skin.m_aPath), "downloadedskins/%s", IStorage::FormatTmpPath(aBuf, sizeof(aBuf), pName));
|
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);
|
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));
|
auto &&pDownloadSkin = std::make_unique<CDownloadSkin>(std::move(Skin));
|
||||||
m_DownloadSkins.insert({pDownloadSkin->GetName(), std::move(pDownloadSkin)});
|
m_DownloadSkins.insert({pDownloadSkin->GetName(), std::move(pDownloadSkin)});
|
||||||
++m_DownloadingSkins;
|
++m_DownloadingSkins;
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ public:
|
||||||
CSkins *m_pSkins;
|
CSkins *m_pSkins;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual int OnCompletion(int State) override;
|
virtual void OnCompletion() override;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CGetPngFile(CSkins *pSkins, const char *pUrl, IStorage *pStorage, const char *pDest);
|
CGetPngFile(CSkins *pSkins, const char *pUrl, IStorage *pStorage, const char *pDest);
|
||||||
|
|
|
@ -100,6 +100,7 @@ void CGameClient::OnConsoleInit()
|
||||||
#if defined(CONF_AUTOUPDATE)
|
#if defined(CONF_AUTOUPDATE)
|
||||||
m_pUpdater = Kernel()->RequestInterface<IUpdater>();
|
m_pUpdater = Kernel()->RequestInterface<IUpdater>();
|
||||||
#endif
|
#endif
|
||||||
|
m_pHttp = Kernel()->RequestInterface<IHttp>();
|
||||||
|
|
||||||
m_Menus.SetMenuBackground(&m_MenuBackground);
|
m_Menus.SetMenuBackground(&m_MenuBackground);
|
||||||
|
|
||||||
|
|
|
@ -177,6 +177,7 @@ private:
|
||||||
#if defined(CONF_AUTOUPDATE)
|
#if defined(CONF_AUTOUPDATE)
|
||||||
class IUpdater *m_pUpdater;
|
class IUpdater *m_pUpdater;
|
||||||
#endif
|
#endif
|
||||||
|
class IHttp *m_pHttp;
|
||||||
|
|
||||||
CLayers m_Layers;
|
CLayers m_Layers;
|
||||||
CCollision m_Collision;
|
CCollision m_Collision;
|
||||||
|
@ -251,6 +252,7 @@ public:
|
||||||
return m_pUpdater;
|
return m_pUpdater;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
class IHttp *Http() { return m_pHttp; }
|
||||||
|
|
||||||
int NetobjNumCorrections()
|
int NetobjNumCorrections()
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue