• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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