mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-13 03:28:19 +00:00
340e79904d
Make fetcher client only and move Task class from interface. Fix queue logic, add destructor. LEAN_AND_MEAN moved up to prevent curl including the whole winapi Remove JobNo, Fix callback, Cleanup debug messages. Fix include guard Copy the Url and Destination. Delete pTask. Fix typo Add Completion callback Let the user pass context Add virtual to inherited funcs Use 0 instead of NULL Give fetcher the ability to create folders. Fix couple of small bugs. Added .lib files for MSVC. Leave user the allocation of CFetchTask. Get rid of unnecessary cb arguments. Get the HTTP return code from libcurl. Incorperate the storage system. Fail on HTTP error >= 400. Add more info to the task. Add blocking way of getting HTTP resp code. Remove resp code from normal tasks as we fail >= 400 anyways. Sleep instead of killing the thread Forgot one instance of respcode. Provide HTTP download for maps. Dont check 404 first.
146 lines
4 KiB
C++
146 lines
4 KiB
C++
#include <base/system.h>
|
|
#include <engine/storage.h>
|
|
#include "fetcher.h"
|
|
|
|
CFetchTask::CFetchTask()
|
|
{
|
|
m_pNext = NULL;
|
|
}
|
|
|
|
CFetcher::CFetcher()
|
|
{
|
|
m_pStorage = NULL;
|
|
m_pHandle = NULL;
|
|
m_Lock = lock_create();
|
|
m_pFirst = NULL;
|
|
m_pLast = NULL;
|
|
}
|
|
|
|
bool CFetcher::Init()
|
|
{
|
|
m_pStorage = Kernel()->RequestInterface<IStorage>();
|
|
if(!curl_global_init(CURL_GLOBAL_DEFAULT) && (m_pHandle = curl_easy_init()))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
CFetcher::~CFetcher()
|
|
{
|
|
if(m_pHandle)
|
|
curl_easy_cleanup(m_pHandle);
|
|
curl_global_cleanup();
|
|
}
|
|
|
|
void CFetcher::QueueAdd(CFetchTask *pTask,const char *pUrl, const char *pDest, int StorageType, void *pUser, COMPFUNC pfnCompCb, PROGFUNC pfnProgCb)
|
|
{
|
|
str_copy(pTask->m_pUrl, pUrl, sizeof(pTask->m_pUrl));
|
|
str_copy(pTask->m_pDest, pDest, sizeof(pTask->m_pDest));
|
|
pTask->m_StorageType = StorageType;
|
|
pTask->m_pfnProgressCallback = pfnProgCb;
|
|
pTask->m_pfnCompCallback = pfnCompCb;
|
|
pTask->m_pUser = pUser;
|
|
pTask->m_Size = pTask->m_Progress = 0;
|
|
|
|
lock_wait(m_Lock);
|
|
if(!m_pFirst){
|
|
void *pHandle = thread_create(&FetcherThread, this);
|
|
thread_detach(pHandle);
|
|
m_pFirst = pTask;
|
|
m_pLast = m_pFirst;
|
|
}
|
|
else {
|
|
m_pLast->m_pNext = pTask;
|
|
m_pLast = pTask;
|
|
}
|
|
pTask->m_State = CFetchTask::STATE_QUEUED;
|
|
lock_release(m_Lock);
|
|
}
|
|
|
|
long CFetcher::HTTPResponse(const char *pUrl)
|
|
{
|
|
long resp;
|
|
CURL *pHandle = curl_easy_init();
|
|
curl_easy_setopt(pHandle, CURLOPT_CAINFO, "cacert.pem");
|
|
curl_easy_setopt(pHandle, CURLOPT_NOBODY, true);
|
|
curl_easy_setopt(pHandle, CURLOPT_URL, pUrl);
|
|
curl_easy_perform(pHandle);
|
|
curl_easy_getinfo(pHandle, CURLINFO_RESPONSE_CODE, &resp);
|
|
return resp;
|
|
}
|
|
|
|
void CFetcher::FetcherThread(void *pUser)
|
|
{
|
|
CFetcher *pFetcher = (CFetcher *) pUser;
|
|
dbg_msg("fetcher", "Thread started...");
|
|
while(1){
|
|
lock_wait(pFetcher->m_Lock);
|
|
CFetchTask *pTask = pFetcher->m_pFirst;
|
|
if(pTask)
|
|
pFetcher->m_pFirst = pTask->m_pNext;
|
|
lock_release(pFetcher->m_Lock);
|
|
if(pTask){
|
|
dbg_msg("fetcher", "Task got %s:%s", pTask->m_pUrl, pTask->m_pDest);
|
|
pFetcher->FetchFile(pTask);
|
|
}
|
|
else
|
|
thread_sleep(10);
|
|
}
|
|
}
|
|
|
|
bool CFetcher::FetchFile(CFetchTask *pTask)
|
|
{
|
|
for(int i = 0; pTask->m_pDest[i] != '\0'; i++){
|
|
if(pTask->m_pDest[i] == '/'){
|
|
pTask->m_pDest[i] = '\0';
|
|
m_pStorage->CreateFolder(pTask->m_pDest,2);
|
|
pTask->m_pDest[i] = '/';
|
|
}
|
|
}
|
|
|
|
IOHANDLE File = m_pStorage->OpenFile(pTask->m_pDest, IOFLAG_WRITE, pTask->m_StorageType);
|
|
|
|
char aErr[CURL_ERROR_SIZE];
|
|
curl_easy_setopt(m_pHandle, CURLOPT_ERRORBUFFER, aErr);
|
|
|
|
curl_easy_setopt(m_pHandle, CURLOPT_VERBOSE, 1L);
|
|
curl_easy_setopt(m_pHandle, CURLOPT_FAILONERROR, 1L);
|
|
curl_easy_setopt(m_pHandle, CURLOPT_CAINFO, "cacert.pem");
|
|
curl_easy_setopt(m_pHandle, CURLOPT_URL, pTask->m_pUrl);
|
|
curl_easy_setopt(m_pHandle, CURLOPT_WRITEDATA, File);
|
|
curl_easy_setopt(m_pHandle, CURLOPT_WRITEFUNCTION, &CFetcher::WriteToFile);
|
|
curl_easy_setopt(m_pHandle, CURLOPT_NOPROGRESS, 0);
|
|
curl_easy_setopt(m_pHandle, CURLOPT_PROGRESSDATA, pTask);
|
|
curl_easy_setopt(m_pHandle, CURLOPT_PROGRESSFUNCTION, &CFetcher::ProgressCallback);
|
|
|
|
dbg_msg("fetcher", "Downloading %s", pTask->m_pDest);
|
|
pTask->m_State = CFetchTask::STATE_RUNNING;
|
|
if(curl_easy_perform(m_pHandle) != CURLE_OK){
|
|
dbg_msg("fetcher", "Task failed. libcurl error: %s", aErr);
|
|
pTask->m_State = CFetchTask::STATE_ERROR;
|
|
return false;
|
|
}
|
|
pTask->m_State = CFetchTask::STATE_DONE;
|
|
io_close(File);
|
|
if(pTask->m_pfnCompCallback)
|
|
pTask->m_pfnCompCallback(pTask, pTask->m_pUser);
|
|
dbg_msg("fetcher", "Task done %s", pTask->m_pDest);
|
|
return true;
|
|
}
|
|
|
|
void CFetcher::WriteToFile(char *pData, size_t size, size_t nmemb, void *pFile)
|
|
{
|
|
io_write((IOHANDLE)pFile, pData, size*nmemb);
|
|
}
|
|
|
|
int CFetcher::ProgressCallback(void *pUser, double DlTotal, double DlCurr, double UlTotal, double UlCurr)
|
|
{
|
|
CFetchTask *pTask = (CFetchTask *)pUser;
|
|
dbg_msg("fetcher", "DlCurr:%f, DlTotal:%f", DlCurr, DlTotal);
|
|
pTask->m_Current = DlCurr;
|
|
if(!pTask->m_Size)
|
|
pTask->m_Size = DlTotal;
|
|
pTask->m_Progress = (100*DlCurr)/(DlTotal ? DlTotal : 1);
|
|
if(pTask->m_pfnProgressCallback)
|
|
pTask->m_pfnProgressCallback(pTask, pTask->m_pUser);
|
|
return 0;
|
|
} |