• 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 #include "constant.h"
20 #include "log.h"
21 
22 namespace OHOS::Request::Legacy {
23 bool DownloadTask::isCurlGlobalInited_ = false;
24 const uint32_t DEFAULT_READ_TIMEOUT = 60;
25 const uint32_t DEFAULT_LOW_SPEED_LIMIT = 30;
26 constexpr uint32_t RETRY_TIME = 10;
DownloadTask(const std::string & token,const DownloadOption & option,const DoneFunc & callback)27 DownloadTask::DownloadTask(const std::string &token, const DownloadOption &option, const DoneFunc &callback)
28     : taskId_(token), option_(option), callback_(callback), totalSize_(0), hasFileSize_(false)
29 {
30     REQUEST_HILOGI("constructor");
31 }
32 
~DownloadTask()33 DownloadTask::~DownloadTask()
34 {
35     REQUEST_HILOGI("destroy");
36     if (filp_ != nullptr) {
37         fclose(filp_);
38     }
39     delete[] errorBuffer_;
40     delete thread_;
41 }
42 
OpenDownloadFile() const43 FILE *DownloadTask::OpenDownloadFile() const
44 {
45     auto downloadFile = option_.fileDir_ + '/' + option_.filename_;
46     FILE *filp = fopen(downloadFile.c_str(), "w+");
47     if (filp == nullptr) {
48         REQUEST_HILOGE("open download file failed");
49     }
50     return filp;
51 }
52 
GetLocalFileSize()53 uint32_t DownloadTask::GetLocalFileSize()
54 {
55     if (filp_ == nullptr) {
56         filp_ = OpenDownloadFile();
57         if (filp_ == nullptr) {
58             return 0;
59         }
60     }
61 
62     int nRet = fseek(filp_, 0, SEEK_END);
63     if (nRet != 0) {
64         REQUEST_HILOGE("fseek error");
65         return 0;
66     }
67     long lRet = ftell(filp_);
68     if (lRet < 0) {
69         REQUEST_HILOGE("ftell error");
70         return 0;
71     }
72     return static_cast<uint32_t>(lRet);
73 }
NotifyDone(bool successful,const std::string & errMsg)74 void DownloadTask::NotifyDone(bool successful, const std::string &errMsg)
75 {
76     if (filp_ != nullptr) {
77         fclose(filp_);
78         filp_ = nullptr;
79 
80         if (!successful) {
81             REQUEST_HILOGE("remove download file");
82             remove((option_.fileDir_ + '/' + option_.filename_).c_str());
83         }
84     }
85 
86     if (callback_) {
87         callback_(taskId_, successful, errMsg);
88     }
89 }
90 
GetFileSize(uint32_t & result)91 bool DownloadTask::GetFileSize(uint32_t &result)
92 {
93     if (hasFileSize_) {
94         REQUEST_HILOGD("Already get file size");
95         return true;
96     }
97     std::unique_ptr<CURL, decltype(&curl_easy_cleanup)> handle(curl_easy_init(), curl_easy_cleanup);
98 
99     if (!handle) {
100         REQUEST_HILOGD("Failed to create download service task");
101         return false;
102     }
103 
104     curl_easy_setopt(handle.get(), CURLOPT_URL, option_.url_.c_str());
105     curl_easy_setopt(handle.get(), CURLOPT_HEADER, 1L);
106     curl_easy_setopt(handle.get(), CURLOPT_NOBODY, 1L);
107     CURLcode code = curl_easy_perform(handle.get());
108     double size = 0.0;
109     curl_easy_getinfo(handle.get(), CURLINFO_CONTENT_LENGTH_DOWNLOAD, &size);
110 
111     if (code == CURLE_OK) {
112         if (size > UINT_MAX) {
113             REQUEST_HILOGD("file size overflow");
114             return false;
115         }
116         result = static_cast<uint32_t>(size);
117         if (result == static_cast<uint32_t>(-1)) {
118             result = 0;
119         }
120         hasFileSize_ = true;
121         REQUEST_HILOGD("Has got file size");
122     }
123     REQUEST_HILOGD("fetch file size %{public}d", result);
124     return hasFileSize_;
125 }
126 
SetOption(CURL * handle,curl_slist * & headers)127 bool DownloadTask::SetOption(CURL *handle, curl_slist *&headers)
128 {
129     filp_ = OpenDownloadFile();
130     if (filp_ == nullptr) {
131         return false;
132     }
133     curl_easy_setopt(handle, CURLOPT_WRITEDATA, filp_);
134 
135     errorBuffer_ = new (std::nothrow) char[CURL_ERROR_SIZE];
136     if (errorBuffer_ == nullptr) {
137         return false;
138     }
139     curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, errorBuffer_);
140 
141     curl_easy_setopt(handle, CURLOPT_URL, option_.url_.c_str());
142     curl_easy_setopt(handle, CURLOPT_SSL_VERIFYHOST, 0L);
143     curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0L);
144     curl_easy_setopt(handle, CURLOPT_LOW_SPEED_TIME, DEFAULT_READ_TIMEOUT);
145     curl_easy_setopt(handle, CURLOPT_LOW_SPEED_LIMIT, DEFAULT_LOW_SPEED_LIMIT);
146 
147     if (!option_.header_.empty()) {
148         for (const auto &head : option_.header_) {
149             headers = curl_slist_append(headers, head.c_str());
150         }
151         curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
152     }
153     return true;
154 }
155 
Start()156 void DownloadTask::Start()
157 {
158     REQUEST_HILOGD("taskId=%{public}s url=%{public}s file=%{public}s dir=%{public}s", taskId_.c_str(),
159         option_.url_.c_str(), option_.filename_.c_str(), option_.fileDir_.c_str());
160     if (!isCurlGlobalInited_) {
161         curl_global_init(CURL_GLOBAL_ALL);
162         isCurlGlobalInited_ = true;
163     }
164 
165     thread_ = new (std::nothrow) std::thread(&DownloadTask::Run, this);
166     if (thread_ == nullptr) {
167         NotifyDone(false, "create download thread failed");
168         return;
169     }
170     thread_->detach();
171 }
172 
Run()173 void DownloadTask::Run()
174 {
175     REQUEST_HILOGD("start download task");
176     pthread_setname_np(pthread_self(), "system_download");
177     uint32_t retryTime = 0;
178     bool result = false;
179     do {
180         if (GetFileSize(totalSize_)) {
181             result = DoDownload();
182         }
183         retryTime++;
184         REQUEST_HILOGD("download task retrytime: %{public}u, totalSize_: %{public}u", retryTime, totalSize_);
185     } while (!result && retryTime < RETRY_TIME);
186 
187     if (retryTime >= RETRY_TIME) {
188         NotifyDone(false, "Network failed");
189     }
190 }
191 
DoDownload()192 bool DownloadTask::DoDownload()
193 {
194     REQUEST_HILOGD("download task DoDownload");
195     curl_slist *headers{};
196     std::shared_ptr<CURL> handle(curl_easy_init(), [headers](CURL *handle) {
197         if (headers) {
198             curl_slist_free_all(headers);
199         }
200         curl_easy_cleanup(handle);
201     });
202 
203     if (handle == nullptr) {
204         NotifyDone(false, "curl failed");
205         REQUEST_HILOGD("curl failed");
206         return false;
207     }
208 
209     if (!SetOption(handle.get(), headers)) {
210         REQUEST_HILOGD("curl set option failed");
211         return false;
212     }
213     uint32_t localFileLength = GetLocalFileSize();
214     if (localFileLength > 0) {
215         if (localFileLength < totalSize_) {
216             SetResumeFromLarge(handle.get(), localFileLength);
217         } else {
218             NotifyDone(true, "Download task has already completed");
219             return true;
220         }
221     }
222 
223     auto code = curl_easy_perform(handle.get());
224     REQUEST_HILOGI("code=%{public}d, %{public}s", code, errorBuffer_);
225     if (code == CURLE_OK) {
226         NotifyDone(code == CURLE_OK, errorBuffer_);
227     }
228     return code == CURLE_OK;
229 }
230 
SetResumeFromLarge(CURL * curl,uint64_t pos)231 void DownloadTask::SetResumeFromLarge(CURL *curl, uint64_t pos)
232 {
233     curl_easy_setopt(curl, CURLOPT_RESUME_FROM_LARGE, pos);
234 }
235 } // namespace OHOS::Request::Legacy