ddnet/src/engine/client/fetcher.cpp
Learath 340e79904d Added fetcher interface.
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.
2015-01-19 22:14:52 +01:00

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;
}