diff --git a/download/interfaces/kits/js/napi/download_single/include/download_config.h b/download/interfaces/kits/js/napi/download_single/include/download_config.h index debad3f..1730548 100644 --- a/download/interfaces/kits/js/napi/download_single/include/download_config.h +++ b/download/interfaces/kits/js/napi/download_single/include/download_config.h @@ -40,6 +40,10 @@ public: void SetTitle(const std::string &title); + void SetFD(int32_t fd); + + void SetFDError(int32_t fdError); + [[nodiscard]] const std::string &GetUrl() const; [[nodiscard]] const std::map &GetHeader() const; @@ -56,6 +60,10 @@ public: [[nodiscard]] const std::string &GetTitle() const; + int32_t GetFD() const; + + int32_t GetFDError() const; + void Dump(bool isFull = true) const; private: @@ -74,6 +82,10 @@ private: std::string filePath_; std::string title_; + + int32_t fd_; + + int32_t fdError_; }; } // namespace OHOS::Request::Download diff --git a/download/interfaces/kits/js/napi/download_single/src/download_config.cpp b/download/interfaces/kits/js/napi/download_single/src/download_config.cpp index 8fb91b9..b729697 100644 --- a/download/interfaces/kits/js/napi/download_single/src/download_config.cpp +++ b/download/interfaces/kits/js/napi/download_single/src/download_config.cpp @@ -20,7 +20,7 @@ namespace OHOS::Request::Download { DownloadConfig::DownloadConfig() : url_(""), enableMetered_(false), enableRoaming_(false), description_(""), networkType_(0), - filePath_(""), title_("") { + filePath_(""), title_(""), fd_(-1), fdError_(0) { } void DownloadConfig::SetUrl(const std::string &url) @@ -63,6 +63,16 @@ void DownloadConfig::SetTitle(const std::string &title) title_ = title; } +void DownloadConfig::SetFD(int32_t fd) +{ + fd_ = fd; +} + +void DownloadConfig::SetFDError(int32_t fdError) +{ + fdError_ = fdError; +} + const std::string &DownloadConfig::GetUrl() const { return url_; @@ -103,8 +113,20 @@ const std::string &DownloadConfig::GetTitle() const return title_; } +int32_t DownloadConfig::GetFD() const +{ + return fd_; +} + +int32_t DownloadConfig::GetFDError() const +{ + return fdError_; +} + void DownloadConfig::Dump(bool isFull) const { + DOWNLOAD_HILOGD("fd: %{public}d", fd_); + DOWNLOAD_HILOGD("fd errno: %{public}s", strerror(fdError_)); DOWNLOAD_HILOGD("URL: %{public}s", url_.c_str()); DOWNLOAD_HILOGD("enableMetered: %{public}s", enableMetered_ ? "true" : "false"); DOWNLOAD_HILOGD("enableRoaming: %{public}s", enableRoaming_ ? "true" : "false"); diff --git a/download/interfaces/kits/js/napi/download_single/src/download_service_proxy.cpp b/download/interfaces/kits/js/napi/download_single/src/download_service_proxy.cpp index c94df7d..7af1e4a 100644 --- a/download/interfaces/kits/js/napi/download_single/src/download_service_proxy.cpp +++ b/download/interfaces/kits/js/napi/download_single/src/download_service_proxy.cpp @@ -13,9 +13,12 @@ * limitations under the License. */ #include "download_service_proxy.h" +#include +#include +#include +#include #include "iremote_broker.h" - #include "log.h" #include "download_common.h" @@ -32,6 +35,26 @@ uint32_t DownloadServiceProxy::Request(const DownloadConfig &config) MessageParcel data, reply; MessageOption option; data.WriteInterfaceToken(GetDescriptor()); + + int32_t fd = -1; + int32_t err = 0; + fd = open(config.GetFilePath().c_str(), O_RDWR); + if (fd > 0) { + DOWNLOAD_HILOGD("File [%{public}s] already exists", config.GetFilePath().c_str()); + fd = -1; + } else { + fd = open(config.GetFilePath().c_str(), O_CREAT | O_RDWR, 0644); + if (fd < 0) { + DOWNLOAD_HILOGD("Failed to open file [%{public}s], errno [%{public}s]", + config.GetFilePath().c_str(), strerror(errno)); + err = errno; + } + } + + DOWNLOAD_HILOGI("Succeed to open file [%{public}s, fd [%{public}d]]", config.GetFilePath().c_str(), fd); + data.WriteFileDescriptor(fd); + data.WriteInt32(err); + close(fd); data.WriteString(config.GetUrl()); data.WriteBool(config.GetMetered()); data.WriteBool(config.GetRoaming()); diff --git a/download/interfaces/kits/js/napi/download_single/src/download_task_napi.cpp b/download/interfaces/kits/js/napi/download_single/src/download_task_napi.cpp index 124373b..79d75d9 100644 --- a/download/interfaces/kits/js/napi/download_single/src/download_task_napi.cpp +++ b/download/interfaces/kits/js/napi/download_single/src/download_task_napi.cpp @@ -156,7 +156,8 @@ bool DownloadTaskNapi::ParseConfig(napi_env env, napi_value configValue, Downloa bool DownloadTaskNapi::ParseHeader(napi_env env, napi_value configValue, DownloadConfig &config) { if (!NapiUtils::HasNamedProperty(env, configValue, PARAM_KEY_HEADER)) { - return false; + DOWNLOAD_HILOGD("No header present, ignore it"); + return true; } napi_value header = NapiUtils::GetNamedProperty(env, configValue, PARAM_KEY_HEADER); if (NapiUtils::GetValueType(env, header) != napi_object) { @@ -164,6 +165,7 @@ bool DownloadTaskNapi::ParseHeader(napi_env env, napi_value configValue, Downloa } auto names = NapiUtils::GetPropertyNames(env, header); std::vector::iterator iter; + DOWNLOAD_HILOGD("current name list size = %{public}d", names.size()); for (iter = names.begin(); iter != names.end(); ++iter) { auto value = NapiUtils::GetStringPropertyUtf8(env, header, *iter); diff --git a/download/services/include/download_service_manager.h b/download/services/include/download_service_manager.h index e7132bd..538da1a 100644 --- a/download/services/include/download_service_manager.h +++ b/download/services/include/download_service_manager.h @@ -56,7 +56,6 @@ private: NONE_QUEUE, PENDING_QUEUE, PAUSED_QUEUE, - COMPLETED_QUEUE, }; uint32_t GetCurrentTaskId(); @@ -65,12 +64,15 @@ private: void PushQueue(std::queue &queue, uint32_t taskId); void RemoveFromQueue(std::queue &queue, uint32_t taskId); + bool GetNetworkStatus(); + void ResumeTaskByNetwork(); + static void MonitorNetwork(DownloadServiceManager *thisVal); + private: bool initialized_; std::recursive_mutex mutex_; std::map> taskMap_; std::queue pendingQueue_; - std::queue completedQueue_; std::queue pausedQueue_; std::vector> threadList_; @@ -79,6 +81,8 @@ private: uint32_t threadNum_; uint32_t timeoutRetry_; + std::shared_ptr networkThread_; + static uint32_t taskId; static std::recursive_mutex instanceLock_; static std::shared_ptr instance_; diff --git a/download/services/include/download_service_stub.h b/download/services/include/download_service_stub.h index 894b1d3..7bdd78f 100644 --- a/download/services/include/download_service_stub.h +++ b/download/services/include/download_service_stub.h @@ -26,9 +26,9 @@ public: int32_t OnRemoteRequest(uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option) override; private: - bool OnRequest(Parcel &data, Parcel &reply); - bool OnPause(Parcel &data, Parcel &reply); - bool OnQuery(Parcel &data, Parcel &reply); + bool OnRequest(MessageParcel &data, MessageParcel &reply); + bool OnPause(MessageParcel &data, MessageParcel &reply); + bool OnQuery(MessageParcel &data, MessageParcel &reply); bool OnQueryMimeType(MessageParcel &data, MessageParcel &reply); bool OnRemove(MessageParcel &data, MessageParcel &reply); bool OnResume(MessageParcel &data, MessageParcel &reply); diff --git a/download/services/include/download_service_task.h b/download/services/include/download_service_task.h index 062decf..eb421e9 100644 --- a/download/services/include/download_service_task.h +++ b/download/services/include/download_service_task.h @@ -45,6 +45,7 @@ public: void GetRunResult(DownloadStatus &status, ErrorCode &code, PausedReason &reason); void SetRetryTime(uint32_t retryTime); + void SetNetworkStatus(bool isOnline); private: void SetStatus(DownloadStatus status, ErrorCode code, PausedReason reason); @@ -57,6 +58,7 @@ private: void DumpPausedReason(); bool ExecHttp(); + bool SetFileSizeOption(CURL *curl, struct curl_slist *requestHeader); bool SetOption(CURL *curl, struct curl_slist *requestHeader); struct curl_slist *MakeHeaders(const std::vector &vec); @@ -73,6 +75,7 @@ private: bool CheckResumeCondition(); void ForceStopRunning(); + bool HandleFileError(); private: uint32_t taskId_; @@ -93,6 +96,9 @@ private: DownloadTaskCallback eventCb_; std::recursive_mutex mutex_; + bool hasFileSize_; + bool isOnline_; + uint32_t prevSize_; }; } // namespace OHOS::Request::Download #endif // DOWNLOAD_TASK_H diff --git a/download/services/src/download_service_manager.cpp b/download/services/src/download_service_manager.cpp index 3cdc1b4..119a3b6 100644 --- a/download/services/src/download_service_manager.cpp +++ b/download/services/src/download_service_manager.cpp @@ -20,7 +20,7 @@ static constexpr uint32_t THREAD_POOL_NUM = 4; static constexpr uint32_t TASK_SLEEP_INTERVAL = 1; -static constexpr uint32_t MAX_RETRY_TIMES = 10; +static constexpr uint32_t MAX_RETRY_TIMES = 3; namespace OHOS::Request::Download { uint32_t DownloadServiceManager::taskId = 1000; @@ -28,7 +28,8 @@ std::recursive_mutex DownloadServiceManager::instanceLock_; std::shared_ptr DownloadServiceManager::instance_ = nullptr; DownloadServiceManager::DownloadServiceManager() - : initialized_(false), interval_(TASK_SLEEP_INTERVAL), threadNum_(THREAD_POOL_NUM), timeoutRetry_(MAX_RETRY_TIMES) + : initialized_(false), interval_(TASK_SLEEP_INTERVAL), threadNum_(THREAD_POOL_NUM), timeoutRetry_(MAX_RETRY_TIMES), + networkThread_(nullptr) { } @@ -66,14 +67,18 @@ bool DownloadServiceManager::Create(uint32_t threadNum) DOWNLOAD_HILOGD("Failed to initialize 'curl'"); return false; } + networkThread_ = std::make_shared(MonitorNetwork, this); + initialized_ = true; - return initialized_; + return initialized_; } void DownloadServiceManager::Destroy() { - std::for_each(threadList_.begin(), threadList_.end(), [](std::shared_ptr t) { t->Stop(); }); + std::for_each(threadList_.begin(), threadList_.end(), [](auto t) { t->Stop(); }); threadList_.clear(); + initialized_ = false; + networkThread_->join(); } uint32_t DownloadServiceManager::AddTask(const DownloadConfig& config) @@ -121,7 +126,9 @@ bool DownloadServiceManager::ProcessTask() if (pendingQueue_.size() > 0) { taskId = pendingQueue_.front(); pendingQueue_.pop(); - return taskMap_[taskId]; + if (taskMap_.find(taskId) != taskMap_.end()) { + return taskMap_[taskId]; + } } return nullptr; }; @@ -142,10 +149,12 @@ bool DownloadServiceManager::Pause(uint32_t taskId) if (!initialized_) { return false; } + DOWNLOAD_HILOGD("Pause Task[%{public}d]", taskId); auto it = taskMap_.find(taskId); if (it == taskMap_.end()) { return false; } + if (it->second->Pause()) { MoveTaskToQueue(taskId, it->second); return true; @@ -158,10 +167,12 @@ bool DownloadServiceManager::Resume(uint32_t taskId) if (!initialized_) { return false; } + DOWNLOAD_HILOGD("Resume Task[%{public}d]", taskId); auto it = taskMap_.find(taskId); if (it == taskMap_.end()) { return false; } + if (it->second->Resume()) { MoveTaskToQueue(taskId, it->second); return true; @@ -174,17 +185,18 @@ bool DownloadServiceManager::Remove(uint32_t taskId) if (!initialized_) { return false; } + DOWNLOAD_HILOGD("Remove Task[%{public}d]", taskId); auto it = taskMap_.find(taskId); if (it == taskMap_.end()) { return false; } + bool result = it->second->Remove(); if (result) { std::lock_guard autoLock(mutex_); - RemoveFromQueue(pendingQueue_, taskId); - RemoveFromQueue(pausedQueue_, taskId); - RemoveFromQueue(completedQueue_, taskId); taskMap_.erase(it); + RemoveFromQueue(pendingQueue_, taskId); + RemoveFromQueue(pausedQueue_, taskId); } return result; } @@ -222,24 +234,20 @@ uint32_t DownloadServiceManager::GetCurrentTaskId() DownloadServiceManager::QueueType DownloadServiceManager::DecideQueueType(DownloadStatus status) { switch (status) { - case SESSION_PENDING: - case SESSION_RUNNING: - return QueueType::NONE_QUEUE; - - case SESSION_SUCCESS: - return QueueType::COMPLETED_QUEUE; - case SESSION_PAUSED: return QueueType::PAUSED_QUEUE; - case SESSION_FAILED: case SESSION_UNKNOWN: return QueueType::PENDING_QUEUE; - + + case SESSION_PENDING: + case SESSION_RUNNING: + case SESSION_SUCCESS: + case SESSION_FAILED: default: - return QueueType::PENDING_QUEUE; + return QueueType::NONE_QUEUE; } - return QueueType::PENDING_QUEUE; + return QueueType::NONE_QUEUE; } void DownloadServiceManager::MoveTaskToQueue(uint32_t taskId, std::shared_ptr task) @@ -248,28 +256,20 @@ void DownloadServiceManager::MoveTaskToQueue(uint32_t taskId, std::shared_ptrGetRunResult(status, code, reason); + DOWNLOAD_HILOGD("Status [%{public}d], Code [%{public}d], Reason [%{public}d]", status, code, reason); switch (DecideQueueType(status)) { case QueueType::PENDING_QUEUE: { std::lock_guard autoLock(mutex_); RemoveFromQueue(pausedQueue_, taskId); - RemoveFromQueue(completedQueue_, taskId); PushQueue(pendingQueue_, taskId); break; } case QueueType::PAUSED_QUEUE: { std::lock_guard autoLock(mutex_); RemoveFromQueue(pendingQueue_, taskId); - RemoveFromQueue(completedQueue_, taskId); PushQueue(pausedQueue_, taskId); break; - } - case QueueType::COMPLETED_QUEUE: { - std::lock_guard autoLock(mutex_); - RemoveFromQueue(pendingQueue_, taskId); - RemoveFromQueue(pausedQueue_, taskId); - PushQueue(completedQueue_, taskId); - break; - } + } case QueueType::NONE_QUEUE: default: break; @@ -279,6 +279,10 @@ void DownloadServiceManager::MoveTaskToQueue(uint32_t taskId, std::shared_ptr &queue, uint32_t taskId) { std::lock_guard autoLock(mutex_); + if (taskMap_.find(taskId) == taskMap_.end()) { + DOWNLOAD_HILOGD("invalid task id [%{public}d]", taskId); + return; + } bool foundIt = false; if (queue.size() > 0) { uint32_t indicatorId = queue.front(); @@ -318,4 +322,74 @@ uint32_t DownloadServiceManager::GetInterval() const { return interval_; } + +bool DownloadServiceManager::GetNetworkStatus() +{ + bool isOnline = false; + std::unique_ptr handle(curl_easy_init(), curl_easy_cleanup); + + if (!handle) { + DOWNLOAD_HILOGD("Failed to create network monitor task"); + return false; + } + + std::string example = "www.example.com"; + curl_easy_setopt(handle.get(), CURLOPT_URL, example.c_str()); + CURLcode code = curl_easy_perform(handle.get()); + if (code == CURLE_OK) { + DOWNLOAD_HILOGD("Network status is online"); + isOnline = true; + } + return isOnline; +} + +void DownloadServiceManager::ResumeTaskByNetwork() +{ + int taskCount = 0; + std::lock_guard autoLock(mutex_); + if (pausedQueue_.size() > 0) { + size_t size = pausedQueue_.size(); + while (size-- > 0) { + uint32_t taskId = pausedQueue_.front(); + if (taskMap_.find(taskId) != taskMap_.end()) { + pausedQueue_.pop(); + + auto task = taskMap_[taskId]; + DownloadStatus status; + ErrorCode code; + PausedReason reason; + task->GetRunResult(status, code, reason); + + if (reason != PAUSED_BY_USER) { + task->Resume(); + PushQueue(pendingQueue_, taskId); + taskCount++; + } else { + pausedQueue_.push(taskId); + } + } + } + } + DOWNLOAD_HILOGD("[%{public}d] task has been resumed by network status changed", taskCount); +} + +void DownloadServiceManager::MonitorNetwork(DownloadServiceManager *thisVal) +{ + bool isOnline = true; + bool currentStatus = true; + + while (thisVal->initialized_) { + currentStatus = thisVal->GetNetworkStatus(); + if (!isOnline && currentStatus) { + // offline --> online + if (thisVal) { + thisVal->ResumeTaskByNetwork(); + } + } + isOnline = currentStatus; + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + std::this_thread::yield(); + } +} + } // namespace OHOS::Request::Download \ No newline at end of file diff --git a/download/services/src/download_service_stub.cpp b/download/services/src/download_service_stub.cpp index 3d19190..b173485 100644 --- a/download/services/src/download_service_stub.cpp +++ b/download/services/src/download_service_stub.cpp @@ -15,7 +15,7 @@ #include "download_service_stub.h" #include "ipc_skeleton.h" -#include "parcel.h" +#include "message_parcel.h" #include "download_common.h" #include "download_service_interface.h" #include "log.h" @@ -62,10 +62,14 @@ int32_t DownloadServiceStub::OnRemoteRequest( return E_DOWNLOAD_OK; } -bool DownloadServiceStub::OnRequest(Parcel &data, Parcel &reply) +bool DownloadServiceStub::OnRequest(MessageParcel &data, MessageParcel &reply) { DOWNLOAD_HILOGD("Receive request"); DownloadConfig config; + int32_t fd = data.ReadFileDescriptor(); + DOWNLOAD_HILOGI("Get FD from client, fd [%{public}d]", fd); + config.SetFD(fd); + config.SetFDError(data.ReadInt32()); config.SetUrl(data.ReadString()); config.SetMetered(data.ReadBool()); config.SetRoaming(data.ReadBool()); @@ -88,7 +92,7 @@ bool DownloadServiceStub::OnRequest(Parcel &data, Parcel &reply) return true; } -bool DownloadServiceStub::OnPause(Parcel &data, Parcel &reply) +bool DownloadServiceStub::OnPause(MessageParcel &data, MessageParcel &reply) { bool result = Pause(data.ReadUint32()); if (!reply.WriteBool(result)) { @@ -98,7 +102,7 @@ bool DownloadServiceStub::OnPause(Parcel &data, Parcel &reply) return true; } -bool DownloadServiceStub::OnQuery(Parcel &data, Parcel &reply) +bool DownloadServiceStub::OnQuery(MessageParcel &data, MessageParcel &reply) { DownloadInfo info; bool result = Query(data.ReadUint32(), info); diff --git a/download/services/src/download_service_task.cpp b/download/services/src/download_service_task.cpp index bf1071e..5f26094 100644 --- a/download/services/src/download_service_task.cpp +++ b/download/services/src/download_service_task.cpp @@ -16,6 +16,9 @@ #include "download_service_task.h" #include +#include +#include +#include #include "constant.h" #include "log.h" @@ -23,7 +26,7 @@ namespace OHOS::Request::Download { DownloadServiceTask::DownloadServiceTask(uint32_t taskId, const DownloadConfig &config) : taskId_(taskId), config_(config), status_(SESSION_UNKNOWN), code_(ERROR_UNKNOWN), reason_(PAUSED_UNKNOWN), mimeType_(""), file_(nullptr), totalSize_(0), downloadSize_(0), isPartialMode_(false), forceStop_(false), - isRemoved_(false), retryTime_(10), eventCb_(nullptr) { + isRemoved_(false), retryTime_(10), eventCb_(nullptr), hasFileSize_(false), isOnline_(true), prevSize_(0) { } DownloadServiceTask::~DownloadServiceTask(void) @@ -33,6 +36,10 @@ DownloadServiceTask::~DownloadServiceTask(void) fflush(file_); fclose(file_); } + if (config_.GetFD() > 0) { + close(config_.GetFD()); + config_.SetFD(-1); + } } uint32_t DownloadServiceTask::GetId() const @@ -43,59 +50,70 @@ uint32_t DownloadServiceTask::GetId() const bool DownloadServiceTask::Run() { DOWNLOAD_HILOGD("Task[%{public}d] start.", taskId_); - std::lock_guard autoLock(mutex_); - if (!GetFileSize(totalSize_)) { - SetStatus(SESSION_PENDING, ERROR_UNKNOWN, reason_); + if (HandleFileError()) { return false; } + uint32_t retryTime = 0; - bool result = true; - bool enableTimeout; + bool result = false; + bool enableTimeout = false; + SetStatus(SESSION_RUNNING); + do { enableTimeout = false; - SetStatus(SESSION_RUNNING); - result = ExecHttp(); + if (status_ != SESSION_RUNNING && status_ != SESSION_PENDING) { + break; + } + if (GetFileSize(totalSize_)) { + result = ExecHttp(); + } DumpStatus(); DumpErrorCode(); DumpPausedReason(); + // HTTP timeout occurs, retry if (status_ == SESSION_PENDING) { enableTimeout = true; retryTime++; } } while (!result && enableTimeout && retryTime < retryTime_); + if (retryTime >= retryTime_) { + SetStatus(SESSION_PAUSED, ERROR_UNKNOWN, PAUSED_WAITING_TO_RETRY); + } return result; } bool DownloadServiceTask::Pause() { - DOWNLOAD_HILOGD("Pause Task[%{public}d], current status is %{public}d\n", taskId_, status_); + DOWNLOAD_HILOGD("Status [%{public}d], Code [%{public}d], Reason [%{public}d]", status_, code_, reason_); if (status_ != SESSION_RUNNING && status_ != SESSION_PENDING) { return false; } ForceStopRunning(); - SetStatus(SESSION_PAUSED); + SetStatus(SESSION_PAUSED, ERROR_UNKNOWN, PAUSED_BY_USER); return true; } bool DownloadServiceTask::Resume() { - DOWNLOAD_HILOGD("Resume Task[%{public}d], current status is %{public}d\n", taskId_, status_); - if (status_ != SESSION_PAUSED) { - return false; - } - ForceStopRunning(); - if (!CheckResumeCondition()) { - SetStatus(SESSION_FAILED, ERROR_CANNOT_RESUME, reason_); + DOWNLOAD_HILOGD("Status [%{public}d], Code [%{public}d], Reason [%{public}d]", status_, code_, reason_); + if (status_ == SESSION_PAUSED || (status_ == SESSION_FAILED && code_ == ERROR_CANNOT_RESUME)) { + forceStop_ = false; + if (!CheckResumeCondition()) { + SetStatus(SESSION_FAILED, ERROR_CANNOT_RESUME, PAUSED_UNKNOWN); + } else { + // reset status + SetStatus(SESSION_UNKNOWN, ERROR_UNKNOWN, PAUSED_UNKNOWN); + } return true; } - SetStatus(SESSION_UNKNOWN, code_, reason_); - return true; + return false; } + bool DownloadServiceTask::Remove() { - DOWNLOAD_HILOGD("Remove Task[%{public}d], current status is %{public}d\n", taskId_, status_); + DOWNLOAD_HILOGD("Status [%{public}d], Code [%{public}d], Reason [%{public}d]", status_, code_, reason_); isRemoved_ = true; ForceStopRunning(); if (eventCb_ != nullptr) { @@ -147,11 +165,19 @@ void DownloadServiceTask::SetRetryTime(uint32_t retryTime) retryTime_ = retryTime; } +void DownloadServiceTask::SetNetworkStatus(bool isOnline) +{ + std::lock_guard autoLock(mutex_); + isOnline_ = isOnline; + if (status_ == SESSION_PAUSED && reason_ == PAUSED_WAITING_TO_RETRY && !isOnline_) { + reason_ = PAUSED_WAITING_FOR_NETWORK; + } +} + void DownloadServiceTask::SetStatus(DownloadStatus status, ErrorCode code, PausedReason reason) { auto stateChange = [this](DownloadStatus status, ErrorCode code, PausedReason reason) -> bool { std::lock_guard autoLock(mutex_); - this->forceStop_ = false; bool isChanged = false; if (status != this->status_) { this->status_ = status; @@ -161,16 +187,24 @@ void DownloadServiceTask::SetStatus(DownloadStatus status, ErrorCode code, Pause this->code_ = code; isChanged = true; } - if (reason != this->reason_) { - this->reason_ = reason; - isChanged = true; + if (this->reason_ != PAUSED_BY_USER) { + if (!isOnline_ && reason == PAUSED_WAITING_TO_RETRY) { + reason = PAUSED_WAITING_FOR_NETWORK; + } + if (reason != this->reason_) { + this->reason_ = reason; + isChanged = true; + } } + return true; }; + DOWNLOAD_HILOGD("Status [%{public}d], Code [%{public}d], Reason [%{public}d]", status, code, reason); if (!stateChange(status, code, reason)) { return; } if (eventCb_ != nullptr) { + std::lock_guard autoLock(mutex_); switch (status_) { case SESSION_SUCCESS: eventCb_("complete", taskId_, 0, 0); @@ -194,7 +228,6 @@ void DownloadServiceTask::SetStatus(DownloadStatus status) { auto stateChange = [this](DownloadStatus status) -> bool { std::lock_guard autoLock(mutex_); - this->forceStop_ = false; if (status == this->status_) { DOWNLOAD_HILOGD("ignore same status"); return false; @@ -202,10 +235,12 @@ void DownloadServiceTask::SetStatus(DownloadStatus status) this->status_ = status; return true; }; + DOWNLOAD_HILOGD("Status [%{public}d]", status); if (!stateChange(status)) { return; } if (eventCb_ != nullptr) { + std::lock_guard autoLock(mutex_); switch (status_) { case SESSION_SUCCESS: eventCb_("complete", taskId_, 0, 0); @@ -227,8 +262,8 @@ void DownloadServiceTask::SetStatus(DownloadStatus status) void DownloadServiceTask::SetError(ErrorCode code) { + DOWNLOAD_HILOGD("Code [%{public}d]", code); std::lock_guard autoLock(mutex_); - this->forceStop_ = false; if (code == code_) { DOWNLOAD_HILOGD("ignore same error code"); return; @@ -238,13 +273,19 @@ void DownloadServiceTask::SetError(ErrorCode code) void DownloadServiceTask::SetReason(PausedReason reason) { + DOWNLOAD_HILOGD("Reason [%{public}d]", reason); std::lock_guard autoLock(mutex_); - this->forceStop_ = false; - if (reason == reason_) { - DOWNLOAD_HILOGD("ignore same paused reason"); - return; - } - reason_ = reason; + + if (reason_ != PAUSED_BY_USER) { + if (!isOnline_ && reason == PAUSED_WAITING_TO_RETRY) { + reason = PAUSED_WAITING_FOR_NETWORK; + } + if (reason == reason_) { + DOWNLOAD_HILOGD("ignore same paused reason"); + return; + } + reason_ = reason; + } } void DownloadServiceTask::DumpStatus() @@ -358,8 +399,11 @@ size_t DownloadServiceTask::WriteCallback(void *buffer, size_t size, size_t num, { size_t result = 0; DownloadServiceTask *this_ = static_cast(param); - if (this_ != nullptr && this_->file_) { - result = fwrite(buffer, size, num, this_->file_); + if (this_ != nullptr && this_->config_.GetFD() > 0) { + result = write(this_->config_.GetFD(), buffer, size * num); + if (result < size * num) { + DOWNLOAD_HILOGE("origin size = %{pulic}d, write size = %{pulic}d", size * num, result); + } this_->downloadSize_ += static_cast(result); } return result; @@ -384,12 +428,18 @@ int DownloadServiceTask::ProgressCallback(void *pParam, double dltotal, double d if (this_->isRemoved_) { DOWNLOAD_HILOGD("download task has been removed\n"); } - if (this_->eventCb_ != nullptr && !this_->isRemoved_) { - this_->eventCb_("progress", this_->taskId_, this_->downloadSize_, this_->totalSize_); - } if (this_->forceStop_) { DOWNLOAD_HILOGD("Pause issued by user\n"); return HTTP_FORCE_STOP; + } + if (this_->eventCb_ != nullptr && !this_->isRemoved_) { + if (this_->prevSize_ != this_->downloadSize_) { + std::lock_guard autoLock(this_->mutex_); + if (this_->status_ != SESSION_PAUSED) { + this_->eventCb_("progress", this_->taskId_, this_->downloadSize_, this_->totalSize_); + this_->prevSize_ = this_->downloadSize_; + } + } } // calc the download speed } @@ -418,29 +468,29 @@ bool DownloadServiceTask::ExecHttp() DOWNLOAD_HILOGD("set option failed"); return false; } - std::string tmpFileName = GetTmpPath().c_str(); - file_ = fopen(tmpFileName.c_str(), "ab"); - if (file_ != nullptr) { - DOWNLOAD_HILOGD("Succeed to open %{public}s", tmpFileName.c_str()); - fseek(file_, 0, SEEK_END); - uint32_t pos = ftell(file_); + + if (config_.GetFD() > 0) { + DOWNLOAD_HILOGD("Succeed to open %{public}s", config_.GetFilePath().c_str()); + uint64_t pos = lseek(config_.GetFD(), 0, SEEK_END); + downloadSize_ = 0; if (pos > 0) { if (pos < totalSize_) { isPartialMode_ = true; + downloadSize_ = static_cast(pos); SetResumeFromLarge(handle.get(), pos); - } else if (pos == totalSize_) { + } else if (pos >= totalSize_) { downloadSize_ = totalSize_; DOWNLOAD_HILOGD("Download task has already completed"); SetStatus(SESSION_SUCCESS); return true; } else { DOWNLOAD_HILOGD("Download size exceed the file size, re-download it"); - fclose(file_); - file_ = fopen(tmpFileName.c_str(), "wb"); + return false; } } + prevSize_ = downloadSize_; } else { - DOWNLOAD_HILOGD("Failed to open %{public}s", tmpFileName.c_str()); + DOWNLOAD_HILOGD("Failed to open %{public}s", config_.GetFilePath().c_str()); } CURLcode code = curl_easy_perform(handle.get()); @@ -456,6 +506,51 @@ bool DownloadServiceTask::ExecHttp() return code == CURLE_OK; } +bool DownloadServiceTask::SetFileSizeOption(CURL *curl, struct curl_slist *requestHeader) +{ + curl_easy_setopt(curl, CURLOPT_URL, config_.GetUrl().c_str()); + + curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, HeaderCallback); + curl_easy_setopt(curl, CURLOPT_HEADERDATA, this); + + if (requestHeader != nullptr) { + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, requestHeader); + } + // Some servers don't like requests that are made without a user-agent field, so we provide one + curl_easy_setopt(curl, CURLOPT_USERAGENT, HTTP_DEFAULT_USER_AGENT); +#if 1 + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + + /* first #undef CURL_DISABLE_COOKIES in curl config */ + curl_easy_setopt(curl, CURLOPT_COOKIEFILE, ""); + +#ifdef DOWNLOAD_USE_PROXY + curl_easy_setopt(curl, CURLOPT_PROXY, HTTP_PROXY_URL_PORT); + curl_easy_setopt(curl, CURLOPT_PROXYTYPE, HTTP_PROXY_TYPE); + curl_easy_setopt(curl, CURLOPT_HTTPPROXYTUNNEL, 1L); +#ifdef DOWNLOAD_PROXY_PASS + curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, HTTP_PROXY_PASS); +#endif // DOWNLOAD_PROXY_PASS +#endif // DOWNLOAD_USE_PROXY + +#ifdef DOWNLOAD_SSL_CERTIFICATION + curl_easy_setopt(curl, CURLOPT_CAINFO, HTTP_DEFAULT_CA_PATH); +#else + // NO_SSL_CERTIFICATION + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); +#endif + + curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L); +#if HTTP_CURL_PRINT_VERBOSE + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L, context); +#endif + curl_easy_setopt(curl, CURLOPT_TIMEOUT, DEFAULT_READ_TIMEOUT); + curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, DEFAULT_CONNECT_TIMEOUT); +#endif + return true; +} + bool DownloadServiceTask::SetOption(CURL *curl, struct curl_slist *requestHeader) { curl_easy_setopt(curl, CURLOPT_URL, config_.GetUrl().c_str()); @@ -525,7 +620,7 @@ void DownloadServiceTask::SetResumeFromLarge(CURL *curl, long long pos) bool DownloadServiceTask::GetFileSize(uint32_t &result) { - if (totalSize_) { + if (hasFileSize_) { DOWNLOAD_HILOGD("Already get file size"); return true; } @@ -544,23 +639,30 @@ bool DownloadServiceTask::GetFileSize(uint32_t &result) }); std::unique_ptr header(MakeHeaders(vec), curl_slist_free_all); - if (!SetOption(handle.get(), header.get())) { + if (!SetFileSizeOption(handle.get(), header.get())) { DOWNLOAD_HILOGD("set option failed"); return false; } curl_easy_setopt(handle.get(), CURLOPT_NOBODY, 1L); - CURLcode res; - curl_easy_perform(handle.get()); - res = curl_easy_getinfo(handle.get(), CURLINFO_CONTENT_LENGTH_DOWNLOAD, &size); - if (res == CURLE_OK) { + CURLcode code = curl_easy_perform(handle.get()); + curl_easy_getinfo(handle.get(), CURLINFO_CONTENT_LENGTH_DOWNLOAD, &size); + + if (code == CURLE_OK) { result = static_cast(size); + if (result == static_cast(-1)) { + result = 0; + } + hasFileSize_ = true; + DOWNLOAD_HILOGD("Has got file size"); + } else { + if (status_ == SESSION_RUNNING || status_ == SESSION_PENDING) { + SetStatus(SESSION_PENDING, ERROR_UNKNOWN, PAUSED_UNKNOWN); + } } - if (result == -1) { - result = 0; - } + DOWNLOAD_HILOGD("fetch file size %{public}d", result); - return true; + return hasFileSize_; } std::string DownloadServiceTask::GetTmpPath() @@ -574,22 +676,41 @@ void DownloadServiceTask::HandleResponseCode(CURLcode code, int32_t httpCode) DOWNLOAD_HILOGD("download task has been removed"); return; } + DOWNLOAD_HILOGD("Current CURLcode is %{public}d, httpCode is %{public}d\n", code, httpCode); + if (status_ == SESSION_PAUSED && reason_ == PAUSED_BY_USER) { + DOWNLOAD_HILOGD("Pause By User:ignore status changed caused by libcurl"); + return; + } + switch (code) { case CURLE_OK: if (httpCode == HTTP_OK || (isPartialMode_ && httpCode == HTTP_PARIAL_FILE)) { SetStatus(SESSION_SUCCESS); return; } - + break; + case CURLE_ABORTED_BY_CALLBACK: if (httpCode == HTTP_OK || (isPartialMode_ && httpCode == HTTP_PARIAL_FILE)) { - SetStatus(SESSION_PAUSED, code_, PAUSED_BY_USER); + SetStatus(SESSION_PAUSED, ERROR_UNKNOWN, PAUSED_BY_USER); return; } + break; + + case CURLE_WRITE_ERROR: + if (httpCode == HTTP_OK || (isPartialMode_ && httpCode == HTTP_PARIAL_FILE)) { + SetStatus(SESSION_FAILED, ERROR_HTTP_DATA_ERROR, PAUSED_UNKNOWN); + return; + } + break; + case CURLE_TOO_MANY_REDIRECTS: - SetStatus(SESSION_FAILED, ERROR_TOO_MANY_REDIRECTS, reason_); + SetStatus(SESSION_FAILED, ERROR_TOO_MANY_REDIRECTS, PAUSED_UNKNOWN); return; + case CURLE_COULDNT_RESOLVE_PROXY: + case CURLE_COULDNT_RESOLVE_HOST: + case CURLE_COULDNT_CONNECT: case CURLE_OPERATION_TIMEDOUT: SetStatus(SESSION_PENDING); return; @@ -597,13 +718,14 @@ void DownloadServiceTask::HandleResponseCode(CURLcode code, int32_t httpCode) default: break; } - DOWNLOAD_HILOGD("Current CURLcode is %{public}d, httpCode is %{public}d\n", code, httpCode); - SetStatus(SESSION_FAILED, ERROR_UNHANDLED_HTTP_CODE, reason_); + SetStatus(SESSION_FAILED, ERROR_UNHANDLED_HTTP_CODE, PAUSED_UNKNOWN); } bool DownloadServiceTask::CheckResumeCondition() { - // current paused issued by user + if (!isOnline_) { + return false; + } return true; } @@ -611,12 +733,16 @@ void DownloadServiceTask::ForceStopRunning() { forceStop_ = true; } + void DownloadServiceTask::HandleCleanup(DownloadStatus status) { switch (status) { case SESSION_SUCCESS: // rename download to target file name - rename(GetTmpPath().c_str(), config_.GetFilePath().c_str()); + if (config_.GetFD() > 0) { + close(config_.GetFD()); + config_.SetFD(-1); + } break; case SESSION_FAILED: @@ -626,4 +752,30 @@ void DownloadServiceTask::HandleCleanup(DownloadStatus status) break; } } + +bool DownloadServiceTask::HandleFileError() +{ + + ErrorCode code = ERROR_UNKNOWN; + + if (config_.GetFD() < 0) { + switch (config_.GetFDError()) { + case 0: + DOWNLOAD_HILOGD("File [%{public}s] already exists", config_.GetFilePath().c_str()); + code = ERROR_FILE_ALREADY_EXISTS; + break; + + case ENODEV: + code = ERROR_DEVICE_NOT_FOUND; + break; + + default: + code = ERROR_FILE_ERROR; + break; + } + SetStatus(SESSION_FAILED, code, PAUSED_UNKNOWN); + return true; + } + return false; +} } // namespace OHOS::Request::Download diff --git a/upload/interfaces/kits/napi/src/request_module.cpp b/upload/interfaces/kits/napi/src/request_module.cpp index 6823224..2e41c05 100644 --- a/upload/interfaces/kits/napi/src/request_module.cpp +++ b/upload/interfaces/kits/napi/src/request_module.cpp @@ -40,6 +40,7 @@ static napi_value paused_queue_wifi = nullptr; static napi_value paused_for_network = nullptr; static napi_value paused_to_retry = nullptr; static napi_value paused_by_user = nullptr; +static napi_value paused_unknown = nullptr; static napi_value session_success = nullptr; static napi_value session_running = nullptr; static napi_value session_pending = nullptr; @@ -68,6 +69,7 @@ static napi_value Init(napi_env env, napi_value exports) napi_create_int32(env, static_cast(PAUSED_WAITING_FOR_NETWORK), &paused_for_network); napi_create_int32(env, static_cast(PAUSED_WAITING_TO_RETRY), &paused_to_retry); napi_create_int32(env, static_cast(PAUSED_BY_USER), &paused_by_user); + napi_create_int32(env, static_cast(PAUSED_UNKNOWN), &paused_unknown); /* Create session status Const */ napi_create_int32(env, static_cast(SESSION_SUCCESS), &session_success); @@ -94,8 +96,9 @@ static napi_value Init(napi_env env, napi_value exports) DECLARE_NAPI_STATIC_PROPERTY("PAUSED_WAITING_FOR_NETWORK", paused_for_network), DECLARE_NAPI_STATIC_PROPERTY("PAUSED_WAITING_TO_RETRY", paused_to_retry), DECLARE_NAPI_STATIC_PROPERTY("PAUSED_BY_USER", paused_by_user), + DECLARE_NAPI_STATIC_PROPERTY("PAUSED_UNKNOWN", paused_unknown), - DECLARE_NAPI_STATIC_PROPERTY("SESSION_SUCCESS", session_success), + DECLARE_NAPI_STATIC_PROPERTY("SESSION_SUCCESSFUL", session_success), DECLARE_NAPI_STATIC_PROPERTY("SESSION_RUNNING", session_running), DECLARE_NAPI_STATIC_PROPERTY("SESSION_PENDING", session_pending), DECLARE_NAPI_STATIC_PROPERTY("SESSION_PAUSED", session_paused),