• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 std::mutex DownloadManager::mutex_;
43 
44 class ACE_FORCE_EXPORT DownloadManagerPrview : public DownloadManager {
45 public:
46     DownloadManagerPrview() = default;
~DownloadManagerPrview()47     ~DownloadManagerPrview()
48     {
49         if (isCurl_) {
50             curl_global_cleanup();
51         }
52     }
53 
Download(const std::string & url,std::vector<uint8_t> & dataOut)54     bool Download(const std::string& url, std::vector<uint8_t>& dataOut) override
55     {
56         // when calling, it is necessary to set it to true and call curl clean up method
57         // during download manager ohos object destruction
58         isCurl_ = true;
59         if (!Initialize()) {
60             return false;
61         }
62 
63         std::unique_ptr<CURL, decltype(&curl_easy_cleanup)> handle(curl_easy_init(), &curl_easy_cleanup);
64         if (!handle) {
65             return false;
66         }
67 
68         dataOut.clear();
69         std::string errorStr;
70         errorStr.reserve(CURL_ERROR_SIZE);
71 
72         ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_URL, url.c_str());
73         ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_WRITEFUNCTION, OnWritingMemory);
74         ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_WRITEDATA, &dataOut);
75         // Some servers don't like requests that are made without a user-agent field, so we provide one
76         ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_USERAGENT, "libcurl-agent/1.0");
77         ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_URL, url.c_str());
78         ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_VERBOSE, 1L);
79         ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_ERRORBUFFER, errorStr.data());
80 
81         ProxyInfo proxy;
82         if (GetProxy(proxy)) {
83             ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_PROXY, proxy.host.c_str());
84             ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_PROXYPORT, proxy.port);
85             if (!proxy.exclusions.empty()) {
86                 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_NOPROXY, proxy.exclusions.c_str());
87             }
88             ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
89             ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_HTTPPROXYTUNNEL, 1L);
90         }
91 
92         ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_SSL_VERIFYPEER, 0L);
93         ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_SSL_VERIFYHOST, 0L);
94 
95         CURLcode result = curl_easy_perform(handle.get());
96         if (result != CURLE_OK) {
97             LOGE("Failed to download, url: %{private}s, %{public}s", url.c_str(), curl_easy_strerror(result));
98             if (!errorStr.empty()) {
99                 LOGE("Failed to download reason: %{public}s", errorStr.c_str());
100             }
101             dataOut.clear();
102             return false;
103         }
104         dataOut.shrink_to_fit();
105         return true;
106     }
107 
DownloadAsync(DownloadCallback && downloadCallback,const std::string & url,int32_t instanceId)108     bool DownloadAsync(DownloadCallback&& downloadCallback, const std::string& url, int32_t instanceId) override
109     {
110         return false;
111     }
112 
DownloadSync(DownloadCallback && downloadCallback,const std::string & url,int32_t instanceId)113     bool DownloadSync(DownloadCallback&& downloadCallback, const std::string& url, int32_t instanceId) override
114     {
115         return false;
116     }
117 
118 private:
119     struct ProxyInfo {
120         std::string host;
121         int32_t port = 0;
122         std::string exclusions;
123     };
124 
Initialize()125     bool Initialize()
126     {
127         if (initialized_) {
128             return true;
129         }
130 
131         std::lock_guard<std::mutex> lock(mutex_);
132         if (initialized_) {
133             return true;
134         }
135         if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
136             LOGE("Failed to initialize 'curl'");
137             return false;
138         }
139         initialized_ = true;
140         return true;
141     }
142 
OnWritingMemory(void * data,size_t size,size_t memBytes,void * userData)143     static size_t OnWritingMemory(void* data, size_t size, size_t memBytes, void* userData)
144     {
145         // size is always 1, for more details see https://curl.haxx.se/libcurl/c/CURLOPT_WRITEFUNCTION.html
146         auto& dataOut = *static_cast<std::vector<uint8_t>*>(userData);
147         auto chunkData = static_cast<uint8_t*>(data);
148         dataOut.insert(dataOut.end(), chunkData, chunkData + memBytes);
149         return memBytes;
150     }
151 
GetProxy(ProxyInfo & proxy)152     static bool GetProxy(ProxyInfo& proxy)
153     {
154         return false;
155     }
156 
157     std::mutex mutex_;
158     bool initialized_ = false;
159     bool isCurl_ = false;
160 };
161 
GetInstance()162 DownloadManager* DownloadManager::GetInstance()
163 {
164     if (!instance_) {
165         std::lock_guard<std::mutex> lock(mutex_);
166         if (!instance_) {
167             instance_.reset(new DownloadManagerPrview());
168         }
169     }
170     return instance_.get();
171 }
172 } // namespace OHOS::Ace
173