• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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             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 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             LOGE("Failed to create fetch task");
53             return false;
54         }
55 
56         struct curl_slist* header = nullptr;
57         if (!requestData.GetHeader().empty()) {
58             for (auto&& [key, value] : requestData.GetHeader()) {
59                 header = curl_slist_append(header, (key + ":" + value).c_str());
60             }
61             ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_HTTPHEADER, header);
62         }
63 
64         ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_TIMEOUT_MS, HttpConstant::TIME_OUT);
65         ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_CONNECTTIMEOUT_MS, HttpConstant::TIME_OUT);
66         ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_BUFFERSIZE, HttpConstant::BUFFER_SIZE);
67 
68         std::string responseBody;
69         ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_WRITEFUNCTION, OnWritingMemoryBody);
70         ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_WRITEDATA, &responseBody);
71 
72         std::string responseHeader;
73         ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_HEADERFUNCTION, OnWritingMemoryHeader);
74         ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_HEADERDATA, &responseHeader);
75 
76         // Some servers don't like requests that are made without a user-agent field, so we provide one
77         ACE_CURL_EASY_SET_OPTION(handle.get(), CURLOPT_USERAGENT, "libcurl-agent/1.0");
78 
79         std::string method = requestData.GetMethod();
80         if (method.empty()) {
81             method = "GET";
82         }
83         if (method == HttpConstant::HTTP_METHOD_HEAD || method == HttpConstant::HTTP_METHOD_OPTIONS ||
84             method == HttpConstant::HTTP_METHOD_DELETE || method == HttpConstant::HTTP_METHOD_TRACE ||
85             method == HttpConstant::HTTP_METHOD_GET) {
86             SetOptionForGet(requestData, handle.get());
87         } else if (method == HttpConstant::HTTP_METHOD_POST || method == HttpConstant::HTTP_METHOD_PUT) {
88             SetOptionForPost(requestData, handle.get());
89         } else {
90             LOGE("no method match!");
91             responseData.SetCode(HttpConstant::ERROR);
92             responseData.SetData(responseBody);
93             return true;
94         }
95 
96         CURLcode result = curl_easy_perform(handle.get());
97         if (result != CURLE_OK) {
98             LOGE("Failed to fetch, url: %{private}s, %{public}s", requestData.GetUrl().c_str(),
99                 curl_easy_strerror(result));
100             responseData.SetCode(HttpConstant::ERROR);
101             responseData.SetData(responseBody);
102             return true;
103         }
104 
105         char* ct = nullptr;
106         CURLcode res = curl_easy_getinfo(handle.get(), CURLINFO_CONTENT_TYPE, &ct);
107         if ((CURLE_OK == res) && ct) {
108             LOGD("fetch-preview content_type: %{public}s", ct);
109         }
110 
111         int32_t responseCode = HttpConstant::ERROR;
112         curl_easy_getinfo(handle.get(), CURLINFO_RESPONSE_CODE, &responseCode);
113         responseData.SetCode(responseCode);
114         responseData.SetData(responseBody);
115         responseData.SetHeaders(responseHeader);
116 
117         curl_slist_free_all(header);
118 
119         return true;
120     }
121 
SetOptionForGet(const RequestData requestData,CURL * curl) const122     bool SetOptionForGet(const RequestData requestData, CURL* curl) const
123     {
124         // refer to function buildConnectionWithParam() in HttpFetchImpl.java
125         LOGD("begin to set option for get and encode final url");
126         std::string url = requestData.GetUrl();
127         if (requestData.GetData() != "") {
128             std::size_t index = url.find(HttpConstant::URL_PARAM_SEPARATOR);
129             if (index != std::string::npos) {
130                 std::string param = url.substr(index + 1);
131 
132                 std::string encodeIn = param + HttpConstant::URL_PARAM_DELIMITER + requestData.GetData();
133                 char* encodeOut = curl_easy_escape(curl, encodeIn.c_str(), 0);
134                 if (encodeOut != nullptr) {
135                     url = url.substr(0, index + 1) + encodeOut;
136                     curl_free(encodeOut);
137                 }
138             } else {
139                 char* encodeOut = curl_easy_escape(curl, requestData.GetData().c_str(), 0);
140                 if (encodeOut != nullptr) {
141                     url = url + HttpConstant::URL_PARAM_SEPARATOR + encodeOut;
142                     curl_free(encodeOut);
143                 }
144             }
145         }
146         LOGD("final url : %{public}s", url.c_str());
147         ACE_CURL_EASY_SET_OPTION(curl, CURLOPT_URL, url.c_str());
148         return true;
149     }
150 
SetOptionForPost(const RequestData requestData,CURL * curl) const151     bool SetOptionForPost(const RequestData requestData, CURL* curl) const
152     {
153         // refer to function buildConnectionWithStream() in HttpFetchImpl.java
154         LOGD("begin to set option for post");
155         std::string url = requestData.GetUrl();
156         LOGD("final url : %{public}s", url.c_str());
157         ACE_CURL_EASY_SET_OPTION(curl, CURLOPT_URL, url.c_str());
158         ACE_CURL_EASY_SET_OPTION(curl, CURLOPT_POSTFIELDS, requestData.GetData().c_str());
159         return true;
160     }
161 
162 private:
OnWritingMemoryBody(const void * data,size_t size,size_t memBytes,void * userData)163     static size_t OnWritingMemoryBody(const void* data, size_t size, size_t memBytes, void* userData)
164     {
165         ((std::string*)userData)->append((char*)data, 0, size * memBytes);
166         return size * memBytes;
167     }
OnWritingMemoryHeader(const void * data,size_t size,size_t memBytes,void * userData)168     static size_t OnWritingMemoryHeader(const void* data, size_t size, size_t memBytes, void* userData)
169     {
170         ((std::string*)userData)->append((char*)data, 0, size * memBytes);
171         return size * memBytes;
172     }
173 
Initialize()174     bool Initialize()
175     {
176         if (initialized_) {
177             return true;
178         }
179 
180         std::lock_guard<std::mutex> lock(mutex_);
181         if (initialized_) {
182             return true;
183         }
184         if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
185             LOGE("Failed to initialize 'curl'");
186             return false;
187         }
188         initialized_ = true;
189         return true;
190     }
191 
192     std::mutex mutex_;
193     bool initialized_ = false;
194 };
195 
196 FetchManagerImpl::FetchManagerImpl() = default;
197 
~FetchManagerImpl()198 FetchManagerImpl::~FetchManagerImpl()
199 {
200     curl_global_cleanup();
201 }
202 
203 } // namespace
204 
GetInstance()205 FetchManager& FetchManager::GetInstance()
206 {
207     return Singleton<FetchManagerImpl>::GetInstance();
208 }
209 
210 } // namespace OHOS::Ace
211