1 /*
2 * Copyright (c) 2023-2024 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 <condition_variable>
17 #include <cstdint>
18 #include <functional>
19 #include <memory>
20 #include <mutex>
21 #include <optional>
22 #include <string>
23 #include <vector>
24
25 #include "curl/curl.h"
26
27 #include "base/log/log.h"
28 #include "base/network/download_manager.h"
29 #include "base/utils/macros.h"
30
31 #define ACE_CURL_EASY_SET_OPTION(handle, opt, data) \
32 do { \
33 CURLcode result = curl_easy_setopt(handle, opt, data); \
34 if (result != CURLE_OK) { \
35 return false; \
36 } \
37 } while (0)
38
39 namespace OHOS::Ace {
40
41 std::unique_ptr<DownloadManager> DownloadManager::instance_ = nullptr;
42
43 class ACE_FORCE_EXPORT DownloadManagerPrview : public DownloadManager {
44 public:
45 DownloadManagerPrview() = default;
~DownloadManagerPrview()46 ~DownloadManagerPrview()
47 {
48 if (isCurl_) {
49 curl_global_cleanup();
50 }
51 }
52
Download(const std::string & url,std::vector<uint8_t> & dataOut)53 bool Download(const std::string& url, std::vector<uint8_t>& dataOut) override
54 {
55 // when calling, it is necessary to set it to true and call curl clean up method
56 // during download manager ohos object destruction
57 isCurl_ = true;
58 if (!Initialize()) {
59 return false;
60 }
61
62 std::unique_ptr<CURL, decltype(&curl_easy_cleanup)> handle(curl_easy_init(), &curl_easy_cleanup);
63 if (!handle) {
64 return false;
65 }
66
67 dataOut.clear();
68 std::string errorStr;
69 errorStr.reserve(CURL_ERROR_SIZE);
70
71 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_URL, url.c_str());
72 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_WRITEFUNCTION, OnWritingMemory);
73 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_WRITEDATA, &dataOut);
74 // Some servers don't like requests that are made without a user-agent field, so we provide one
75 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_USERAGENT, "libcurl-agent/1.0");
76 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_URL, url.c_str());
77 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_VERBOSE, 1L);
78 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_ERRORBUFFER, errorStr.data());
79
80 ProxyInfo proxy;
81 if (GetProxy(proxy)) {
82 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_PROXY, proxy.host.c_str());
83 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_PROXYPORT, proxy.port);
84 if (!proxy.exclusions.empty()) {
85 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_NOPROXY, proxy.exclusions.c_str());
86 }
87 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
88 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_HTTPPROXYTUNNEL, 1L);
89 }
90
91 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_SSL_VERIFYPEER, 0L);
92 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_SSL_VERIFYHOST, 0L);
93
94 CURLcode result = curl_easy_perform(handle.get());
95 if (result != CURLE_OK) {
96 LOGE("Failed to download, url: %{private}s, %{public}s", url.c_str(), curl_easy_strerror(result));
97 if (!errorStr.empty()) {
98 LOGE("Failed to download reason: %{public}s", errorStr.c_str());
99 }
100 dataOut.clear();
101 return false;
102 }
103 dataOut.shrink_to_fit();
104 return true;
105 }
106
Download(const std::string & url,const std::shared_ptr<DownloadResult> & result)107 bool Download(const std::string& url, const std::shared_ptr<DownloadResult>& result) override
108 {
109 return false;
110 }
111
DownloadAsync(DownloadCallback && downloadCallback,const std::string & url,int32_t instanceId,int32_t nodeId)112 bool DownloadAsync(
113 DownloadCallback&& downloadCallback, const std::string& url, int32_t instanceId, int32_t nodeId) override
114 {
115 return false;
116 }
117
DownloadSync(DownloadCallback && downloadCallback,const std::string & url,int32_t instanceId,int32_t nodeId)118 bool DownloadSync(
119 DownloadCallback&& downloadCallback, const std::string& url, int32_t instanceId, int32_t nodeId) override
120 {
121 return false;
122 }
123
DownloadAsyncWithPreload(DownloadCallback && downloadCallback,const std::string & url,int32_t instanceId,int32_t nodeId)124 bool DownloadAsyncWithPreload(
125 DownloadCallback&& downloadCallback, const std::string& url, int32_t instanceId, int32_t nodeId) override
126 {
127 return false;
128 }
129
DownloadSyncWithPreload(DownloadCallback && downloadCallback,const std::string & url,int32_t instanceId,int32_t nodeId)130 bool DownloadSyncWithPreload(
131 DownloadCallback&& downloadCallback, const std::string& url, int32_t instanceId, int32_t nodeId) override
132 {
133 return false;
134 }
135
fetchCachedResult(const std::string & url,std::string & result)136 bool fetchCachedResult(const std::string& url, std::string& result) override
137 {
138 return false;
139 }
140
RemoveDownloadTask(const std::string & url,int32_t nodeId,bool isCancel=true)141 bool RemoveDownloadTask(const std::string& url, int32_t nodeId, bool isCancel = true) override
142 {
143 return false;
144 }
145
RemoveDownloadTaskWithPreload(const std::string & url,int32_t nodeId,bool isCancel=true)146 bool RemoveDownloadTaskWithPreload(const std::string& url, int32_t nodeId, bool isCancel = true) override
147 {
148 return false;
149 }
150
IsContains(const std::string & url)151 bool IsContains(const std::string& url) override
152 {
153 return false;
154 }
155
156 private:
157 struct ProxyInfo {
158 std::string host;
159 int32_t port = 0;
160 std::string exclusions;
161 };
162
Initialize()163 bool Initialize()
164 {
165 if (initialized_) {
166 return true;
167 }
168
169 std::lock_guard<std::mutex> lock(mutex_);
170 if (initialized_) {
171 return true;
172 }
173 if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
174 LOGE("Failed to initialize 'curl'");
175 return false;
176 }
177 initialized_ = true;
178 return true;
179 }
180
OnWritingMemory(void * data,size_t size,size_t memBytes,void * userData)181 static size_t OnWritingMemory(void* data, size_t size, size_t memBytes, void* userData)
182 {
183 // size is always 1, for more details see https://curl.haxx.se/libcurl/c/CURLOPT_WRITEFUNCTION.html
184 auto& dataOut = *static_cast<std::vector<uint8_t>*>(userData);
185 auto chunkData = static_cast<uint8_t*>(data);
186 dataOut.insert(dataOut.end(), chunkData, chunkData + memBytes);
187 return memBytes;
188 }
189
GetProxy(ProxyInfo & proxy)190 static bool GetProxy(ProxyInfo& proxy)
191 {
192 return false;
193 }
194
195 std::mutex mutex_;
196 bool initialized_ = false;
197 bool isCurl_ = false;
198 };
199
GetInstance()200 DownloadManager* DownloadManager::GetInstance()
201 {
202 static std::once_flag onceFlag;
203 std::call_once(onceFlag, []() {
204 instance_.reset(new DownloadManagerPrview());
205 });
206 return instance_.get();
207 }
208 } // namespace OHOS::Ace
209