• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "legacy/download_task.h"
17 
18 #include <pthread.h>
19 
20 #include "constant.h"
21 #include "log.h"
22 
23 namespace OHOS::Request::Legacy {
24 bool DownloadTask::isCurlGlobalInited_ = false;
25 const uint32_t DEFAULT_READ_TIMEOUT = 60;
26 const uint32_t DEFAULT_LOW_SPEED_LIMIT = 30;
27 constexpr uint32_t RETRY_TIME = 10;
DownloadTask(const std::string & token,const DownloadOption & option,const DoneFunc & callback)28 DownloadTask::DownloadTask(const std::string &token, const DownloadOption &option, const DoneFunc &callback)
29     : taskId_(token), option_(option), callback_(callback), totalSize_(0), hasFileSize_(false)
30 {
31     REQUEST_HILOGI("constructor");
32 }
33 
~DownloadTask()34 DownloadTask::~DownloadTask()
35 {
36     REQUEST_HILOGI("destroy");
37     if (filp_ != nullptr) {
38         fclose(filp_);
39     }
40     delete[] errorBuffer_;
41     delete thread_;
42 }
43 
OpenDownloadFile() const44 FILE *DownloadTask::OpenDownloadFile() const
45 {
46     auto downloadFile = option_.fileDir_ + '/' + option_.filename_;
47     FILE *filp = fopen(downloadFile.c_str(), "w+");
48     if (filp == nullptr) {
49         REQUEST_HILOGE("open download file failed");
50     }
51     return filp;
52 }
53 
GetLocalFileSize()54 uint32_t DownloadTask::GetLocalFileSize()
55 {
56     if (filp_ == nullptr) {
57         filp_ = OpenDownloadFile();
58         if (filp_ == nullptr) {
59             return 0;
60         }
61     }
62 
63     int nRet = fseek(filp_, 0, SEEK_END);
64     if (nRet != 0) {
65         REQUEST_HILOGE("fseek error");
66         return 0;
67     }
68     long lRet = ftell(filp_);
69     if (lRet < 0) {
70         REQUEST_HILOGE("ftell error");
71         return 0;
72     }
73     return static_cast<uint32_t>(lRet);
74 }
NotifyDone(bool successful,const std::string & errMsg)75 void DownloadTask::NotifyDone(bool successful, const std::string &errMsg)
76 {
77     if (filp_ != nullptr) {
78         fclose(filp_);
79         filp_ = nullptr;
80 
81         if (!successful) {
82             REQUEST_HILOGE("remove download file");
83             remove((option_.fileDir_ + '/' + option_.filename_).c_str());
84         }
85     }
86 
87     if (callback_) {
88         callback_(taskId_, successful, errMsg);
89     }
90 }
91 
GetFileSize(uint32_t & result)92 bool DownloadTask::GetFileSize(uint32_t &result)
93 {
94     if (hasFileSize_) {
95         REQUEST_HILOGD("Already get file size");
96         return true;
97     }
98     std::unique_ptr<CURL, decltype(&curl_easy_cleanup)> handle(curl_easy_init(), curl_easy_cleanup);
99 
100     if (!handle) {
101         REQUEST_HILOGD("Failed to create download service task");
102         return false;
103     }
104 
105     curl_easy_setopt(handle.get(), CURLOPT_URL, option_.url_.c_str());
106     curl_easy_setopt(handle.get(), CURLOPT_HEADER, 1L);
107     curl_easy_setopt(handle.get(), CURLOPT_NOBODY, 1L);
108     CURLcode code = curl_easy_perform(handle.get());
109     double size = 0.0;
110     curl_easy_getinfo(handle.get(), CURLINFO_CONTENT_LENGTH_DOWNLOAD, &size);
111 
112     if (code == CURLE_OK) {
113         if (size > UINT_MAX) {
114             REQUEST_HILOGD("file size overflow");
115             return false;
116         }
117         result = static_cast<uint32_t>(size);
118         if (result == static_cast<uint32_t>(-1)) {
119             result = 0;
120         }
121         hasFileSize_ = true;
122         REQUEST_HILOGD("Has got file size");
123     }
124     REQUEST_HILOGD("fetch file size %{public}d", result);
125     return hasFileSize_;
126 }
127 
SetOption(CURL * handle,curl_slist * & headers)128 bool DownloadTask::SetOption(CURL *handle, curl_slist *&headers)
129 {
130     filp_ = OpenDownloadFile();
131     if (filp_ == nullptr) {
132         return false;
133     }
134     curl_easy_setopt(handle, CURLOPT_WRITEDATA, filp_);
135 
136     errorBuffer_ = new (std::nothrow) char[CURL_ERROR_SIZE];
137     if (errorBuffer_ == nullptr) {
138         return false;
139     }
140     curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, errorBuffer_);
141 
142     curl_easy_setopt(handle, CURLOPT_URL, option_.url_.c_str());
143     curl_easy_setopt(handle, CURLOPT_SSL_VERIFYHOST, 0L);
144     curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0L);
145     curl_easy_setopt(handle, CURLOPT_LOW_SPEED_TIME, DEFAULT_READ_TIMEOUT);
146     curl_easy_setopt(handle, CURLOPT_LOW_SPEED_LIMIT, DEFAULT_LOW_SPEED_LIMIT);
147 
148     if (!option_.header_.empty()) {
149         for (const auto &head : option_.header_) {
150             headers = curl_slist_append(headers, head.c_str());
151         }
152         curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
153     }
154     return true;
155 }
156 
Start()157 void DownloadTask::Start()
158 {
159     if (!isCurlGlobalInited_) {
160         curl_global_init(CURL_GLOBAL_ALL);
161         isCurlGlobalInited_ = true;
162     }
163 
164     thread_ = new (std::nothrow) std::thread(&DownloadTask::Run, this);
165     if (thread_ == nullptr) {
166         NotifyDone(false, "create download thread failed");
167         return;
168     }
169     thread_->detach();
170 }
171 
Run()172 void DownloadTask::Run()
173 {
174     REQUEST_HILOGD("start download task");
175     pthread_setname_np(pthread_self(), "system_download");
176     uint32_t retryTime = 0;
177     bool result = false;
178     do {
179         if (GetFileSize(totalSize_)) {
180             result = DoDownload();
181         }
182         retryTime++;
183         REQUEST_HILOGD("download task retrytime: %{public}u, totalSize_: %{public}u", retryTime, totalSize_);
184     } while (!result && retryTime < RETRY_TIME);
185 
186     if (retryTime >= RETRY_TIME) {
187         NotifyDone(false, "Network failed");
188     }
189 }
190 
DoDownload()191 bool DownloadTask::DoDownload()
192 {
193     REQUEST_HILOGD("download task DoDownload");
194     curl_slist *headers{};
195     std::shared_ptr<CURL> handle(curl_easy_init(), [headers](CURL *handle) {
196         if (headers) {
197             curl_slist_free_all(headers);
198         }
199         curl_easy_cleanup(handle);
200     });
201 
202     if (handle == nullptr) {
203         NotifyDone(false, "curl failed");
204         REQUEST_HILOGD("curl failed");
205         return false;
206     }
207 
208     if (!SetOption(handle.get(), headers)) {
209         REQUEST_HILOGD("curl set option failed");
210         return false;
211     }
212     uint32_t localFileLength = GetLocalFileSize();
213     if (localFileLength > 0) {
214         if (localFileLength < totalSize_) {
215             SetResumeFromLarge(handle.get(), localFileLength);
216         } else {
217             NotifyDone(true, "Download task has already completed");
218             return true;
219         }
220     }
221 
222     auto code = curl_easy_perform(handle.get());
223     REQUEST_HILOGI("code=%{public}d, %{public}s", code, errorBuffer_);
224     if (code == CURLE_OK) {
225         NotifyDone(code == CURLE_OK, errorBuffer_);
226     }
227     return code == CURLE_OK;
228 }
229 
SetResumeFromLarge(CURL * curl,uint64_t pos)230 void DownloadTask::SetResumeFromLarge(CURL *curl, uint64_t pos)
231 {
232     curl_easy_setopt(curl, CURLOPT_RESUME_FROM_LARGE, pos);
233 }
234 } // namespace OHOS::Request::Legacy