• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 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 "download_service_task.h"
17 
18 #include <algorithm>
19 #include <cinttypes>
20 #include <mutex>
21 #include <unistd.h>
22 #include <fstream>
23 #include <sstream>
24 #include <ios>
25 #include <map>
26 #include <memory>
27 #include <string>
28 #include <ostream>
29 #include <utility>
30 #include <vector>
31 #include <curl/easy.h>
32 #include "log.h"
33 #include "network_adapter.h"
34 #include "hitrace_meter.h"
35 #include "task_statistics.h"
36 #include "task_fault.h"
37 #include "download_background_notification.h"
38 
39 namespace OHOS::Request::Download {
40 namespace {
41 static constexpr const char *URL_HTTPS = "https";
42 static constexpr const char *TLS_VERSION_DEFAULT = "CURL_SSLVERSION_TLSv1_2";
43 static constexpr const char *HTTP_GET_METHOD = "GET";
44 static constexpr uint32_t HUNDRED_PERCENT = 100;
45 static constexpr uint32_t TEN_PERCENT_THRESHOLD = 10;
46 static constexpr uint32_t NOTIFICATION_FREQUENCY = 2000;
47 static constexpr uint32_t RETRY_TIME_MAX = 10;
48 static constexpr int CURL_REMOVE_TASK = 1;
49 static constexpr int64_t INVALID_LEN = -1;
50 }
DownloadServiceTask(uint32_t taskId,const DownloadConfig & config)51 DownloadServiceTask::DownloadServiceTask(uint32_t taskId, const DownloadConfig &config)
52     : taskId_(taskId), config_(config), status_(SESSION_UNKNOWN), code_(ERROR_UNKNOWN), reason_(PAUSED_UNKNOWN),
53       mimeType_(""), totalSize_(INVALID_LEN), downloadSize_(INVALID_LEN), isPartialMode_(false), forceStop_(false),
54       isRemoved_(false), retryTime_(RETRY_TIME_MAX), eventCb_(nullptr), hasFileSize_(false), isOnline_(true),
55       prevSize_(INVALID_LEN)
56 {
57 }
58 
~DownloadServiceTask(void)59 DownloadServiceTask::~DownloadServiceTask(void)
60 {
61     DOWNLOAD_HILOGD("Destructed download service task [%{public}d]", taskId_);
62     if (config_.GetFD() > 0) {
63         close(config_.GetFD());
64         config_.SetFD(-1);
65     }
66 }
67 
GetId() const68 uint32_t DownloadServiceTask::GetId() const
69 {
70     return taskId_;
71 }
72 
Run()73 bool DownloadServiceTask::Run()
74 {
75     DOWNLOAD_HILOGI("Task[%{public}d] start", taskId_);
76     if (HandleFileError()) {
77         return false;
78     }
79 
80     if (!NetworkAdapter::GetInstance().IsOnline()) {
81         DOWNLOAD_HILOGI("network is offline");
82         SetStatus(SESSION_FAILED, ERROR_OFFLINE, PAUSED_UNKNOWN);
83         return false;
84     }
85 
86     uint32_t retryTime = 0;
87     bool result = false;
88     bool enableTimeout = false;
89     SetStatus(SESSION_RUNNING);
90 
91     do {
92         if (!IsSatisfiedConfiguration()) {
93             DOWNLOAD_HILOGI("networktype not Satisfied Configuration");
94             ForceStopRunning();
95             SetStatus(SESSION_FAILED, ERROR_UNSUPPORTED_NETWORK_TYPE, PAUSED_UNKNOWN);
96             break;
97         }
98         enableTimeout = false;
99         if (status_ != SESSION_RUNNING && status_ != SESSION_PENDING) {
100             break;
101         }
102         if (!GetFileSize(totalSize_)) {
103             DOWNLOAD_HILOGI("get file size fail");
104         }
105 
106         result = ExecHttp();
107         DumpStatus();
108         DumpErrorCode();
109         DumpPausedReason();
110 
111         // HTTP timeout occurs, retry
112         if (status_ == SESSION_PENDING) {
113             enableTimeout = true;
114             retryTime++;
115         }
116     } while (!result && enableTimeout && retryTime < retryTime_);
117     if (retryTime >= retryTime_) {
118         SetStatus(SESSION_PAUSED, ERROR_UNKNOWN, PAUSED_WAITING_TO_RETRY);
119     }
120     return result;
121 }
122 
Pause()123 bool DownloadServiceTask::Pause()
124 {
125     DOWNLOAD_HILOGI("Status [%{public}d], Code [%{public}d], Reason [%{public}d]", status_, code_, reason_);
126     if (status_ != SESSION_RUNNING && status_ != SESSION_PENDING) {
127         return false;
128     }
129     ForceStopRunning();
130 
131     SetStatus(SESSION_PAUSED, ERROR_UNKNOWN, PAUSED_BY_USER);
132     return true;
133 }
134 
Resume()135 bool DownloadServiceTask::Resume()
136 {
137     DOWNLOAD_HILOGI("Status [%{public}d], Code [%{public}d], Reason [%{public}d]", status_, code_, reason_);
138     if (status_ == SESSION_PAUSED || (status_ == SESSION_FAILED && code_ == ERROR_CANNOT_RESUME)) {
139         forceStop_ = false;
140         if (!CheckResumeCondition()) {
141             SetStatus(SESSION_FAILED, ERROR_CANNOT_RESUME, PAUSED_UNKNOWN);
142         } else {
143             // reset status
144             SetStatus(SESSION_UNKNOWN, ERROR_UNKNOWN, PAUSED_UNKNOWN);
145         }
146         return true;
147     }
148     return false;
149 }
150 
Remove()151 bool DownloadServiceTask::Remove()
152 {
153     DOWNLOAD_HILOGI("Task[%{public}d], Status [%{public}d], Code [%{public}d], Reason [%{public}d]",  taskId_,
154                     status_, code_, reason_);
155     isRemoved_ = true;
156     ForceStopRunning();
157     if (eventCb_ != nullptr) {
158         eventCb_("remove", taskId_, 0, 0, true);
159     }
160     return true;
161 }
162 
Query(DownloadInfo & info)163 bool DownloadServiceTask::Query(DownloadInfo &info)
164 {
165     DOWNLOAD_HILOGD("Query Task[%{public}d], current status is %{public}d", taskId_, status_);
166     info.SetDescription(config_.GetDescription());
167     info.SetDownloadedBytes(downloadSize_);
168     info.SetDownloadId(taskId_);
169     info.SetFailedReason(code_);
170     std::string fileName = config_.GetFilePath().substr(config_.GetFilePath().rfind('/') + 1);
171     std::string filePath = config_.GetFilePath().substr(0, config_.GetFilePath().rfind('/'));
172     info.SetFileName(fileName);
173     info.SetFilePath(filePath);
174     info.SetPausedReason(reason_);
175     info.SetStatus(status_);
176     info.SetTargetURI(config_.GetUrl());
177     info.SetDownloadTitle(config_.GetTitle());
178     info.SetDownloadTotalBytes(totalSize_);
179     info.SetNetworkType(config_.GetNetworkType());
180     info.SetMetered(config_.IsMetered());
181     info.SetRoaming(config_.IsRoaming());
182     return true;
183 }
184 
QueryMimeType(std::string & mimeType)185 bool DownloadServiceTask::QueryMimeType(std::string &mimeType)
186 {
187     DOWNLOAD_HILOGD("Query Mime Type of Task[%{public}d], current status is %{public}d", taskId_, status_);
188     mimeType = mimeType_;
189     return true;
190 }
191 
InstallCallback(DownloadTaskCallback cb)192 void DownloadServiceTask::InstallCallback(DownloadTaskCallback cb)
193 {
194     eventCb_ = cb;
195 }
196 
GetRunResult(DownloadStatus & status,ErrorCode & code,PausedReason & reason)197 void DownloadServiceTask::GetRunResult(DownloadStatus &status, ErrorCode &code, PausedReason &reason)
198 {
199     status = status_;
200     code = code_;
201     reason = reason_;
202 }
203 
SetRetryTime(uint32_t retryTime)204 void DownloadServiceTask::SetRetryTime(uint32_t retryTime)
205 {
206     retryTime_ = retryTime;
207 }
208 
SetNetworkStatus(bool isOnline)209 void DownloadServiceTask::SetNetworkStatus(bool isOnline)
210 {
211     std::lock_guard<std::recursive_mutex> autoLock(mutex_);
212     isOnline_ = isOnline;
213     if (status_ == SESSION_PAUSED && reason_ == PAUSED_WAITING_TO_RETRY && !isOnline_) {
214         reason_ = PAUSED_WAITING_FOR_NETWORK;
215     }
216 }
217 
SetStatus(DownloadStatus status,ErrorCode code,PausedReason reason)218 void DownloadServiceTask::SetStatus(DownloadStatus status, ErrorCode code, PausedReason reason)
219 {
220     auto stateChange = [this](DownloadStatus status, ErrorCode code, PausedReason reason) -> bool {
221         std::lock_guard<std::recursive_mutex> autoLock(mutex_);
222         bool isChanged = false;
223         if (status != this->status_) {
224             this->status_ = status;
225             isChanged = true;
226         }
227         if (code != this->code_) {
228             this->code_ = code;
229             isChanged = true;
230         }
231         if (this->reason_ != PAUSED_BY_USER) {
232             if (!isOnline_ && reason == PAUSED_WAITING_TO_RETRY) {
233                 reason = PAUSED_WAITING_FOR_NETWORK;
234             }
235             if (reason != this->reason_) {
236                 this->reason_ = reason;
237                 isChanged = true;
238             }
239         }
240 
241         return isChanged;
242     };
243     DOWNLOAD_HILOGI("Status [%{public}d], Code [%{public}d], Reason [%{public}d]", status, code, reason);
244     if (!stateChange(status, code, reason)) {
245         return;
246     }
247     if (eventCb_ != nullptr) {
248         std::lock_guard<std::recursive_mutex> autoLock(mutex_);
249         switch (status_) {
250             case SESSION_SUCCESS:
251                 eventCb_("complete", taskId_, 0, 0, true);
252                 break;
253 
254             case SESSION_PAUSED:
255                 eventCb_("pause", taskId_, 0, 0, true);
256                 break;
257 
258             case SESSION_FAILED:
259                 eventCb_("fail", taskId_, code_, 0, true);
260                 break;
261 
262             default:
263                 break;
264         }
265     }
266 }
267 
SetStatus(DownloadStatus status)268 void DownloadServiceTask::SetStatus(DownloadStatus status)
269 {
270     auto stateChange = [this](DownloadStatus status) -> bool {
271         std::lock_guard<std::recursive_mutex> autoLock(mutex_);
272         if (status == this->status_) {
273             DOWNLOAD_HILOGD("ignore same status");
274             return false;
275         }
276         this->status_ = status;
277         return true;
278     };
279     DOWNLOAD_HILOGI("Status [%{public}d]", status);
280     if (!stateChange(status)) {
281         return;
282     }
283     if (eventCb_ != nullptr) {
284         std::lock_guard<std::recursive_mutex> autoLock(mutex_);
285         switch (status_) {
286             case SESSION_SUCCESS:
287                 eventCb_("complete", taskId_, 0, 0, true);
288                 break;
289 
290             case SESSION_PAUSED:
291                 eventCb_("pause", taskId_, 0, 0, true);
292                 break;
293 
294             case SESSION_FAILED:
295                 eventCb_("fail", taskId_, code_, 0, true);
296                 break;
297 
298             default:
299                 break;
300         }
301     }
302 }
303 
SetError(ErrorCode code)304 void DownloadServiceTask::SetError(ErrorCode code)
305 {
306     DOWNLOAD_HILOGI("Code [%{public}d]", code);
307     std::lock_guard<std::recursive_mutex> autoLock(mutex_);
308     if (code == code_) {
309         DOWNLOAD_HILOGD("ignore same error code");
310         return;
311     }
312     code_ = code;
313 }
314 
SetReason(PausedReason reason)315 void DownloadServiceTask::SetReason(PausedReason reason)
316 {
317     DOWNLOAD_HILOGI("Reason [%{public}d]", reason);
318     std::lock_guard<std::recursive_mutex> autoLock(mutex_);
319 
320     if (reason_ != PAUSED_BY_USER) {
321         if (!isOnline_ && reason == PAUSED_WAITING_TO_RETRY) {
322             reason = PAUSED_WAITING_FOR_NETWORK;
323         }
324         if (reason == reason_) {
325             DOWNLOAD_HILOGD("ignore same paused reason");
326             return;
327         }
328         reason_ = reason;
329     }
330 }
331 
DumpStatus()332 void DownloadServiceTask::DumpStatus()
333 {
334     switch (status_) {
335         case SESSION_SUCCESS:
336             DOWNLOAD_HILOGD("status:	SESSION_SUCCESS");
337             break;
338 
339         case SESSION_RUNNING:
340             DOWNLOAD_HILOGD("status:	SESSION_RUNNING");
341             break;
342 
343         case SESSION_PENDING:
344             DOWNLOAD_HILOGD("status:	SESSION_PENDING");
345             break;
346 
347         case SESSION_PAUSED:
348             DOWNLOAD_HILOGD("status:	SESSION_PAUSED");
349             break;
350 
351         case SESSION_FAILED:
352             DOWNLOAD_HILOGD("status:	SESSION_FAILED");
353             break;
354 
355         case SESSION_UNKNOWN:
356             DOWNLOAD_HILOGD("status:	SESSION_UNKNOWN");
357             break;
358 
359         default:
360             DOWNLOAD_HILOGD("status:	SESSION_UNKNOWN");
361             break;
362     }
363 }
364 
DumpErrorCode()365 void DownloadServiceTask::DumpErrorCode()
366 {
367     switch (code_) {
368         case ERROR_CANNOT_RESUME:
369             DOWNLOAD_HILOGD("error code:	ERROR_CANNOT_RESUME");
370             break;
371 
372         case ERROR_DEVICE_NOT_FOUND:
373             DOWNLOAD_HILOGD("error code:	ERROR_DEVICE_NOT_FOUND");
374             break;
375 
376         case ERROR_INSUFFICIENT_SPACE:
377             DOWNLOAD_HILOGD("error code:	ERROR_INSUFFICIENT_SPACE");
378             break;
379 
380         case ERROR_FILE_ALREADY_EXISTS:
381             DOWNLOAD_HILOGD("error code:	ERROR_FILE_ALREADY_EXISTS");
382             break;
383 
384         case ERROR_FILE_ERROR:
385             DOWNLOAD_HILOGD("error code:	ERROR_FILE_ERROR");
386             break;
387 
388         case ERROR_HTTP_DATA_ERROR:
389             DOWNLOAD_HILOGD("error code:	ERROR_HTTP_DATA_ERROR");
390             break;
391 
392         case ERROR_TOO_MANY_REDIRECTS:
393             DOWNLOAD_HILOGD("error code:	ERROR_TOO_MANY_REDIRECTS");
394             break;
395 
396         case ERROR_UNHANDLED_HTTP_CODE:
397             DOWNLOAD_HILOGD("error code:	ERROR_UNHANDLED_HTTP_CODE");
398             break;
399 
400         case ERROR_UNKNOWN:
401             DOWNLOAD_HILOGD("error code:	ERROR_UNKNOWN");
402             break;
403 
404         default:
405             DOWNLOAD_HILOGD("error code:	SESSION_UNKNOWN");
406             break;
407     }
408 }
409 
DumpPausedReason()410 void DownloadServiceTask::DumpPausedReason()
411 {
412     switch (reason_) {
413         case PAUSED_QUEUED_FOR_WIFI:
414             DOWNLOAD_HILOGD("paused reason:	PAUSED_QUEUED_FOR_WIFI");
415             break;
416 
417         case PAUSED_WAITING_FOR_NETWORK:
418             DOWNLOAD_HILOGD("paused reason:	PAUSED_WAITING_FOR_NETWORK");
419             break;
420 
421         case PAUSED_WAITING_TO_RETRY:
422             DOWNLOAD_HILOGD("paused reason:	PAUSED_WAITING_TO_RETRY");
423             break;
424 
425         case PAUSED_BY_USER:
426             DOWNLOAD_HILOGD("paused reason:	PAUSED_BY_USER");
427             break;
428 
429         case PAUSED_UNKNOWN:
430             DOWNLOAD_HILOGD("paused reason:	PAUSED_UNKNOWN");
431             break;
432 
433         default:
434             DOWNLOAD_HILOGD("paused reason:	PAUSED_UNKNOWN");
435             break;
436     }
437 }
438 
WriteCallback(void * buffer,size_t size,size_t num,void * param)439 size_t DownloadServiceTask::WriteCallback(void *buffer, size_t size, size_t num, void *param)
440 {
441     size_t result = 0;
442     DownloadServiceTask *this_ = static_cast<DownloadServiceTask *>(param);
443     if (this_ != nullptr && this_->config_.GetFD() > 0 && this_->hasFileSize_) {
444         result = static_cast<size_t>(write(this_->config_.GetFD(), buffer, size * num));
445         if (result < size * num) {
446             DOWNLOAD_HILOGE("origin size = %{public}zu, write size = %{public}zu", size * num, result);
447         }
448         this_->downloadSize_ += static_cast<int64_t>(result);
449     }
450     return result;
451 }
452 
ProgressCallback(void * pParam,double dltotal,double dlnow,double ultotal,double ulnow)453 int DownloadServiceTask::ProgressCallback(void *pParam, double dltotal, double dlnow, double ultotal, double ulnow)
454 {
455     DownloadServiceTask *task = static_cast<DownloadServiceTask *>(pParam);
456     if (task != nullptr && task->hasFileSize_) {
457         if (task->isRemoved_) {
458             DOWNLOAD_HILOGI("download task has been removed");
459             return CURL_REMOVE_TASK;
460         }
461         if (task->forceStop_) {
462             DOWNLOAD_HILOGI("Pause issued by user");
463             return HTTP_FORCE_STOP;
464         }
465         if (task->eventCb_ == nullptr) {
466             return 0;
467         }
468         if (task->prevSize_ != task->downloadSize_) {
469             std::lock_guard<std::recursive_mutex> autoLock(task->mutex_);
470             if (task->status_ != SESSION_PAUSED) {
471                 task->eventCb_("progress", task->taskId_, task->downloadSize_, task->totalSize_,  task->isNotifyApp_);
472                 task->PublishNotification(task->config_.IsBackground(), task->prevSize_,
473                                           task->downloadSize_, task->totalSize_);
474                 task->prevSize_ = task->downloadSize_;
475             }
476         }
477         // calc the download speed
478     }
479     return 0;
480 }
481 
ExecHttp()482 bool DownloadServiceTask::ExecHttp()
483 {
484     HitraceScoped traceStart(HITRACE_TAG_MISC, "download file");
485     std::unique_ptr<CURL, decltype(&curl_easy_cleanup)> handle(curl_easy_init(), curl_easy_cleanup);
486     if (!handle) {
487         DOWNLOAD_HILOGE("Failed to create fetch task");
488         return false;
489     }
490     DOWNLOAD_HILOGI("final url: %{public}s", config_.GetUrl().c_str());
491     std::vector<std::string> vec;
492     std::for_each(
493         config_.GetHeader().begin(), config_.GetHeader().end(), [&vec](const std::pair<std::string, std::string> &p) {
494             vec.emplace_back(p.first + HTTP_HEADER_SEPARATOR + p.second);
495         });
496     std::unique_ptr<struct curl_slist, decltype(&curl_slist_free_all)> header(MakeHeaders(vec), curl_slist_free_all);
497     if (!SetOption(handle.get(), header.get())) {
498         DOWNLOAD_HILOGE("set option failed");
499         return false;
500     }
501     if (config_.GetFD() > 0) {
502         DOWNLOAD_HILOGD("Succeed to open download file");
503         off_t pos = lseek64(config_.GetFD(), 0, SEEK_END);
504         downloadSize_ = 0;
505         if (pos > 0) {
506             if (totalSize_ == INVALID_LEN || pos < static_cast<off_t>(totalSize_)) {
507                 SetResumeFromLarge(handle.get(), pos);
508             } else {
509                 downloadSize_ = totalSize_;
510                 DOWNLOAD_HILOGI("Download task has already completed");
511                 SetStatus(SESSION_SUCCESS);
512                 PublishNotification(config_.IsBackground(), HUNDRED_PERCENT);
513                 return true;
514             }
515         }
516         prevSize_ = downloadSize_;
517     } else {
518         DOWNLOAD_HILOGD("Failed to open download file");
519     }
520     PublishNotification(config_.IsBackground(), prevSize_, downloadSize_, totalSize_);
521     CURLcode code = CurlPerformFileTransfer(handle.get());
522     int32_t httpCode;
523     curl_easy_getinfo(handle.get(), CURLINFO_RESPONSE_CODE, &httpCode);
524     HandleResponseCode(code, httpCode);
525     HandleCleanup(status_);
526     RecordTaskEvent(httpCode);
527     return code == CURLE_OK;
528 }
529 
RecordTaskEvent(int32_t httpCode)530 void DownloadServiceTask::RecordTaskEvent(int32_t httpCode)
531 {
532     DOWNLOAD_HILOGI("in RecordTaskEvent");
533     if (status_ == SESSION_SUCCESS) {
534         TaskStatistics::GetInstance().ReportTasksSize(totalSize_);
535         TaskStatistics::GetInstance().ReportTasksNumber();
536     } else {
537         TaskFault::GetInstance().ReportTaskFault(httpCode);
538     }
539 }
540 
CurlPerformFileTransfer(CURL * handle) const541 CURLcode DownloadServiceTask::CurlPerformFileTransfer(CURL *handle) const
542 {
543     std::string traceParam = "name:" + config_.GetFilePath() + " size:" + std::to_string(totalSize_) +
544                              " downloaded size:" + std::to_string(prevSize_);
545     HitraceScoped trace(HITRACE_TAG_MISC, "download file" + traceParam);
546     return curl_easy_perform(handle);
547 }
548 
SetFileSizeOption(CURL * curl,struct curl_slist * requestHeader)549 bool DownloadServiceTask::SetFileSizeOption(CURL *curl, struct curl_slist *requestHeader)
550 {
551     curl_easy_setopt(curl, CURLOPT_URL, config_.GetUrl().c_str());
552     curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, HTTP_GET_METHOD);
553 
554     if (requestHeader != nullptr) {
555         curl_easy_setopt(curl, CURLOPT_HTTPHEADER, requestHeader);
556     }
557     // Some servers don't like requests that are made without a user-agent field, so we provide one
558     curl_easy_setopt(curl, CURLOPT_USERAGENT, HTTP_DEFAULT_USER_AGENT);
559 #if 1
560     curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
561 
562     /* first #undef CURL_DISABLE_COOKIES in curl config */
563     curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "");
564 
565 #ifdef DOWNLOAD_USE_PROXY
566     curl_easy_setopt(curl, CURLOPT_PROXY, HTTP_PROXY_URL_PORT);
567     curl_easy_setopt(curl, CURLOPT_PROXYTYPE, HTTP_PROXY_TYPE);
568     curl_easy_setopt(curl, CURLOPT_HTTPPROXYTUNNEL, 1L);
569 #ifdef DOWNLOAD_PROXY_PASS
570     curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, HTTP_PROXY_PASS);
571 #endif // DOWNLOAD_PROXY_PASS
572 #endif // DOWNLOAD_USE_PROXY
573     if (!SetCertificationOption(curl)) {
574         return false;
575     }
576 
577     curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
578 #if HTTP_CURL_PRINT_VERBOSE
579     curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L, context);
580 #endif
581     curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, DEFAULT_READ_TIMEOUT);
582     curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, 1L);
583     curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, DEFAULT_CONNECT_TIMEOUT);
584 #endif
585     return true;
586 }
587 
SetOption(CURL * curl,struct curl_slist * requestHeader)588 bool DownloadServiceTask::SetOption(CURL *curl, struct curl_slist *requestHeader)
589 {
590     curl_easy_setopt(curl, CURLOPT_URL, config_.GetUrl().c_str());
591     curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
592     curl_easy_setopt(curl, CURLOPT_WRITEDATA, this);
593 
594     curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
595     curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, ProgressCallback);
596     curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, this);
597 
598     if (requestHeader != nullptr) {
599         curl_easy_setopt(curl, CURLOPT_HTTPHEADER, requestHeader);
600     }
601     // Some servers don't like requests that are made without a user-agent field, so we provide one
602     curl_easy_setopt(curl, CURLOPT_USERAGENT, HTTP_DEFAULT_USER_AGENT);
603 #if 1
604     curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
605 
606     /* first #undef CURL_DISABLE_COOKIES in curl config */
607     curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "");
608 
609 #ifdef DOWNLOAD_USE_PROXY
610     curl_easy_setopt(curl, CURLOPT_PROXY, HTTP_PROXY_URL_PORT);
611     curl_easy_setopt(curl, CURLOPT_PROXYTYPE, HTTP_PROXY_TYPE);
612     curl_easy_setopt(curl, CURLOPT_HTTPPROXYTUNNEL, 1L);
613 #ifdef DOWNLOAD_PROXY_PASS
614     curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, HTTP_PROXY_PASS);
615 #endif // DOWNLOAD_PROXY_PASS
616 #endif // DOWNLOAD_USE_PROXY
617     if (!SetCertificationOption(curl)) {
618         return false;
619     }
620 
621     curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
622 #if HTTP_CURL_PRINT_VERBOSE
623     curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L, context);
624 #endif
625     curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, DEFAULT_READ_TIMEOUT);
626     curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, 1L);
627     curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, DEFAULT_CONNECT_TIMEOUT);
628 #endif
629     return true;
630 }
631 
MakeHeaders(const std::vector<std::string> & vec)632 struct curl_slist *DownloadServiceTask::MakeHeaders(const std::vector<std::string> &vec)
633 {
634     struct curl_slist *header = nullptr;
635     std::for_each(vec.begin(), vec.end(), [&header](const std::string &s) {
636         if (!s.empty()) {
637             header = curl_slist_append(header, s.c_str());
638         }
639     });
640     return header;
641 }
642 
SetResumeFromLarge(CURL * curl,long long pos)643 void DownloadServiceTask::SetResumeFromLarge(CURL *curl, long long pos)
644 {
645     isPartialMode_ = true;
646     downloadSize_ = static_cast<int64_t>(pos);
647     curl_easy_setopt(curl, CURLOPT_RESUME_FROM_LARGE, pos);
648 }
649 
GetFileSize(int64_t & result)650 bool DownloadServiceTask::GetFileSize(int64_t &result)
651 {
652     if (hasFileSize_) {
653         DOWNLOAD_HILOGI("Already get file size");
654         return true;
655     }
656     double size = -1;
657     std::unique_ptr<CURL, decltype(&curl_easy_cleanup)> handle(curl_easy_init(), curl_easy_cleanup);
658     if (!handle) {
659         DOWNLOAD_HILOGE("Failed to create download service task");
660         return false;
661     }
662 
663     std::vector<std::string> vec;
664     std::for_each(
665         config_.GetHeader().begin(), config_.GetHeader().end(), [&vec](const std::pair<std::string, std::string> &p) {
666             vec.emplace_back(p.first + HTTP_HEADER_SEPARATOR + p.second);
667         });
668     std::unique_ptr<struct curl_slist, decltype(&curl_slist_free_all)> header(MakeHeaders(vec), curl_slist_free_all);
669 
670     if (!SetFileSizeOption(handle.get(), header.get())) {
671         DOWNLOAD_HILOGE("set option failed");
672         return false;
673     }
674 
675     curl_easy_setopt(handle.get(), CURLOPT_NOBODY, 1L);
676     CURLcode code = curl_easy_perform(handle.get());
677     long respCode = 0;
678     curl_easy_getinfo(handle.get(), CURLINFO_RESPONSE_CODE, &respCode);
679     if ((code == CURLE_OK) && (respCode == HTTP_OK)) {
680         curl_easy_getinfo(handle.get(), CURLINFO_CONTENT_LENGTH_DOWNLOAD, &size);
681         result = static_cast<int64_t>(size);
682         char *ct = nullptr;
683         curl_easy_getinfo(handle.get(), CURLINFO_CONTENT_TYPE, &ct);
684         mimeType_ = ct;
685         hasFileSize_ = true;
686     }
687     DOWNLOAD_HILOGI("file size: %{public}" PRId64 "curl code: %{public}d, http resp: %{public}ld",
688         result, code, respCode);
689     return hasFileSize_;
690 }
691 
GetTmpPath()692 std::string DownloadServiceTask::GetTmpPath()
693 {
694     return config_.GetFilePath() + "_" + std::to_string(taskId_);
695 }
696 
HandleResponseCode(CURLcode code,int32_t httpCode)697 void DownloadServiceTask::HandleResponseCode(CURLcode code, int32_t httpCode)
698 {
699     if (isRemoved_) {
700         DOWNLOAD_HILOGI("download task has been removed");
701         return;
702     }
703     DOWNLOAD_HILOGI("Current CURLcode is %{public}d, httpCode is %{public}d", code, httpCode);
704     if (status_ == SESSION_PAUSED && reason_ == PAUSED_BY_USER) {
705         DOWNLOAD_HILOGI("Pause By User:ignore status changed caused by libcurl");
706         return;
707     }
708 
709     switch (code) {
710         case CURLE_OK:
711             if (httpCode == HTTP_OK || (isPartialMode_ && httpCode == HTTP_PARIAL_FILE)) {
712                 SetStatus(SESSION_SUCCESS);
713                 PublishNotification(config_.IsBackground(), HUNDRED_PERCENT);
714                 return;
715             }
716             break;
717 
718         case CURLE_ABORTED_BY_CALLBACK:
719             if (httpCode == HTTP_OK || (isPartialMode_ && httpCode == HTTP_PARIAL_FILE)) {
720                 SetStatus(SESSION_PAUSED, ERROR_UNKNOWN, PAUSED_BY_USER);
721                 return;
722             }
723             break;
724 
725         case CURLE_WRITE_ERROR:
726             if (httpCode == HTTP_OK || (isPartialMode_ && httpCode == HTTP_PARIAL_FILE)) {
727                 SetStatus(SESSION_FAILED, ERROR_HTTP_DATA_ERROR, PAUSED_UNKNOWN);
728                 return;
729             }
730             break;
731 
732         case CURLE_TOO_MANY_REDIRECTS:
733             SetStatus(SESSION_FAILED, ERROR_TOO_MANY_REDIRECTS, PAUSED_UNKNOWN);
734             return;
735 
736         case CURLE_COULDNT_RESOLVE_PROXY:
737         case CURLE_COULDNT_RESOLVE_HOST:
738         case CURLE_COULDNT_CONNECT:
739         case CURLE_OPERATION_TIMEDOUT:
740             SetStatus(SESSION_PENDING);
741             return;
742 
743         default:
744             break;
745     }
746     SetStatus(SESSION_FAILED, ERROR_UNHANDLED_HTTP_CODE, PAUSED_UNKNOWN);
747 }
748 
CheckResumeCondition()749 bool DownloadServiceTask::CheckResumeCondition()
750 {
751     if (!isOnline_) {
752         return false;
753     }
754     return true;
755 }
756 
ForceStopRunning()757 void DownloadServiceTask::ForceStopRunning()
758 {
759     forceStop_ = true;
760 }
761 
HandleCleanup(DownloadStatus status)762 void DownloadServiceTask::HandleCleanup(DownloadStatus status)
763 {
764     switch (status) {
765         case SESSION_SUCCESS:
766             // rename download to target file name
767             if (config_.GetFD() > 0) {
768                 close(config_.GetFD());
769                 config_.SetFD(-1);
770             }
771             break;
772 
773         case SESSION_FAILED:
774             break;
775 
776         default:
777             break;
778     }
779 }
780 
HandleFileError()781 bool DownloadServiceTask::HandleFileError()
782 {
783     ErrorCode code = ERROR_UNKNOWN;
784     if (config_.GetFD() < 0) {
785         switch (config_.GetFDError()) {
786             case 0:
787                 DOWNLOAD_HILOGD("Download File already exists");
788                 code = ERROR_FILE_ALREADY_EXISTS;
789                 break;
790 
791             case ENODEV:
792                 code = ERROR_DEVICE_NOT_FOUND;
793                 break;
794 
795             default:
796                 code = ERROR_FILE_ERROR;
797                 break;
798         }
799         SetStatus(SESSION_FAILED, code, PAUSED_UNKNOWN);
800         return true;
801     }
802     return false;
803 }
804 
IsSatisfiedConfiguration()805 bool DownloadServiceTask::IsSatisfiedConfiguration()
806 {
807     // Compatible does not support downloading network task configuration version
808     if (config_.GetNetworkType() == NETWORK_INVALID) {
809         return true;
810     }
811     auto networkInfo = NetworkAdapter::GetInstance().GetNetworkInfo();
812     DOWNLOAD_HILOGD("isRoaming_: %{public}d, isMetered_: %{public}d, networkType_: %{public}u",
813                     networkInfo.isRoaming_, networkInfo.isMetered_, networkInfo.networkType_);
814     DOWNLOAD_HILOGD("config_ { isRoaming_: %{public}d,isMetered_: %{public}d, networkType_: %{public}u}",
815                     config_.IsRoaming(), config_.IsMetered(), config_.GetNetworkType());
816     if (networkInfo.isRoaming_ && !config_.IsRoaming()) {
817         return false;
818     }
819     if (networkInfo.isMetered_ && !config_.IsMetered()) {
820         return false;
821     }
822     if ((networkInfo.networkType_ & config_.GetNetworkType()) == NETWORK_INVALID) {
823         return false;
824     }
825     return true;
826 }
827 
SetCertificationOption(CURL * curl)828 bool DownloadServiceTask::SetCertificationOption(CURL *curl)
829 {
830     return (IsHttpsURL() ? SetHttpsCertificationOption(curl) : SetHttpCertificationOption(curl));
831 }
832 
IsHttpsURL()833 bool DownloadServiceTask::IsHttpsURL()
834 {
835     return config_.GetUrl().find(URL_HTTPS) == 0;
836 }
837 
SetHttpCertificationOption(CURL * curl)838 bool DownloadServiceTask::SetHttpCertificationOption(CURL *curl)
839 {
840     curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
841     curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
842     return true;
843 }
844 
SetHttpsCertificationOption(CURL * curl)845 bool DownloadServiceTask::SetHttpsCertificationOption(CURL *curl)
846 {
847     std::string certInfo = ReadCertification();
848     if (certInfo.empty()) {
849         DOWNLOAD_HILOGE("Read certinfo failed");
850         return false;
851     }
852     struct curl_blob blob;
853     blob.data = const_cast<char*>(certInfo.c_str());
854     blob.len = certInfo.size();
855     blob.flags = CURL_BLOB_COPY;
856     std::string version = TLS_VERSION_DEFAULT;
857     std::map<std::string, std::string>::const_iterator iter = config_.GetHeader().find(tlsVersion);
858     if (iter != config_.GetHeader().end()) {
859         version = iter->second;
860         DOWNLOAD_HILOGI("version changes: %{public}s", version.c_str());
861     }
862     curl_easy_setopt(curl, CURLOPT_SSLVERSION, version.c_str());
863     DOWNLOAD_HILOGI("security version is: %{public}s", version.c_str());
864     CURLcode code = curl_easy_setopt(curl, CURLOPT_CAINFO_BLOB, &blob);
865     if (code != CURLE_OK) {
866         return false;
867     }
868     DOWNLOAD_HILOGI("SetHttpsCertificationOption success");
869     return true;
870 }
871 
ReadCertification()872 std::string DownloadServiceTask::ReadCertification()
873 {
874     std::ifstream inFile(std::string(HTTP_DEFAULT_CA_PATH), std::ios::in | std::ios::binary);
875     if (!inFile.is_open()) {
876         DOWNLOAD_HILOGE("open cacert.pem failed");
877         return "";
878     }
879     std::stringstream buf;
880     buf << inFile.rdbuf();
881     std::string certInfo(buf.str());
882     inFile.close();
883     return certInfo;
884 }
885 
PublishNotification(bool background,uint32_t percent)886 void DownloadServiceTask::PublishNotification(bool background, uint32_t percent)
887 {
888     if (!background) {
889         return;
890     }
891     pid_t pid = static_cast<pid_t>(config_.GetApplicationInfoUid());
892     std::string filePath = config_.GetFilePath();
893     DownloadBackgroundNotification::PublishDownloadNotification(taskId_, pid, filePath, percent);
894 }
895 
PublishNotification(bool background,int64_t prevSize,int64_t downloadSize,int64_t totalSize)896 void DownloadServiceTask::PublishNotification(bool background, int64_t prevSize, int64_t downloadSize,
897     int64_t totalSize)
898 {
899     if (!background || totalSize == -1) {
900         return;
901     }
902     if (prevSize == 0) {
903         PublishNotification(background, 0);
904         lastTimestamp_ =  GetCurTimestamp();
905     } else {
906         uint32_t percent = ProgressNotification(prevSize, downloadSize, totalSize);
907         if (percent > 0) {
908             PublishNotification(background, percent);
909         }
910     }
911 }
912 
GetCurTimestamp()913 std::time_t DownloadServiceTask::GetCurTimestamp()
914 {
915     auto tp = std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::system_clock::now());
916     return tp.time_since_epoch().count();
917 }
918 
ProgressNotification(int64_t prevSize,int64_t downloadSize,int64_t totalSize)919 uint32_t DownloadServiceTask::ProgressNotification(int64_t prevSize, int64_t downloadSize, int64_t totalSize)
920 {
921     uint32_t ret = 0;
922     if (totalSize != 0) {
923         uint32_t percent = static_cast<uint32_t>(downloadSize * 100.0 / totalSize);
924         uint32_t lastPercent = static_cast<uint32_t>(prevSize  * 100.0 / totalSize);
925         std::time_t curTimestamp = GetCurTimestamp();
926         if (curTimestamp < lastTimestamp_) {
927             return 0;
928         }
929         if ((percent - lastPercent) >= TEN_PERCENT_THRESHOLD ||
930             (curTimestamp - lastTimestamp_) >= NOTIFICATION_FREQUENCY) {
931             ret = percent;
932             lastTimestamp_ = curTimestamp;
933         }
934     }
935     return ret;
936 }
937 
SetNotifyApp(bool isNotifyApp)938 void DownloadServiceTask::SetNotifyApp(bool isNotifyApp)
939 {
940     isNotifyApp_ = isNotifyApp;
941 }
942 
GetTaskBundleName() const943 std::string DownloadServiceTask::GetTaskBundleName() const
944 {
945     return config_.GetBundleName();
946 }
947 
GetTaskApplicationInfoUid() const948 int32_t DownloadServiceTask::GetTaskApplicationInfoUid() const
949 {
950     return config_.GetApplicationInfoUid();
951 }
952 
953 } // namespace OHOS::Request::Download
954