• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2023 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(PREVIEW)
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         ProxyInfo proxy;
73         if (GetProxy(proxy)) {
74             ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_PROXY, proxy.host.c_str());
75             ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_PROXYPORT, proxy.port);
76             if (!proxy.exclusions.empty()) {
77                 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_NOPROXY, proxy.exclusions.c_str());
78             }
79             ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
80             ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_HTTPPROXYTUNNEL, 1L);
81         }
82 
83 #if defined(IOS_PLATFORM) || defined(ANDROID_PLATFORM) || defined(PREVIEW)
84         ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_SSL_VERIFYPEER, 0L);
85         ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_SSL_VERIFYHOST, 0L);
86 #endif
87 
88         CURLcode result = curl_easy_perform(handle.get());
89         if (result != CURLE_OK) {
90             LOGE("Failed to download, url: %{private}s, %{public}s", url.c_str(), curl_easy_strerror(result));
91             if (!errorStr.empty()) {
92                 LOGE("Failed to download reason: %{public}s", errorStr.c_str());
93             }
94             dataOut.clear();
95             return false;
96         }
97         dataOut.shrink_to_fit();
98         return true;
99     }
100 
101 private:
OnWritingMemory(void * data,size_t size,size_t memBytes,void * userData)102     static size_t OnWritingMemory(void* data, size_t size, size_t memBytes, void* userData)
103     {
104         // size is always 1, for more details see https://curl.haxx.se/libcurl/c/CURLOPT_WRITEFUNCTION.html
105         auto& dataOut = *static_cast<std::vector<uint8_t>*>(userData);
106         auto chunkData = static_cast<uint8_t*>(data);
107         dataOut.insert(dataOut.end(), chunkData, chunkData + memBytes);
108         return memBytes;
109     }
110 
Initialize()111     bool Initialize()
112     {
113         if (initialized_) {
114             return true;
115         }
116 
117         std::lock_guard<std::mutex> lock(mutex_);
118         if (initialized_) {
119             return true;
120         }
121         if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
122             LOGE("Failed to initialize 'curl'");
123             return false;
124         }
125         initialized_ = true;
126         return true;
127     }
128 
129     std::mutex mutex_;
130     bool initialized_ = false;
131 };
132 
133 DownloadManagerImpl::DownloadManagerImpl() = default;
134 
~DownloadManagerImpl()135 DownloadManagerImpl::~DownloadManagerImpl()
136 {
137     curl_global_cleanup();
138 }
139 
140 } // namespace
141 
GetInstance()142 DownloadManager& DownloadManager::GetInstance()
143 {
144     return Singleton<DownloadManagerImpl>::GetInstance();
145 }
146 
147 } // namespace OHOS::Ace
148