123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857 |
- /****************************************************************************
- Copyright (c) 2015-2016 Chukong Technologies Inc.
- Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
- http://www.cocos2d-x.org
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- THE SOFTWARE.
- ****************************************************************************/
- #include "network/CCDownloader-curl.h"
- #include <set>
- #include <curl/curl.h>
- #include "base/CCDirector.h"
- #include "base/CCScheduler.h"
- #include "platform/CCFileUtils.h"
- #include "network/CCDownloader.h"
- // **NOTE**
- // In the file:
- // member function with suffix "Proc" designed called in DownloaderCURL::_threadProc
- // member function without suffix designed called in main thread
- namespace cocos2d { namespace network {
- using namespace std;
- ////////////////////////////////////////////////////////////////////////////////
- // Implementation DownloadTaskCURL
- class DownloadTaskCURL : public IDownloadTask
- {
- static int _sSerialId;
- // if more than one task write to one file, cause file broken
- // so use a set to check this situation
- static set<string> _sStoragePathSet;
- public:
- int serialId;
- DownloadTaskCURL()
- : serialId(_sSerialId++)
- , _fp(nullptr)
- {
- _initInternal();
- DLLOG("Construct DownloadTaskCURL %p", this);
- }
- virtual ~DownloadTaskCURL()
- {
- // if task destroyed unnormally, we should release WritenFileName stored in set.
- // Normally, this action should done when task finished.
- if (_tempFileName.length() && _sStoragePathSet.end() != _sStoragePathSet.find(_tempFileName))
- {
- DownloadTaskCURL::_sStoragePathSet.erase(_tempFileName);
- }
- if (_fp)
- {
- fclose(_fp);
- _fp = nullptr;
- }
- DLLOG("Destruct DownloadTaskCURL %p", this);
- }
- bool init(const string& filename, const string& tempSuffix)
- {
- if (0 == filename.length())
- {
- // data task
- _buf.reserve(CURL_MAX_WRITE_SIZE);
- return true;
- }
- // file task
- _fileName = filename;
- _tempFileName = filename;
- _tempFileName.append(tempSuffix);
- if (_sStoragePathSet.end() != _sStoragePathSet.find(_tempFileName))
- {
- // there is another task uses this storage path
- _errCode = DownloadTask::ERROR_FILE_OP_FAILED;
- _errCodeInternal = 0;
- _errDescription = "More than one download file task write to same file:";
- _errDescription.append(_tempFileName);
- return false;
- }
- _sStoragePathSet.insert(_tempFileName);
- // open temp file handle for write
- bool ret = false;
- do
- {
- string dir;
- size_t found = _tempFileName.find_last_of("/\\");
- if (found == string::npos)
- {
- _errCode = DownloadTask::ERROR_INVALID_PARAMS;
- _errCodeInternal = 0;
- _errDescription = "Can't find dirname in storagePath.";
- break;
- }
- // ensure directory is exist
- auto util = FileUtils::getInstance();
- dir = _tempFileName.substr(0, found+1);
- if (false == util->isDirectoryExist(dir))
- {
- if (false == util->createDirectory(dir))
- {
- _errCode = DownloadTask::ERROR_FILE_OP_FAILED;
- _errCodeInternal = 0;
- _errDescription = "Can't create dir:";
- _errDescription.append(dir);
- break;
- }
- }
- // open file
- _fp = fopen(util->getSuitableFOpen(_tempFileName).c_str(), "ab");
- if (nullptr == _fp)
- {
- _errCode = DownloadTask::ERROR_FILE_OP_FAILED;
- _errCodeInternal = 0;
- _errDescription = "Can't open file:";
- _errDescription.append(_tempFileName);
- }
- ret = true;
- } while (0);
- return ret;
- }
- void initProc()
- {
- lock_guard<mutex> lock(_mutex);
- _initInternal();
- }
- void setErrorProc(int code, int codeInternal, const char *desc)
- {
- lock_guard<mutex> lock(_mutex);
- _errCode = code;
- _errCodeInternal = codeInternal;
- _errDescription = desc;
- }
- size_t writeDataProc(unsigned char *buffer, size_t size, size_t count)
- {
- lock_guard<mutex> lock(_mutex);
- size_t ret = 0;
- if (_fp)
- {
- ret = fwrite(buffer, size, count, _fp);
- }
- else
- {
- ret = size * count;
- auto cap = _buf.capacity();
- auto bufSize = _buf.size();
- if (cap < bufSize + ret)
- {
- _buf.reserve(bufSize * 2);
- }
- _buf.insert(_buf.end() , buffer, buffer + ret);
- }
- if (ret)
- {
- _bytesReceived += ret;
- _totalBytesReceived += ret;
- }
- return ret;
- }
- private:
- friend class DownloaderCURL;
- // for lock object instance
- mutex _mutex;
- // header info
- bool _acceptRanges;
- bool _headerAchieved;
- int64_t _totalBytesExpected;
- string _header; // temp buffer for receive header string, only used in thread proc
- // progress
- int64_t _bytesReceived;
- int64_t _totalBytesReceived;
- // error
- int _errCode;
- int _errCodeInternal;
- string _errDescription;
- // for saving data
- string _fileName;
- string _tempFileName;
- vector<unsigned char> _buf;
- FILE* _fp;
- void _initInternal()
- {
- _acceptRanges = (false);
- _headerAchieved = (false);
- _bytesReceived = (0);
- _totalBytesReceived = (0);
- _totalBytesExpected = (0);
- _errCode = (DownloadTask::ERROR_NO_ERROR);
- _errCodeInternal = (CURLE_OK);
- _header.resize(0);
- _header.reserve(384); // pre alloc header string buffer
- }
- };
- int DownloadTaskCURL::_sSerialId;
- set<string> DownloadTaskCURL::_sStoragePathSet;
- typedef pair< shared_ptr<const DownloadTask>, DownloadTaskCURL *> TaskWrapper;
- ////////////////////////////////////////////////////////////////////////////////
- // Implementation DownloaderCURL::Impl
- // This class shared by DownloaderCURL and work thread.
- class DownloaderCURL::Impl : public enable_shared_from_this<DownloaderCURL::Impl>
- {
- public:
- DownloaderHints hints;
- Impl()
- // : _thread(nullptr)
- {
- DLLOG("Construct DownloaderCURL::Impl %p", this);
- }
- ~Impl()
- {
- DLLOG("Destruct DownloaderCURL::Impl %p %d", this, _thread.joinable());
- }
- void addTask(std::shared_ptr<const DownloadTask> task, DownloadTaskCURL* coTask)
- {
- if (DownloadTask::ERROR_NO_ERROR == coTask->_errCode)
- {
- lock_guard<mutex> lock(_requestMutex);
- _requestQueue.push_back(make_pair(task, coTask));
- }
- else
- {
- lock_guard<mutex> lock(_finishedMutex);
- _finishedQueue.push_back(make_pair(task, coTask));
- }
- }
- void run()
- {
- lock_guard<mutex> lock(_threadMutex);
- if (false == _thread.joinable())
- {
- thread newThread(&DownloaderCURL::Impl::_threadProc, this);
- _thread.swap(newThread);
- }
- }
- void stop()
- {
- lock_guard<mutex> lock(_threadMutex);
- if (_thread.joinable())
- {
- _thread.detach();
- }
- }
- bool stoped()
- {
- lock_guard<mutex> lock(_threadMutex);
- return false == _thread.joinable() ? true : false;
- }
- void getProcessTasks(vector<TaskWrapper>& outList)
- {
- lock_guard<mutex> lock(_processMutex);
- outList.reserve(_processSet.size());
- outList.insert(outList.end(), _processSet.begin(), _processSet.end());
- }
- void getFinishedTasks(vector<TaskWrapper>& outList)
- {
- lock_guard<mutex> lock(_finishedMutex);
- outList.reserve(_finishedQueue.size());
- outList.insert(outList.end(), _finishedQueue.begin(), _finishedQueue.end());
- _finishedQueue.clear();
- }
- private:
- static size_t _outputHeaderCallbackProc(void *buffer, size_t size, size_t count, void *userdata)
- {
- int strLen = int(size * count);
- DLLOG(" _outputHeaderCallbackProc: %.*s", strLen, buffer);
- DownloadTaskCURL& coTask = *((DownloadTaskCURL*)(userdata));
- coTask._header.append((const char *)buffer, strLen);
- return strLen;
- }
- static size_t _outputDataCallbackProc(void *buffer, size_t size, size_t count, void *userdata)
- {
- // DLLOG(" _outputDataCallbackProc: size(%ld), count(%ld)", size, count);
- DownloadTaskCURL *coTask = (DownloadTaskCURL*)userdata;
- // If your callback function returns CURL_WRITEFUNC_PAUSE it will cause this transfer to become paused.
- return coTask->writeDataProc((unsigned char *)buffer, size, count);
- }
- // this function designed call in work thread
- // the curl handle destroyed in _threadProc
- // handle inited for get header
- void _initCurlHandleProc(CURL *handle, TaskWrapper& wrapper, bool forContent = false)
- {
- const DownloadTask& task = *wrapper.first;
- const DownloadTaskCURL* coTask = wrapper.second;
- // set url
- curl_easy_setopt(handle, CURLOPT_URL, task.requestURL.c_str());
- // set write func
- if (forContent)
- {
- curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, DownloaderCURL::Impl::_outputDataCallbackProc);
- }
- else
- {
- curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, DownloaderCURL::Impl::_outputHeaderCallbackProc);
- }
- curl_easy_setopt(handle, CURLOPT_WRITEDATA, coTask);
- curl_easy_setopt(handle, CURLOPT_NOPROGRESS, true);
- // curl_easy_setopt(handle, CURLOPT_XFERINFOFUNCTION, DownloaderCURL::Impl::_progressCallbackProc);
- // curl_easy_setopt(handle, CURLOPT_XFERINFODATA, coTask);
- curl_easy_setopt(handle, CURLOPT_FAILONERROR, true);
- curl_easy_setopt(handle, CURLOPT_NOSIGNAL, 1L);
- if (forContent)
- {
- /** if server acceptRanges and local has part of file, we continue to download **/
- if (coTask->_acceptRanges && coTask->_totalBytesReceived > 0)
- {
- curl_easy_setopt(handle, CURLOPT_RESUME_FROM_LARGE,(curl_off_t)coTask->_totalBytesReceived);
- }
- }
- else
- {
- // get header options
- curl_easy_setopt(handle, CURLOPT_HEADER, 1);
- curl_easy_setopt(handle, CURLOPT_NOBODY, 1);
- }
- // if (!sProxy.empty())
- // {
- // curl_easy_setopt(curl, CURLOPT_PROXY, sProxy.c_str());
- // }
- if (hints.timeoutInSeconds)
- {
- curl_easy_setopt(handle, CURLOPT_CONNECTTIMEOUT, hints.timeoutInSeconds);
- }
- static const long LOW_SPEED_LIMIT = 1;
- static const long LOW_SPEED_TIME = 5;
- curl_easy_setopt(handle, CURLOPT_LOW_SPEED_LIMIT, LOW_SPEED_LIMIT);
- curl_easy_setopt(handle, CURLOPT_LOW_SPEED_TIME, LOW_SPEED_TIME);
- static const int MAX_REDIRS = 2;
- if (MAX_REDIRS)
- {
- curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, true);
- curl_easy_setopt(handle, CURLOPT_MAXREDIRS, MAX_REDIRS);
- }
- }
- // get header info, if success set handle to content download state
- bool _getHeaderInfoProc(CURL *handle, TaskWrapper& wrapper)
- {
- DownloadTaskCURL& coTask = *wrapper.second;
- CURLcode rc = CURLE_OK;
- do
- {
- long httpResponseCode = 0;
- rc = curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &httpResponseCode);
- if (CURLE_OK != rc)
- {
- break;
- }
- if (200 != httpResponseCode)
- {
- char buf[256] = {0};
- sprintf(buf
- , "When request url(%s) header info, return unexcept http response code(%ld)"
- , wrapper.first->requestURL.c_str()
- , httpResponseCode);
- coTask.setErrorProc(DownloadTask::ERROR_IMPL_INTERNAL, CURLE_OK, buf);
- }
- // curl_easy_getinfo(handle, CURLINFO_EFFECTIVE_URL, &effectiveUrl);
- // curl_easy_getinfo(handle, CURLINFO_CONTENT_TYPE, &contentType);
- double contentLen = 0;
- rc = curl_easy_getinfo(handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &contentLen);
- if (CURLE_OK != rc)
- {
- break;
- }
- bool acceptRanges = (string::npos != coTask._header.find("Accept-Ranges")) ? true : false;
- // get current file size
- int64_t fileSize = 0;
- if (acceptRanges && coTask._tempFileName.length())
- {
- fileSize = FileUtils::getInstance()->getFileSize(coTask._tempFileName);
- }
- // set header info to coTask
- lock_guard<mutex> lock(coTask._mutex);
- coTask._totalBytesExpected = (int64_t)contentLen;
- coTask._acceptRanges = acceptRanges;
- if (acceptRanges && fileSize > 0)
- {
- coTask._totalBytesReceived = fileSize;
- }
- coTask._headerAchieved = true;
- } while (0);
- if (CURLE_OK != rc)
- {
- coTask.setErrorProc(DownloadTask::ERROR_IMPL_INTERNAL, rc, curl_easy_strerror(rc));
- }
- return coTask._headerAchieved;
- }
- void _threadProc()
- {
- DLLOG("++++DownloaderCURL::Impl::_threadProc begin %p", this);
- // the holder prevent DownloaderCURL::Impl class instance be destruct in main thread
- auto holder = this->shared_from_this();
- auto thisThreadId = this_thread::get_id();
- uint32_t countOfMaxProcessingTasks = this->hints.countOfMaxProcessingTasks;
- // init curl content
- CURLM* curlmHandle = curl_multi_init();
- unordered_map<CURL*, TaskWrapper> coTaskMap;
- int runningHandles = 0;
- CURLMcode mcode = CURLM_OK;
- int rc = 0; // select return code
- do
- {
- // check the thread should exit or not
- {
- lock_guard<mutex> lock(_threadMutex);
- // if the Impl stoped, this->_thread.reset will be called, thus _thread.get_id() not equal with thisThreadId
- if (thisThreadId != this->_thread.get_id())
- {
- break;
- }
- }
- if (runningHandles)
- {
- // get timeout setting from multi-handle
- long timeoutMS = -1;
- curl_multi_timeout(curlmHandle, &timeoutMS);
- if(timeoutMS < 0)
- {
- timeoutMS = 1000;
- }
- /* get file descriptors from the transfers */
- fd_set fdread;
- fd_set fdwrite;
- fd_set fdexcep;
- int maxfd = -1;
- FD_ZERO(&fdread);
- FD_ZERO(&fdwrite);
- FD_ZERO(&fdexcep);
- mcode = curl_multi_fdset(curlmHandle, &fdread, &fdwrite, &fdexcep, &maxfd);
- if (CURLM_OK != mcode)
- {
- break;
- }
- // do wait action
- if(maxfd == -1)
- {
- this_thread::sleep_for(chrono::milliseconds(timeoutMS));
- rc = 0;
- }
- else
- {
- struct timeval timeout;
- timeout.tv_sec = timeoutMS / 1000;
- timeout.tv_usec = (timeoutMS % 1000) * 1000;
- rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);
- }
- if (rc < 0)
- {
- DLLOG(" _threadProc: select return unexpect code: %d", rc);
- }
- }
- if (coTaskMap.size())
- {
- mcode = CURLM_CALL_MULTI_PERFORM;
- while(CURLM_CALL_MULTI_PERFORM == mcode)
- {
- mcode = curl_multi_perform(curlmHandle, &runningHandles);
- }
- if (CURLM_OK != mcode)
- {
- break;
- }
- struct CURLMsg *m;
- do {
- int msgq = 0;
- m = curl_multi_info_read(curlmHandle, &msgq);
- if(m && (m->msg == CURLMSG_DONE))
- {
- CURL *curlHandle = m->easy_handle;
- CURLcode errCode = m->data.result;
- TaskWrapper wrapper = coTaskMap[curlHandle];
- // remove from multi-handle
- curl_multi_remove_handle(curlmHandle, curlHandle);
- bool reinited = false;
- do
- {
- if (CURLE_OK != errCode)
- {
- wrapper.second->setErrorProc(DownloadTask::ERROR_IMPL_INTERNAL, errCode, curl_easy_strerror(errCode));
- break;
- }
- // if the task is content download task, cleanup the handle
- if (wrapper.second->_headerAchieved)
- {
- break;
- }
- // the task is get header task
- // first, we get info from response
- if (false == _getHeaderInfoProc(curlHandle, wrapper))
- {
- // the error info has been set in _getHeaderInfoProc
- break;
- }
- // after get header info success
- // wrapper.second->_totalBytesReceived inited by local file size
- // if the local file size equal with the content size from header, the file has downloaded finish
- if (wrapper.second->_totalBytesReceived &&
- wrapper.second->_totalBytesReceived == wrapper.second->_totalBytesExpected)
- {
- // the file has download complete
- // break to move this task to finish queue
- break;
- }
- // reinit curl handle for download content
- curl_easy_reset(curlHandle);
- _initCurlHandleProc(curlHandle, wrapper, true);
- mcode = curl_multi_add_handle(curlmHandle, curlHandle);
- if (CURLM_OK != mcode)
- {
- wrapper.second->setErrorProc(DownloadTask::ERROR_IMPL_INTERNAL, mcode, curl_multi_strerror(mcode));
- break;
- }
- reinited = true;
- } while (0);
- if (reinited)
- {
- continue;
- }
- curl_easy_cleanup(curlHandle);
- DLLOG(" _threadProc task clean cur handle :%p with errCode:%d", curlHandle, errCode);
- // remove from coTaskMap
- coTaskMap.erase(curlHandle);
- // remove from _processSet
- {
- lock_guard<mutex> lock(_processMutex);
- if (_processSet.end() != _processSet.find(wrapper)) {
- _processSet.erase(wrapper);
- }
- }
- // add to finishedQueue
- {
- lock_guard<mutex> lock(_finishedMutex);
- _finishedQueue.push_back(wrapper);
- }
- }
- } while(m);
- }
- // process tasks in _requestList
- auto size = coTaskMap.size();
- while (0 == countOfMaxProcessingTasks || size < countOfMaxProcessingTasks)
- {
- // get task wrapper from request queue
- TaskWrapper wrapper;
- {
- lock_guard<mutex> lock(_requestMutex);
- if (_requestQueue.size())
- {
- wrapper = _requestQueue.front();
- _requestQueue.pop_front();
- }
- }
- // if request queue is empty, the wrapper.first is nullptr
- if (! wrapper.first)
- {
- break;
- }
- wrapper.second->initProc();
- // create curl handle from task and add into curl multi handle
- CURL* curlHandle = curl_easy_init();
- if (nullptr == curlHandle)
- {
- wrapper.second->setErrorProc(DownloadTask::ERROR_IMPL_INTERNAL, 0, "Alloc curl handle failed.");
- lock_guard<mutex> lock(_finishedMutex);
- _finishedQueue.push_back(wrapper);
- continue;
- }
- // init curl handle for get header info
- _initCurlHandleProc(curlHandle, wrapper);
- // add curl handle to process list
- mcode = curl_multi_add_handle(curlmHandle, curlHandle);
- if (CURLM_OK != mcode)
- {
- wrapper.second->setErrorProc(DownloadTask::ERROR_IMPL_INTERNAL, mcode, curl_multi_strerror(mcode));
- lock_guard<mutex> lock(_finishedMutex);
- _finishedQueue.push_back(wrapper);
- continue;
- }
- DLLOG(" _threadProc task create curl handle:%p", curlHandle);
- coTaskMap[curlHandle] = wrapper;
- lock_guard<mutex> lock(_processMutex);
- _processSet.insert(wrapper);
- }
- } while (coTaskMap.size());
- curl_multi_cleanup(curlmHandle);
- this->stop();
- DLLOG("----DownloaderCURL::Impl::_threadProc end");
- }
- thread _thread;
- deque<TaskWrapper> _requestQueue;
- set<TaskWrapper> _processSet;
- deque<TaskWrapper> _finishedQueue;
- mutex _threadMutex;
- mutex _requestMutex;
- mutex _processMutex;
- mutex _finishedMutex;
- };
- ////////////////////////////////////////////////////////////////////////////////
- // Implementation DownloaderCURL
- DownloaderCURL::DownloaderCURL(const DownloaderHints& hints)
- : _impl(std::make_shared<Impl>())
- , _currTask(nullptr)
- {
- DLLOG("Construct DownloaderCURL %p", this);
- _impl->hints = hints;
- _scheduler = Director::getInstance()->getScheduler();
- _scheduler->retain();
- _transferDataToBuffer = [this](void *buf, int64_t len)->int64_t
- {
- DownloadTaskCURL& coTask = *_currTask;
- int64_t dataLen = coTask._buf.size();
- if (len < dataLen)
- {
- return 0;
- }
- memcpy(buf, coTask._buf.data(), dataLen);
- coTask._buf.resize(0);
- return dataLen;
- };
- char key[128];
- sprintf(key, "DownloaderCURL(%p)", this);
- _schedulerKey = key;
- _scheduler->schedule(bind(&DownloaderCURL::_onSchedule, this, placeholders::_1),
- this,
- 0.1f,
- true,
- _schedulerKey);
- }
- DownloaderCURL::~DownloaderCURL()
- {
- _scheduler->unschedule(_schedulerKey, this);
- _scheduler->release();
- _impl->stop();
- DLLOG("Destruct DownloaderCURL %p", this);
- }
- IDownloadTask *DownloaderCURL::createCoTask(std::shared_ptr<const DownloadTask>& task)
- {
- DownloadTaskCURL *coTask = new (std::nothrow) DownloadTaskCURL;
- coTask->init(task->storagePath, _impl->hints.tempFileNameSuffix);
- DLLOG(" DownloaderCURL: createTask: Id(%d)", coTask->serialId);
- _impl->addTask(task, coTask);
- _impl->run();
- _scheduler->resumeTarget(this);
- return coTask;
- }
- void DownloaderCURL::_onSchedule(float)
- {
- vector<TaskWrapper> tasks;
- // update processing tasks
- _impl->getProcessTasks(tasks);
- for (auto& wrapper : tasks)
- {
- const DownloadTask& task = *wrapper.first;
- DownloadTaskCURL& coTask = *wrapper.second;
- lock_guard<mutex> lock(coTask._mutex);
- if (coTask._bytesReceived)
- {
- _currTask = &coTask;
- onTaskProgress(task,
- coTask._bytesReceived,
- coTask._totalBytesReceived,
- coTask._totalBytesExpected,
- _transferDataToBuffer);
- _currTask = nullptr;
- coTask._bytesReceived = 0;
- }
- }
- tasks.resize(0);
- // update finished tasks
- _impl->getFinishedTasks(tasks);
- if (_impl->stoped())
- {
- _scheduler->pauseTarget(this);
- }
- for (auto& wrapper : tasks)
- {
- const DownloadTask& task = *wrapper.first;
- DownloadTaskCURL& coTask = *wrapper.second;
- // if there is bytesReceived, call progress update first
- if (coTask._bytesReceived)
- {
- _currTask = &coTask;
- onTaskProgress(task,
- coTask._bytesReceived,
- coTask._totalBytesReceived,
- coTask._totalBytesExpected,
- _transferDataToBuffer);
- coTask._bytesReceived = 0;
- _currTask = nullptr;
- }
- // if file task, close file handle and rename file if needed
- if (coTask._fp)
- {
- fclose(coTask._fp);
- coTask._fp = nullptr;
- do
- {
- if (0 == coTask._fileName.length())
- {
- break;
- }
- auto util = FileUtils::getInstance();
- // if file already exist, remove it
- if (util->isFileExist(coTask._fileName))
- {
- if (false == util->removeFile(coTask._fileName))
- {
- coTask._errCode = DownloadTask::ERROR_FILE_OP_FAILED;
- coTask._errCodeInternal = 0;
- coTask._errDescription = "Can't remove old file: ";
- coTask._errDescription.append(coTask._fileName);
- break;
- }
- }
- // rename file
- if (util->renameFile(coTask._tempFileName, coTask._fileName))
- {
- // success, remove storage from set
- DownloadTaskCURL::_sStoragePathSet.erase(coTask._tempFileName);
- break;
- }
- // failed
- coTask._errCode = DownloadTask::ERROR_FILE_OP_FAILED;
- coTask._errCodeInternal = 0;
- coTask._errDescription = "Can't renamefile from: ";
- coTask._errDescription.append(coTask._tempFileName);
- coTask._errDescription.append(" to: ");
- coTask._errDescription.append(coTask._fileName);
- } while (0);
- }
- // needn't lock coTask here, because tasks has removed form _impl
- onTaskFinish(task, coTask._errCode, coTask._errCodeInternal, coTask._errDescription, coTask._buf);
- DLLOG(" DownloaderCURL: finish Task: Id(%d)", coTask.serialId);
- }
- }
- }} // namespace cocos2d::network
|