mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-17 21:48:19 +00:00
Merge pull request #7683 from Learath2/dd_pr_curlmultifinal
Use curl-multi. Supersedes #5842
This commit is contained in:
commit
bb3bd57c0e
|
@ -1884,6 +1884,7 @@ set_src(ENGINE_INTERFACE GLOB src/engine
|
|||
friends.h
|
||||
ghost.h
|
||||
graphics.h
|
||||
http.h
|
||||
input.h
|
||||
kernel.h
|
||||
keys.h
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 59d64dbb36ade02607ad20a7f3a45605ab1de80d
|
||||
Subproject commit 4d796ea119b52c8901286e14ab96faf7353a5d59
|
|
@ -2539,9 +2539,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);
|
||||
|
|
|
@ -1442,7 +1442,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
|
||||
{
|
||||
|
@ -2659,6 +2659,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()
|
||||
|
@ -2683,12 +2684,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);
|
||||
|
@ -4568,6 +4569,9 @@ bool CClient::RaceRecord_IsRecording()
|
|||
|
||||
void CClient::RequestDDNetInfo()
|
||||
{
|
||||
if(m_pDDNetInfoTask && !m_pDDNetInfoTask->Done())
|
||||
return;
|
||||
|
||||
char aUrl[256];
|
||||
str_copy(aUrl, DDNET_INFO_URL);
|
||||
|
||||
|
@ -4583,7 +4587,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();
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <engine/engine.h>
|
||||
#include <engine/favorites.h>
|
||||
#include <engine/friends.h>
|
||||
#include <engine/http.h>
|
||||
#include <engine/storage.h>
|
||||
|
||||
class CSortWrap
|
||||
|
@ -94,6 +95,7 @@ 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>();
|
||||
m_pPingCache = CreateServerBrowserPingCache(m_pConsole, m_pStorage);
|
||||
|
||||
RegisterCommands();
|
||||
|
@ -101,7 +103,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;
|
||||
std::shared_ptr<CHttpRequest> m_pGet;
|
||||
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 = 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 = 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; }
|
||||
|
@ -284,8 +296,8 @@ private:
|
|||
static bool Validate(json_value *pJson);
|
||||
static bool Parse(json_value *pJson, std::vector<CServerInfo> *pvServers, std::vector<NETADDR> *pvLegacyServers);
|
||||
|
||||
IEngine *m_pEngine;
|
||||
IConsole *m_pConsole;
|
||||
IHttp *m_pHttp;
|
||||
|
||||
int m_State = STATE_DONE;
|
||||
std::shared_ptr<CHttpRequest> m_pGetServers;
|
||||
|
@ -295,10 +307,10 @@ private:
|
|||
std::vector<NETADDR> m_vLegacyServers;
|
||||
};
|
||||
|
||||
CServerBrowserHttp::CServerBrowserHttp(IEngine *pEngine, IConsole *pConsole, const char **ppUrls, int NumUrls, int PreviousBestIndex) :
|
||||
m_pEngine(pEngine),
|
||||
CServerBrowserHttp::CServerBrowserHttp(IEngine *pEngine, IConsole *pConsole, IHttp *pHttp, const char **ppUrls, int NumUrls, int PreviousBestIndex) :
|
||||
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 +340,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 +481,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 +518,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
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
|
||||
#include <cstdlib> // system
|
||||
|
||||
using std::map;
|
||||
using std::string;
|
||||
|
||||
class CUpdaterFetchTask : public CHttpRequest
|
||||
|
@ -25,7 +24,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);
|
||||
|
@ -57,14 +56,11 @@ CUpdaterFetchTask::CUpdaterFetchTask(CUpdater *pUpdater, const char *pFile, cons
|
|||
void CUpdaterFetchTask::OnProgress()
|
||||
{
|
||||
CLockScope ls(m_pUpdater->m_Lock);
|
||||
str_copy(m_pUpdater->m_aStatus, Dest());
|
||||
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,39 +68,33 @@ 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)
|
||||
m_pUpdater->SetCurrentState(IUpdater::MOVE_FILES);
|
||||
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;
|
||||
m_pCurrentTask = nullptr;
|
||||
|
||||
IStorage::FormatTmpPath(m_aClientExecTmp, sizeof(m_aClientExecTmp), CLIENT_EXEC);
|
||||
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 +123,10 @@ int CUpdater::GetCurrentPercent()
|
|||
|
||||
void CUpdater::FetchFile(const char *pFile, const char *pDestPath)
|
||||
{
|
||||
m_pEngine->AddJob(std::make_shared<CUpdaterFetchTask>(this, pFile, pDestPath));
|
||||
CLockScope ls(m_Lock);
|
||||
m_pCurrentTask = std::make_shared<CUpdaterFetchTask>(this, pFile, pDestPath);
|
||||
str_copy(m_aStatus, m_pCurrentTask->Dest());
|
||||
m_pHttp->Run(m_pCurrentTask);
|
||||
}
|
||||
|
||||
bool CUpdater::MoveFile(const char *pFile)
|
||||
|
@ -170,11 +163,15 @@ bool CUpdater::MoveFile(const char *pFile)
|
|||
|
||||
void CUpdater::Update()
|
||||
{
|
||||
switch(m_State)
|
||||
auto State = GetCurrentState();
|
||||
switch(State)
|
||||
{
|
||||
case IUpdater::GOT_MANIFEST:
|
||||
PerformUpdate();
|
||||
break;
|
||||
case IUpdater::DOWNLOADING:
|
||||
RunningUpdate();
|
||||
break;
|
||||
case IUpdater::MOVE_FILES:
|
||||
CommitUpdate();
|
||||
break;
|
||||
|
@ -185,7 +182,7 @@ void CUpdater::Update()
|
|||
|
||||
void CUpdater::AddFileJob(const char *pFile, bool Job)
|
||||
{
|
||||
m_FileJobs[string(pFile)] = Job;
|
||||
m_FileJobs.emplace_front(std::make_pair(pFile, Job));
|
||||
}
|
||||
|
||||
bool CUpdater::ReplaceClient()
|
||||
|
@ -274,37 +271,44 @@ void CUpdater::ParseUpdate()
|
|||
break;
|
||||
}
|
||||
}
|
||||
json_value_free(pVersions);
|
||||
}
|
||||
|
||||
void CUpdater::InitiateUpdate()
|
||||
{
|
||||
m_State = GETTING_MANIFEST;
|
||||
SetCurrentState(IUpdater::GETTING_MANIFEST);
|
||||
FetchFile("update.json");
|
||||
}
|
||||
|
||||
void CUpdater::PerformUpdate()
|
||||
{
|
||||
m_State = PARSING_UPDATE;
|
||||
SetCurrentState(IUpdater::PARSING_UPDATE);
|
||||
dbg_msg("updater", "parsing update.json");
|
||||
ParseUpdate();
|
||||
m_State = DOWNLOADING;
|
||||
m_CurrentJob = m_FileJobs.begin();
|
||||
SetCurrentState(IUpdater::DOWNLOADING);
|
||||
}
|
||||
|
||||
const char *pLastFile;
|
||||
pLastFile = "";
|
||||
for(map<string, bool>::reverse_iterator it = m_FileJobs.rbegin(); it != m_FileJobs.rend(); ++it)
|
||||
void CUpdater::RunningUpdate()
|
||||
{
|
||||
if(m_pCurrentTask)
|
||||
{
|
||||
if(it->second)
|
||||
if(!m_pCurrentTask->Done())
|
||||
{
|
||||
pLastFile = it->first.c_str();
|
||||
break;
|
||||
return;
|
||||
}
|
||||
else if(m_pCurrentTask->State() == HTTP_ERROR || m_pCurrentTask->State() == HTTP_ABORTED)
|
||||
{
|
||||
SetCurrentState(IUpdater::FAIL);
|
||||
}
|
||||
}
|
||||
|
||||
for(auto &FileJob : m_FileJobs)
|
||||
if(m_CurrentJob != m_FileJobs.end())
|
||||
{
|
||||
if(FileJob.second)
|
||||
auto &Job = *m_CurrentJob;
|
||||
if(Job.second)
|
||||
{
|
||||
const char *pFile = FileJob.first.c_str();
|
||||
const char *pFile = Job.first.c_str();
|
||||
size_t len = str_length(pFile);
|
||||
if(!str_comp_nocase(pFile + len - 4, ".dll"))
|
||||
{
|
||||
|
@ -332,24 +336,32 @@ void CUpdater::PerformUpdate()
|
|||
{
|
||||
FetchFile(pFile);
|
||||
}
|
||||
pLastFile = pFile;
|
||||
}
|
||||
else
|
||||
m_pStorage->RemoveBinaryFile(FileJob.first.c_str());
|
||||
}
|
||||
{
|
||||
m_pStorage->RemoveBinaryFile(Job.first.c_str());
|
||||
}
|
||||
|
||||
if(m_ServerUpdate)
|
||||
{
|
||||
FetchFile(PLAT_SERVER_DOWN, m_aServerExecTmp);
|
||||
pLastFile = m_aServerExecTmp;
|
||||
m_CurrentJob++;
|
||||
}
|
||||
if(m_ClientUpdate)
|
||||
else
|
||||
{
|
||||
FetchFile(PLAT_CLIENT_DOWN, m_aClientExecTmp);
|
||||
pLastFile = m_aClientExecTmp;
|
||||
}
|
||||
if(m_ServerUpdate)
|
||||
{
|
||||
FetchFile(PLAT_SERVER_DOWN, m_aServerExecTmp);
|
||||
m_ServerUpdate = false;
|
||||
return;
|
||||
}
|
||||
|
||||
str_copy(m_aLastFile, pLastFile);
|
||||
if(m_ClientUpdate)
|
||||
{
|
||||
FetchFile(PLAT_SERVER_DOWN, m_aServerExecTmp);
|
||||
m_ClientUpdate = false;
|
||||
return;
|
||||
}
|
||||
|
||||
SetCurrentState(IUpdater::MOVE_FILES);
|
||||
}
|
||||
}
|
||||
|
||||
void CUpdater::CommitUpdate()
|
||||
|
@ -365,9 +377,9 @@ void CUpdater::CommitUpdate()
|
|||
if(m_ServerUpdate)
|
||||
Success &= ReplaceServer();
|
||||
if(!Success)
|
||||
m_State = FAIL;
|
||||
SetCurrentState(IUpdater::FAIL);
|
||||
else if(m_pClient->State() == IClient::STATE_ONLINE || m_pClient->EditorHasUnsavedData())
|
||||
m_State = NEED_RESTART;
|
||||
SetCurrentState(IUpdater::NEED_RESTART);
|
||||
else
|
||||
{
|
||||
m_pClient->Restart();
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
|
||||
#include <engine/updater.h>
|
||||
|
||||
#include <map>
|
||||
#include <forward_list>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#define CLIENT_EXEC "DDNet"
|
||||
|
@ -34,6 +35,8 @@
|
|||
#define PLAT_CLIENT_EXEC CLIENT_EXEC PLAT_EXT
|
||||
#define PLAT_SERVER_EXEC SERVER_EXEC PLAT_EXT
|
||||
|
||||
class CUpdaterFetchTask;
|
||||
|
||||
class CUpdater : public IUpdater
|
||||
{
|
||||
friend class CUpdaterFetchTask;
|
||||
|
@ -41,28 +44,31 @@ class CUpdater : public IUpdater
|
|||
class IClient *m_pClient;
|
||||
class IStorage *m_pStorage;
|
||||
class IEngine *m_pEngine;
|
||||
class CHttp *m_pHttp;
|
||||
|
||||
CLock m_Lock;
|
||||
|
||||
int m_State;
|
||||
int m_State GUARDED_BY(m_Lock);
|
||||
char m_aStatus[256] GUARDED_BY(m_Lock);
|
||||
int m_Percent GUARDED_BY(m_Lock);
|
||||
char m_aLastFile[256];
|
||||
char m_aClientExecTmp[64];
|
||||
char m_aServerExecTmp[64];
|
||||
|
||||
std::forward_list<std::pair<std::string, bool>> m_FileJobs;
|
||||
std::shared_ptr<CUpdaterFetchTask> m_pCurrentTask;
|
||||
decltype(m_FileJobs)::iterator m_CurrentJob;
|
||||
|
||||
bool m_ClientUpdate;
|
||||
bool m_ServerUpdate;
|
||||
|
||||
std::map<std::string, bool> m_FileJobs;
|
||||
|
||||
void AddFileJob(const char *pFile, bool Job);
|
||||
void FetchFile(const char *pFile, const char *pDestPath = nullptr);
|
||||
void FetchFile(const char *pFile, const char *pDestPath = nullptr) REQUIRES(!m_Lock);
|
||||
bool MoveFile(const char *pFile);
|
||||
|
||||
void ParseUpdate();
|
||||
void PerformUpdate();
|
||||
void CommitUpdate();
|
||||
void ParseUpdate() REQUIRES(!m_Lock);
|
||||
void PerformUpdate() REQUIRES(!m_Lock);
|
||||
void RunningUpdate() REQUIRES(!m_Lock);
|
||||
void CommitUpdate() REQUIRES(!m_Lock);
|
||||
|
||||
bool ReplaceClient();
|
||||
bool ReplaceServer();
|
||||
|
@ -76,9 +82,9 @@ public:
|
|||
void GetCurrentFile(char *pBuf, int BufSize) override REQUIRES(!m_Lock);
|
||||
int GetCurrentPercent() override REQUIRES(!m_Lock);
|
||||
|
||||
void InitiateUpdate() override;
|
||||
void Init();
|
||||
void Update() override;
|
||||
void InitiateUpdate() REQUIRES(!m_Lock) override;
|
||||
void Init(CHttp *pHttp);
|
||||
void Update() REQUIRES(!m_Lock) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
18
src/engine/http.h
Normal file
18
src/engine/http.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
#ifndef ENGINE_HTTP_H
|
||||
#define ENGINE_HTTP_H
|
||||
|
||||
#include "kernel.h"
|
||||
|
||||
class IHttpRequest
|
||||
{
|
||||
};
|
||||
|
||||
class IHttp : public IInterface
|
||||
{
|
||||
MACRO_INTERFACE("http")
|
||||
|
||||
public:
|
||||
virtual void Run(std::shared_ptr<IHttpRequest> pRequest) = 0;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -70,17 +70,19 @@ class CRegister : public IRegister
|
|||
int m_Index;
|
||||
int m_InfoSerial;
|
||||
std::shared_ptr<CShared> m_pShared;
|
||||
std::unique_ptr<CHttpRequest> m_pRegister;
|
||||
std::shared_ptr<CHttpRequest> m_pRegister;
|
||||
IHttp *m_pHttp;
|
||||
void Run() override;
|
||||
|
||||
public:
|
||||
CJob(int Protocol, int ServerPort, int Index, int InfoSerial, std::shared_ptr<CShared> pShared, std::unique_ptr<CHttpRequest> &&pRegister) :
|
||||
CJob(int Protocol, int ServerPort, int Index, int InfoSerial, std::shared_ptr<CShared> pShared, std::shared_ptr<CHttpRequest> &&pRegister, IHttp *pHttp) :
|
||||
m_Protocol(Protocol),
|
||||
m_ServerPort(ServerPort),
|
||||
m_Index(Index),
|
||||
m_InfoSerial(InfoSerial),
|
||||
m_pShared(std::move(pShared)),
|
||||
m_pRegister(std::move(pRegister))
|
||||
m_pRegister(std::move(pRegister)),
|
||||
m_pHttp(pHttp)
|
||||
{
|
||||
}
|
||||
~CJob() override = default;
|
||||
|
@ -110,6 +112,8 @@ class CRegister : public IRegister
|
|||
CConfig *m_pConfig;
|
||||
IConsole *m_pConsole;
|
||||
IEngine *m_pEngine;
|
||||
IHttp *m_pHttp;
|
||||
|
||||
// Don't start sending registers before the server has initialized
|
||||
// completely.
|
||||
bool m_GotFirstUpdateCall = false;
|
||||
|
@ -130,7 +134,7 @@ class CRegister : public IRegister
|
|||
char m_aServerInfo[16384];
|
||||
|
||||
public:
|
||||
CRegister(CConfig *pConfig, IConsole *pConsole, IEngine *pEngine, int ServerPort, unsigned SixupSecurityToken);
|
||||
CRegister(CConfig *pConfig, IConsole *pConsole, IEngine *pEngine, IHttp *pHttp, int ServerPort, unsigned SixupSecurityToken);
|
||||
void Update() override;
|
||||
void OnConfigChange() override;
|
||||
bool OnPacket(const CNetChunk *pPacket) override;
|
||||
|
@ -309,7 +313,7 @@ void CRegister::CProtocol::SendRegister()
|
|||
RequestIndex = m_pShared->m_NumTotalRequests;
|
||||
m_pShared->m_NumTotalRequests += 1;
|
||||
}
|
||||
m_pParent->m_pEngine->AddJob(std::make_shared<CJob>(m_Protocol, m_pParent->m_ServerPort, RequestIndex, InfoSerial, m_pShared, std::move(pRegister)));
|
||||
m_pParent->m_pEngine->AddJob(std::make_shared<CJob>(m_Protocol, m_pParent->m_ServerPort, RequestIndex, InfoSerial, m_pShared, std::move(pRegister), m_pParent->m_pHttp));
|
||||
m_NewChallengeToken = false;
|
||||
|
||||
m_PrevRegister = Now;
|
||||
|
@ -332,7 +336,7 @@ void CRegister::CProtocol::SendDeleteIfRegistered(bool Shutdown)
|
|||
char aSecret[UUID_MAXSTRSIZE];
|
||||
FormatUuid(m_pParent->m_Secret, aSecret, sizeof(aSecret));
|
||||
|
||||
std::unique_ptr<CHttpRequest> pDelete = HttpPost(m_pParent->m_pConfig->m_SvRegisterUrl, (const unsigned char *)"", 0);
|
||||
std::shared_ptr<CHttpRequest> pDelete = HttpPost(m_pParent->m_pConfig->m_SvRegisterUrl, (const unsigned char *)"", 0);
|
||||
pDelete->HeaderString("Action", "delete");
|
||||
pDelete->HeaderString("Address", aAddress);
|
||||
pDelete->HeaderString("Secret", aSecret);
|
||||
|
@ -348,7 +352,7 @@ void CRegister::CProtocol::SendDeleteIfRegistered(bool Shutdown)
|
|||
pDelete->Timeout(CTimeout{1000, 1000, 0, 0});
|
||||
}
|
||||
log_info(ProtocolToSystem(m_Protocol), "deleting...");
|
||||
m_pParent->m_pEngine->AddJob(std::move(pDelete));
|
||||
m_pParent->m_pHttp->Run(pDelete);
|
||||
}
|
||||
|
||||
CRegister::CProtocol::CProtocol(CRegister *pParent, int Protocol) :
|
||||
|
@ -405,7 +409,8 @@ void CRegister::CProtocol::OnToken(const char *pToken)
|
|||
|
||||
void CRegister::CProtocol::CJob::Run()
|
||||
{
|
||||
IEngine::RunJobBlocking(m_pRegister.get());
|
||||
m_pHttp->Run(m_pRegister);
|
||||
m_pRegister->Wait();
|
||||
if(m_pRegister->State() != HTTP_DONE)
|
||||
{
|
||||
// TODO: log the error response content from master
|
||||
|
@ -471,10 +476,11 @@ void CRegister::CProtocol::CJob::Run()
|
|||
}
|
||||
}
|
||||
|
||||
CRegister::CRegister(CConfig *pConfig, IConsole *pConsole, IEngine *pEngine, int ServerPort, unsigned SixupSecurityToken) :
|
||||
CRegister::CRegister(CConfig *pConfig, IConsole *pConsole, IEngine *pEngine, IHttp *pHttp, int ServerPort, unsigned SixupSecurityToken) :
|
||||
m_pConfig(pConfig),
|
||||
m_pConsole(pConsole),
|
||||
m_pEngine(pEngine),
|
||||
m_pHttp(pHttp),
|
||||
m_ServerPort(ServerPort),
|
||||
m_aProtocols{
|
||||
CProtocol(this, PROTOCOL_TW6_IPV6),
|
||||
|
@ -749,7 +755,7 @@ void CRegister::OnShutdown()
|
|||
}
|
||||
}
|
||||
|
||||
IRegister *CreateRegister(CConfig *pConfig, IConsole *pConsole, IEngine *pEngine, int ServerPort, unsigned SixupSecurityToken)
|
||||
IRegister *CreateRegister(CConfig *pConfig, IConsole *pConsole, IEngine *pEngine, IHttp *pHttp, int ServerPort, unsigned SixupSecurityToken)
|
||||
{
|
||||
return new CRegister(pConfig, pConsole, pEngine, ServerPort, SixupSecurityToken);
|
||||
return new CRegister(pConfig, pConsole, pEngine, pHttp, ServerPort, SixupSecurityToken);
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
class CConfig;
|
||||
class IConsole;
|
||||
class IEngine;
|
||||
class IHttp;
|
||||
struct CNetChunk;
|
||||
|
||||
class IRegister
|
||||
|
@ -23,6 +24,6 @@ public:
|
|||
virtual void OnShutdown() = 0;
|
||||
};
|
||||
|
||||
IRegister *CreateRegister(CConfig *pConfig, IConsole *pConsole, IEngine *pEngine, int ServerPort, unsigned SixupSecurityToken);
|
||||
IRegister *CreateRegister(CConfig *pConfig, IConsole *pConsole, IEngine *pEngine, IHttp *pHttp, int ServerPort, unsigned SixupSecurityToken);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -2792,7 +2792,8 @@ int CServer::Run()
|
|||
#endif
|
||||
|
||||
IEngine *pEngine = Kernel()->RequestInterface<IEngine>();
|
||||
m_pRegister = CreateRegister(&g_Config, m_pConsole, pEngine, this->Port(), m_NetServer.GetGlobalToken());
|
||||
IHttp *pHttp = Kernel()->RequestInterface<IHttp>();
|
||||
m_pRegister = CreateRegister(&g_Config, m_pConsole, pEngine, pHttp, this->Port(), m_NetServer.GetGlobalToken());
|
||||
|
||||
m_NetServer.SetCallbacks(NewClientCallback, NewClientNoAuthCallback, ClientRejoinCallback, DelClientCallback, this);
|
||||
|
||||
|
@ -3840,7 +3841,8 @@ void CServer::RegisterCommands()
|
|||
m_pStorage = Kernel()->RequestInterface<IStorage>();
|
||||
m_pAntibot = Kernel()->RequestInterface<IEngineAntibot>();
|
||||
|
||||
HttpInit(m_pStorage);
|
||||
m_Http.Init(std::chrono::seconds{2});
|
||||
Kernel()->RegisterInterface(static_cast<IHttp *>(&m_Http), false);
|
||||
|
||||
// register console commands
|
||||
Console()->Register("kick", "i[id] ?r[reason]", CFGFLAG_SERVER, ConKick, this, "Kick player with specified id for any reason");
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <engine/shared/demo.h>
|
||||
#include <engine/shared/econ.h>
|
||||
#include <engine/shared/fifo.h>
|
||||
#include <engine/shared/http.h>
|
||||
#include <engine/shared/netban.h>
|
||||
#include <engine/shared/network.h>
|
||||
#include <engine/shared/protocol.h>
|
||||
|
@ -238,6 +239,7 @@ public:
|
|||
CEcon m_Econ;
|
||||
CFifo m_Fifo;
|
||||
CServerBan m_ServerBan;
|
||||
CHttp m_Http;
|
||||
|
||||
IEngineMap *m_pMap;
|
||||
|
||||
|
|
|
@ -8,6 +8,9 @@
|
|||
#include <engine/storage.h>
|
||||
#include <game/version.h>
|
||||
|
||||
#include <limits>
|
||||
#include <thread>
|
||||
|
||||
#if !defined(CONF_FAMILY_WINDOWS)
|
||||
#include <csignal>
|
||||
#endif
|
||||
|
@ -15,34 +18,10 @@
|
|||
#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();
|
||||
}
|
||||
// There is a stray constant on Windows/MSVC...
|
||||
#ifdef ERROR
|
||||
#undef ERROR
|
||||
#endif
|
||||
|
||||
int CurlDebug(CURL *pHandle, curl_infotype Type, char *pData, size_t DataSize, void *pUser)
|
||||
{
|
||||
|
@ -71,39 +50,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 +89,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,80 +109,79 @@ bool CHttpRequest::BeforeInit()
|
|||
return true;
|
||||
}
|
||||
|
||||
int CHttpRequest::RunImpl(CURL *pUser)
|
||||
bool CHttpRequest::ConfigureHandle(void *pHandle)
|
||||
{
|
||||
CURL *pHandle = (CURL *)pUser;
|
||||
if(!pHandle)
|
||||
CURL *pH = (CURL *)pHandle;
|
||||
if(!BeforeInit())
|
||||
{
|
||||
return HTTP_ERROR;
|
||||
return false;
|
||||
}
|
||||
|
||||
if(g_Config.m_DbgCurl)
|
||||
{
|
||||
curl_easy_setopt(pHandle, CURLOPT_VERBOSE, 1L);
|
||||
curl_easy_setopt(pHandle, CURLOPT_DEBUGFUNCTION, CurlDebug);
|
||||
curl_easy_setopt(pH, CURLOPT_VERBOSE, 1L);
|
||||
curl_easy_setopt(pH, CURLOPT_DEBUGFUNCTION, CurlDebug);
|
||||
}
|
||||
long Protocols = CURLPROTO_HTTPS;
|
||||
if(g_Config.m_HttpAllowInsecure)
|
||||
{
|
||||
Protocols |= CURLPROTO_HTTP;
|
||||
}
|
||||
char aErr[CURL_ERROR_SIZE];
|
||||
curl_easy_setopt(pHandle, CURLOPT_ERRORBUFFER, aErr);
|
||||
|
||||
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_LOW_SPEED_LIMIT, m_Timeout.LowSpeedLimit);
|
||||
curl_easy_setopt(pHandle, CURLOPT_LOW_SPEED_TIME, m_Timeout.LowSpeedTime);
|
||||
curl_easy_setopt(pH, CURLOPT_ERRORBUFFER, m_aErr);
|
||||
|
||||
curl_easy_setopt(pH, CURLOPT_CONNECTTIMEOUT_MS, m_Timeout.ConnectTimeoutMs);
|
||||
curl_easy_setopt(pH, CURLOPT_TIMEOUT_MS, m_Timeout.TimeoutMs);
|
||||
curl_easy_setopt(pH, CURLOPT_LOW_SPEED_LIMIT, m_Timeout.LowSpeedLimit);
|
||||
curl_easy_setopt(pH, CURLOPT_LOW_SPEED_TIME, m_Timeout.LowSpeedTime);
|
||||
if(m_MaxResponseSize >= 0)
|
||||
{
|
||||
curl_easy_setopt(pHandle, CURLOPT_MAXFILESIZE_LARGE, (curl_off_t)m_MaxResponseSize);
|
||||
curl_easy_setopt(pH, 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__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif
|
||||
curl_easy_setopt(pHandle, CURLOPT_PROTOCOLS, Protocols);
|
||||
curl_easy_setopt(pH, CURLOPT_PROTOCOLS, Protocols);
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
curl_easy_setopt(pHandle, CURLOPT_FOLLOWLOCATION, 1L);
|
||||
curl_easy_setopt(pHandle, CURLOPT_MAXREDIRS, 4L);
|
||||
curl_easy_setopt(pHandle, CURLOPT_FAILONERROR, 1L);
|
||||
curl_easy_setopt(pHandle, CURLOPT_URL, m_aUrl);
|
||||
curl_easy_setopt(pHandle, CURLOPT_NOSIGNAL, 1L);
|
||||
curl_easy_setopt(pHandle, CURLOPT_USERAGENT, GAME_NAME " " GAME_RELEASE_VERSION " (" CONF_PLATFORM_STRING "; " CONF_ARCH_STRING ")");
|
||||
curl_easy_setopt(pHandle, CURLOPT_ACCEPT_ENCODING, ""); // Use any compression algorithm supported by libcurl.
|
||||
curl_easy_setopt(pH, CURLOPT_FOLLOWLOCATION, 1L);
|
||||
curl_easy_setopt(pH, CURLOPT_MAXREDIRS, 4L);
|
||||
curl_easy_setopt(pH, CURLOPT_FAILONERROR, 1L);
|
||||
curl_easy_setopt(pH, CURLOPT_URL, m_aUrl);
|
||||
curl_easy_setopt(pH, CURLOPT_NOSIGNAL, 1L);
|
||||
curl_easy_setopt(pH, CURLOPT_USERAGENT, GAME_NAME " " GAME_RELEASE_VERSION " (" CONF_PLATFORM_STRING "; " CONF_ARCH_STRING ")");
|
||||
curl_easy_setopt(pH, CURLOPT_ACCEPT_ENCODING, ""); // Use any compression algorithm supported by libcurl.
|
||||
|
||||
curl_easy_setopt(pHandle, CURLOPT_WRITEDATA, this);
|
||||
curl_easy_setopt(pHandle, CURLOPT_WRITEFUNCTION, WriteCallback);
|
||||
curl_easy_setopt(pHandle, CURLOPT_NOPROGRESS, 0L);
|
||||
curl_easy_setopt(pHandle, CURLOPT_PROGRESSDATA, this);
|
||||
curl_easy_setopt(pH, CURLOPT_WRITEDATA, this);
|
||||
curl_easy_setopt(pH, CURLOPT_WRITEFUNCTION, WriteCallback);
|
||||
curl_easy_setopt(pH, CURLOPT_NOPROGRESS, 0L);
|
||||
curl_easy_setopt(pH, CURLOPT_PROGRESSDATA, this);
|
||||
// ‘CURLOPT_PROGRESSFUNCTION’ is deprecated: since 7.32.0. Use CURLOPT_XFERINFOFUNCTION
|
||||
// See problems with curl_off_t type in header file in https://github.com/ddnet/ddnet/pull/6185/
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif
|
||||
curl_easy_setopt(pHandle, CURLOPT_PROGRESSFUNCTION, ProgressCallback);
|
||||
curl_easy_setopt(pH, CURLOPT_PROGRESSFUNCTION, ProgressCallback);
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
curl_easy_setopt(pHandle, CURLOPT_IPRESOLVE, m_IpResolve == IPRESOLVE::V4 ? CURL_IPRESOLVE_V4 : m_IpResolve == IPRESOLVE::V6 ? CURL_IPRESOLVE_V6 : CURL_IPRESOLVE_WHATEVER);
|
||||
curl_easy_setopt(pH, CURLOPT_IPRESOLVE, m_IpResolve == IPRESOLVE::V4 ? CURL_IPRESOLVE_V4 : m_IpResolve == IPRESOLVE::V6 ? CURL_IPRESOLVE_V6 : CURL_IPRESOLVE_WHATEVER);
|
||||
if(g_Config.m_Bindaddr[0] != '\0')
|
||||
{
|
||||
curl_easy_setopt(pHandle, CURLOPT_INTERFACE, g_Config.m_Bindaddr);
|
||||
curl_easy_setopt(pH, CURLOPT_INTERFACE, g_Config.m_Bindaddr);
|
||||
}
|
||||
|
||||
if(curl_version_info(CURLVERSION_NOW)->version_num < 0x074400)
|
||||
{
|
||||
// Causes crashes, see https://github.com/ddnet/ddnet/issues/4342.
|
||||
// No longer a problem in curl 7.68 and above, and 0x44 = 68.
|
||||
curl_easy_setopt(pHandle, CURLOPT_FORBID_REUSE, 1L);
|
||||
curl_easy_setopt(pH, CURLOPT_FORBID_REUSE, 1L);
|
||||
}
|
||||
|
||||
#ifdef CONF_PLATFORM_ANDROID
|
||||
|
@ -266,7 +193,7 @@ int CHttpRequest::RunImpl(CURL *pUser)
|
|||
case REQUEST::GET:
|
||||
break;
|
||||
case REQUEST::HEAD:
|
||||
curl_easy_setopt(pHandle, CURLOPT_NOBODY, 1L);
|
||||
curl_easy_setopt(pH, CURLOPT_NOBODY, 1L);
|
||||
break;
|
||||
case REQUEST::POST:
|
||||
case REQUEST::POST_JSON:
|
||||
|
@ -278,29 +205,14 @@ int CHttpRequest::RunImpl(CURL *pUser)
|
|||
{
|
||||
Header("Content-Type:");
|
||||
}
|
||||
curl_easy_setopt(pHandle, CURLOPT_POSTFIELDS, m_pBody);
|
||||
curl_easy_setopt(pHandle, CURLOPT_POSTFIELDSIZE, m_BodyLength);
|
||||
curl_easy_setopt(pH, CURLOPT_POSTFIELDS, m_pBody);
|
||||
curl_easy_setopt(pH, CURLOPT_POSTFIELDSIZE, m_BodyLength);
|
||||
break;
|
||||
}
|
||||
|
||||
curl_easy_setopt(pHandle, CURLOPT_HTTPHEADER, m_pHeaders);
|
||||
curl_easy_setopt(pH, 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 +268,30 @@ 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 +323,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 +347,22 @@ 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 +386,215 @@ 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 != CHttp::UNINITIALIZED; });
|
||||
if(m_State != CHttp::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 = CHttp::ERROR;
|
||||
m_Cv.notify_all();
|
||||
return;
|
||||
}
|
||||
|
||||
m_pMultiH = curl_multi_init();
|
||||
if(!m_pMultiH)
|
||||
{
|
||||
dbg_msg("http", "curl_multi_init failed");
|
||||
m_State = CHttp::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 = CHttp::RUNNING;
|
||||
m_Cv.notify_all();
|
||||
dbg_msg("http", "running");
|
||||
Lock.unlock();
|
||||
|
||||
while(m_State == CHttp::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 = CHttp::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 = CHttp::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 = CHttp::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 != CHttp::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 != CHttp::UNINITIALIZED; });
|
||||
m_PendingRequests.emplace_back(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 != CHttp::RUNNING)
|
||||
return;
|
||||
|
||||
m_Shutdown = true;
|
||||
curl_multi_wakeup(m_pMultiH);
|
||||
}
|
||||
|
||||
CHttp::~CHttp()
|
||||
{
|
||||
if(!m_pThread)
|
||||
return;
|
||||
|
||||
Shutdown();
|
||||
thread_wait(m_pThread);
|
||||
}
|
||||
|
|
|
@ -7,6 +7,13 @@
|
|||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <deque>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <engine/http.h>
|
||||
|
||||
typedef struct _json_value json_value;
|
||||
class IStorage;
|
||||
|
@ -42,8 +49,10 @@ struct CTimeout
|
|||
long LowSpeedTime;
|
||||
};
|
||||
|
||||
class CHttpRequest : public IJob
|
||||
class CHttpRequest : public IHttpRequest
|
||||
{
|
||||
friend class CHttp;
|
||||
|
||||
enum class REQUEST
|
||||
{
|
||||
GET = 0,
|
||||
|
@ -51,6 +60,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 +110,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,12 +127,13 @@ 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);
|
||||
~CHttpRequest();
|
||||
virtual ~CHttpRequest();
|
||||
|
||||
void Timeout(CTimeout Timeout) { m_Timeout = Timeout; }
|
||||
void MaxResponseSize(int64_t MaxResponseSize) { m_MaxResponseSize = MaxResponseSize; }
|
||||
|
@ -157,8 +186,15 @@ 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_RUNNING;
|
||||
}
|
||||
void Abort() { m_Abort = true; }
|
||||
|
||||
void Wait();
|
||||
|
||||
void Result(unsigned char **ppResult, size_t *pResultLength) const;
|
||||
json_value *ResultJson() const;
|
||||
};
|
||||
|
@ -199,7 +235,45 @@ inline std::unique_ptr<CHttpRequest> HttpPostJson(const char *pUrl, const char *
|
|||
return pResult;
|
||||
}
|
||||
|
||||
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;
|
||||
~CHttp();
|
||||
};
|
||||
|
||||
#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>
|
||||
|
||||
|
@ -512,34 +513,37 @@ 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);
|
||||
~CCommunityIconLoadJob();
|
||||
|
||||
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)
|
||||
|
@ -1841,6 +1822,12 @@ CMenus::CCommunityIconLoadJob::CCommunityIconLoadJob(CMenus *pMenus, const char
|
|||
{
|
||||
}
|
||||
|
||||
CMenus::CCommunityIconLoadJob::~CCommunityIconLoadJob()
|
||||
{
|
||||
free(m_ImageInfo.m_pData);
|
||||
m_ImageInfo.m_pData = nullptr;
|
||||
}
|
||||
|
||||
int CMenus::CommunityIconScan(const char *pName, int IsDir, int DirType, void *pUser)
|
||||
{
|
||||
const char *pExtension = ".png";
|
||||
|
@ -1964,10 +1951,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 +1998,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,10 @@ public:
|
|||
return m_pUpdater;
|
||||
}
|
||||
#endif
|
||||
class IHttp *Http()
|
||||
{
|
||||
return m_pHttp;
|
||||
}
|
||||
|
||||
int NetobjNumCorrections()
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue