1 /*
2 * Copyright (c) 2021 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 "adapter/preview/osal/fetch_manager.h"
17
18 #include <memory>
19 #include <mutex>
20
21 #include "curl/curl.h"
22
23 #include "adapter/preview/osal/http_constant.h"
24 #include "base/log/log.h"
25 #include "base/utils/singleton.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 LOGW("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 FetchManagerImpl final : public FetchManager, public Singleton<FetchManagerImpl> {
40 DECLARE_SINGLETON(FetchManagerImpl);
41 ACE_DISALLOW_MOVE(FetchManagerImpl);
42
43 public:
Fetch(const RequestData requestData,const int32_t callbackId,ResponseData & responseData)44 bool Fetch(const RequestData requestData, const int32_t callbackId, ResponseData& responseData) 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 return false;
53 }
54
55 struct curl_slist* header = nullptr;
56 if (!requestData.GetHeader().empty()) {
57 for (auto&& [key, value] : requestData.GetHeader()) {
58 header = curl_slist_append(header, (key + ":" + value).c_str());
59 }
60 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_HTTPHEADER, header);
61 }
62
63 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_TIMEOUT_MS, HttpConstant::TIME_OUT);
64 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_CONNECTTIMEOUT_MS, HttpConstant::TIME_OUT);
65 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_BUFFERSIZE, HttpConstant::BUFFER_SIZE);
66
67 std::string responseBody;
68 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_WRITEFUNCTION, OnWritingMemoryBody);
69 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_WRITEDATA, &responseBody);
70
71 std::string responseHeader;
72 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_HEADERFUNCTION, OnWritingMemoryHeader);
73 ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_HEADERDATA, &responseHeader);
74
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
78 std::string method = requestData.GetMethod();
79 if (method.empty()) {
80 method = "GET";
81 }
82 if (method == HttpConstant::HTTP_METHOD_HEAD || method == HttpConstant::HTTP_METHOD_OPTIONS ||
83 method == HttpConstant::HTTP_METHOD_DELETE || method == HttpConstant::HTTP_METHOD_TRACE ||
84 method == HttpConstant::HTTP_METHOD_GET) {
85 SetOptionForGet(requestData, handle.get());
86 } else if (method == HttpConstant::HTTP_METHOD_POST || method == HttpConstant::HTTP_METHOD_PUT) {
87 SetOptionForPost(requestData, handle.get());
88 } else {
89 responseData.SetCode(HttpConstant::ERROR);
90 responseData.SetData(responseBody);
91 return true;
92 }
93
94 CURLcode result = curl_easy_perform(handle.get());
95 if (result != CURLE_OK) {
96 LOGW("Failed to fetch, url: %{private}s, %{public}s", requestData.GetUrl().c_str(),
97 curl_easy_strerror(result));
98 responseData.SetCode(HttpConstant::ERROR);
99 responseData.SetData(responseBody);
100 return true;
101 }
102
103 char* ct = nullptr;
104 curl_easy_getinfo(handle.get(), CURLINFO_CONTENT_TYPE, &ct);
105
106 int32_t responseCode = HttpConstant::ERROR;
107 curl_easy_getinfo(handle.get(), CURLINFO_RESPONSE_CODE, &responseCode);
108 responseData.SetCode(responseCode);
109 responseData.SetData(responseBody);
110 responseData.SetHeaders(responseHeader);
111 if (header != nullptr) {
112 curl_slist_free_all(header);
113 }
114 return true;
115 }
116
SetOptionForGet(const RequestData requestData,CURL * curl) const117 bool SetOptionForGet(const RequestData requestData, CURL* curl) const
118 {
119 // refer to function buildConnectionWithParam() in HttpFetchImpl.java
120 std::string url = requestData.GetUrl();
121 if (requestData.GetData() != "") {
122 std::size_t index = url.find(HttpConstant::URL_PARAM_SEPARATOR);
123 if (index != std::string::npos) {
124 std::string param = url.substr(index + 1);
125
126 std::string encodeIn = param + HttpConstant::URL_PARAM_DELIMITER + requestData.GetData();
127 char* encodeOut = curl_easy_escape(curl, encodeIn.c_str(), 0);
128 if (encodeOut != nullptr) {
129 url = url.substr(0, index + 1) + encodeOut;
130 curl_free(encodeOut);
131 }
132 } else {
133 char* encodeOut = curl_easy_escape(curl, requestData.GetData().c_str(), 0);
134 if (encodeOut != nullptr) {
135 url = url + HttpConstant::URL_PARAM_SEPARATOR + encodeOut;
136 curl_free(encodeOut);
137 }
138 }
139 }
140 ACE_CURL_EASY_SET_OPTION(curl, CURLOPT_URL, url.c_str());
141 return true;
142 }
143
SetOptionForPost(const RequestData requestData,CURL * curl) const144 bool SetOptionForPost(const RequestData requestData, CURL* curl) const
145 {
146 // refer to function buildConnectionWithStream() in HttpFetchImpl.java
147 std::string url = requestData.GetUrl();
148 ACE_CURL_EASY_SET_OPTION(curl, CURLOPT_URL, url.c_str());
149 ACE_CURL_EASY_SET_OPTION(curl, CURLOPT_POSTFIELDS, requestData.GetData().c_str());
150 return true;
151 }
152
153 private:
OnWritingMemoryBody(const void * data,size_t size,size_t memBytes,void * userData)154 static size_t OnWritingMemoryBody(const void* data, size_t size, size_t memBytes, void* userData)
155 {
156 ((std::string*)userData)->append((char*)data, 0, size * memBytes);
157 return size * memBytes;
158 }
OnWritingMemoryHeader(const void * data,size_t size,size_t memBytes,void * userData)159 static size_t OnWritingMemoryHeader(const void* data, size_t size, size_t memBytes, void* userData)
160 {
161 ((std::string*)userData)->append((char*)data, 0, size * memBytes);
162 return size * memBytes;
163 }
164
Initialize()165 bool Initialize()
166 {
167 if (initialized_) {
168 return true;
169 }
170
171 std::lock_guard<std::mutex> lock(mutex_);
172 if (initialized_) {
173 return true;
174 }
175 if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
176 LOGE("Failed to initialize 'curl'");
177 return false;
178 }
179 initialized_ = true;
180 return true;
181 }
182
183 std::mutex mutex_;
184 bool initialized_ = false;
185 };
186
187 FetchManagerImpl::FetchManagerImpl() = default;
188
~FetchManagerImpl()189 FetchManagerImpl::~FetchManagerImpl()
190 {
191 curl_global_cleanup();
192 }
193
194 } // namespace
195
GetInstance()196 FetchManager& FetchManager::GetInstance()
197 {
198 return Singleton<FetchManagerImpl>::GetInstance();
199 }
200
201 } // namespace OHOS::Ace
202