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