1 /*
2 * Copyright (c) 2021-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 "base/network/download_manager.h"
17
18 #include <memory>
19 #include <mutex>
20
21 #include "curl/curl.h"
22
23 #include "base/log/log.h"
24 #include "base/utils/singleton.h"
25 #include "base/utils/utils.h"
26
27 #define ACE_CURL_EASY_SET_OPTION(handle, opt, data) \
28 do { \
29 CURLcode result = curl_easy_setopt(handle, opt, data); \
30 if (result != CURLE_OK) { \
31 LOGE("Failed to set option: %{public}s, %{public}s", #opt, curl_easy_strerror(result)); \
32 return false; \
33 } \
34 } while (0)
35
36 namespace OHOS::Ace {
37 namespace {
38
39 class DownloadManagerImpl final : public DownloadManager, public Singleton<DownloadManagerImpl> {
40 DECLARE_SINGLETON(DownloadManagerImpl);
41 ACE_DISALLOW_MOVE(DownloadManagerImpl);
42
43 public:
Download(const std::string & url,std::vector<uint8_t> & dataOut)44 bool Download(const std::string& url, std::vector<uint8_t>& dataOut) override
45 {
46 if (!Initialize()) {
47 return false;
48 }
49
50 std::unique_ptr<CURL, decltype(&curl_easy_cleanup)> handle(curl_easy_init(), &curl_easy_cleanup);
51 if (!handle) {
52 LOGE("Failed to create download task");
53 return false;
54 }
55
56 dataOut.clear();
57 std::string errorStr;
58 errorStr.reserve(CURL_ERROR_SIZE);
59
60 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_URL, url.c_str());
61 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_WRITEFUNCTION, OnWritingMemory);
62 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_WRITEDATA, &dataOut);
63 // Some servers don't like requests that are made without a user-agent field, so we provide one
64 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_USERAGENT, "libcurl-agent/1.0");
65 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_URL, url.c_str());
66 #if !defined(WINDOWS_PLATFORM) and !defined(MAC_PLATFORM)
67 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_CAINFO, "/etc/ssl/certs/cacert.pem");
68 #endif
69 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_VERBOSE, 1L);
70 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_ERRORBUFFER, errorStr.data());
71
72 CURLcode result = curl_easy_perform(handle.get());
73 if (result != CURLE_OK) {
74 LOGE("Failed to download, url: %{private}s, %{public}s", url.c_str(), curl_easy_strerror(result));
75 if (!errorStr.empty()) {
76 LOGE("Failed to download reason: %{public}s", errorStr.c_str());
77 }
78 dataOut.clear();
79 return false;
80 }
81 dataOut.shrink_to_fit();
82 return true;
83 }
84
85 private:
OnWritingMemory(void * data,size_t size,size_t memBytes,void * userData)86 static size_t OnWritingMemory(void* data, size_t size, size_t memBytes, void* userData)
87 {
88 // size is always 1, for more details see https://curl.haxx.se/libcurl/c/CURLOPT_WRITEFUNCTION.html
89 auto& dataOut = *static_cast<std::vector<uint8_t>*>(userData);
90 auto chunkData = static_cast<uint8_t*>(data);
91 dataOut.insert(dataOut.end(), chunkData, chunkData + memBytes);
92 return memBytes;
93 }
94
Initialize()95 bool Initialize()
96 {
97 if (initialized_) {
98 return true;
99 }
100
101 std::lock_guard<std::mutex> lock(mutex_);
102 if (initialized_) {
103 return true;
104 }
105 if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
106 LOGE("Failed to initialize 'curl'");
107 return false;
108 }
109 initialized_ = true;
110 return true;
111 }
112
113 std::mutex mutex_;
114 bool initialized_ = false;
115 };
116
117 DownloadManagerImpl::DownloadManagerImpl() = default;
118
~DownloadManagerImpl()119 DownloadManagerImpl::~DownloadManagerImpl()
120 {
121 curl_global_cleanup();
122 }
123
124 }
125
GetInstance()126 DownloadManager& DownloadManager::GetInstance()
127 {
128 return Singleton<DownloadManagerImpl>::GetInstance();
129 }
130
131 } // namespace OHOS::Ace
132