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 <cstddef>
17 #include <cstring>
18 #include <memory>
19 #include <thread>
20 #include <unistd.h>
21
22 #ifdef HTTP_PROXY_ENABLE
23 #include "parameter.h"
24 #endif
25
26 #include "base64_utils.h"
27 #include "cache_proxy.h"
28 #include "constant.h"
29 #include "event_list.h"
30 #include "http_async_work.h"
31 #include "http_time.h"
32 #include "napi_utils.h"
33 #include "netstack_common_utils.h"
34 #include "netstack_log.h"
35 #include "securec.h"
36
37 #include "http_exec.h"
38
39 #define NETSTACK_CURL_EASY_SET_OPTION(handle, opt, data, asyncContext) \
40 do { \
41 CURLcode result = curl_easy_setopt(handle, opt, data); \
42 if (result != CURLE_OK) { \
43 const char *err = curl_easy_strerror(result); \
44 NETSTACK_LOGE("Failed to set option: %{public}s, %{public}s %{public}d", #opt, err, result); \
45 (asyncContext)->SetErrorCode(result); \
46 return false; \
47 } \
48 } while (0)
49
50 namespace OHOS::NetStack {
51 static constexpr size_t MAX_LIMIT = 5 * 1024 * 1024;
52 static constexpr int CURL_TIMEOUT_MS = 50;
53 static constexpr int CONDITION_TIMEOUT_S = 3600;
54 static constexpr int CURL_MAX_WAIT_MSECS = 10;
55 static constexpr int CURL_HANDLE_NUM = 10;
56 #ifdef HTTP_PROXY_ENABLE
57 static constexpr int32_t SYSPARA_MAX_SIZE = 128;
58 static constexpr const char *DEFAULT_HTTP_PROXY_HOST = "NONE";
59 static constexpr const char *DEFAULT_HTTP_PROXY_PORT = "0";
60 static constexpr const char *DEFAULT_HTTP_PROXY_EXCLUSION_LIST = "NONE";
61 static constexpr const char *HTTP_PROXY_HOST_KEY = "persist.netmanager_base.http_proxy.host";
62 static constexpr const char *HTTP_PROXY_PORT_KEY = "persist.netmanager_base.http_proxy.port";
63 static constexpr const char *HTTP_PROXY_EXCLUSIONS_KEY = "persist.netmanager_base.http_proxy.exclusion_list";
64 #endif
AddCurlHandle(CURL * handle,RequestContext * context)65 bool HttpExec::AddCurlHandle(CURL *handle, RequestContext *context)
66 {
67 if (handle == nullptr || staticVariable_.curlMulti == nullptr) {
68 NETSTACK_LOGE("handle nullptr");
69 return false;
70 }
71
72 std::lock_guard guard(staticVariable_.curlMultiMutex);
73 staticVariable_.infoQueue.emplace(context, handle);
74 staticVariable_.conditionVariable.notify_one();
75 return true;
76 }
77
78 HttpExec::StaticVariable HttpExec::staticVariable_; /* NOLINT */
79
RequestWithoutCache(RequestContext * context)80 bool HttpExec::RequestWithoutCache(RequestContext *context)
81 {
82 if (!staticVariable_.initialized) {
83 NETSTACK_LOGE("curl not init");
84 return false;
85 }
86
87 auto handle = curl_easy_init();
88 if (!handle) {
89 NETSTACK_LOGE("Failed to create fetch task");
90 return false;
91 }
92
93 std::vector<std::string> vec;
94 std::for_each(context->options.GetHeader().begin(), context->options.GetHeader().end(),
95 [&vec](const std::pair<std::string, std::string> &p) {
96 vec.emplace_back(p.first + HttpConstant::HTTP_HEADER_SEPARATOR + p.second);
97 });
98 context->SetCurlHeaderList(MakeHeaders(vec));
99
100 if (!SetOption(handle, context, context->GetCurlHeaderList())) {
101 NETSTACK_LOGE("set option failed");
102 return false;
103 }
104
105 context->response.SetRequestTime(HttpTime::GetNowTimeGMT());
106
107 if (!AddCurlHandle(handle, context)) {
108 NETSTACK_LOGE("add handle failed");
109 return false;
110 }
111
112 return true;
113 }
114
GetCurlDataFromHandle(CURL * handle,RequestContext * context,CURLMSG curlMsg,CURLcode result)115 bool HttpExec::GetCurlDataFromHandle(CURL *handle, RequestContext *context, CURLMSG curlMsg, CURLcode result)
116 {
117 if (curlMsg != CURLMSG_DONE) {
118 NETSTACK_LOGE("CURLMSG %{public}s", std::to_string(curlMsg).c_str());
119 context->SetErrorCode(NapiUtils::NETSTACK_NAPI_INTERNAL_ERROR);
120 return false;
121 }
122
123 if (result != CURLE_OK) {
124 context->SetErrorCode(result);
125 NETSTACK_LOGE("CURLcode result %{public}s", std::to_string(result).c_str());
126 return false;
127 }
128
129 context->response.SetResponseTime(HttpTime::GetNowTimeGMT());
130
131 int64_t responseCode;
132 CURLcode code = curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &responseCode);
133 if (code != CURLE_OK) {
134 context->SetErrorCode(code);
135 return false;
136 }
137 context->response.SetResponseCode(responseCode);
138 if (context->response.GetResponseCode() == static_cast<uint32_t>(ResponseCode::NOT_MODIFIED)) {
139 NETSTACK_LOGI("cache is NOT_MODIFIED, we use the cache");
140 context->SetResponseByCache();
141 return true;
142 }
143 NETSTACK_LOGD("responseCode is %{public}s", std::to_string(responseCode).c_str());
144
145 struct curl_slist *cookies = nullptr;
146 code = curl_easy_getinfo(handle, CURLINFO_COOKIELIST, &cookies);
147 if (code != CURLE_OK) {
148 context->SetErrorCode(code);
149 return false;
150 }
151
152 std::unique_ptr<struct curl_slist, decltype(&curl_slist_free_all)> cookiesHandle(cookies, curl_slist_free_all);
153 while (cookies) {
154 context->response.AppendCookies(cookies->data, strlen(cookies->data));
155 if (cookies->next != nullptr) {
156 context->response.AppendCookies(HttpConstant::HTTP_LINE_SEPARATOR,
157 strlen(HttpConstant::HTTP_LINE_SEPARATOR));
158 }
159 cookies = cookies->next;
160 }
161 context->response.ParseHeaders();
162
163 return true;
164 }
165
HandleCurlData(CURLMsg * msg)166 void HttpExec::HandleCurlData(CURLMsg *msg)
167 {
168 if (msg == nullptr) {
169 return;
170 }
171
172 auto handle = msg->easy_handle;
173 if (handle == nullptr) {
174 return;
175 }
176
177 auto it = staticVariable_.contextMap.find(handle);
178 if (it == staticVariable_.contextMap.end()) {
179 NETSTACK_LOGE("can not find context");
180 return;
181 }
182
183 auto context = it->second;
184 staticVariable_.contextMap.erase(it);
185 if (context == nullptr) {
186 NETSTACK_LOGE("can not find context");
187 return;
188 }
189
190 NETSTACK_LOGI("priority = %{public}d", context->options.GetPriority());
191 context->SetExecOK(GetCurlDataFromHandle(handle, context, msg->msg, msg->data.result));
192 if (context->IsExecOK()) {
193 CacheProxy proxy(context->options);
194 proxy.WriteResponseToCache(context->response);
195 }
196
197 if (context->GetManager() == nullptr) {
198 NETSTACK_LOGE("can not find context manager");
199 return;
200 }
201
202 if (context->GetManager()->IsManagerValid()) {
203 if (context->IsRequest2()) {
204 if (context->IsExecOK()) {
205 NapiUtils::CreateUvQueueWorkEnhanced(context->GetEnv(), context, OnDataEnd);
206 }
207 NapiUtils::CreateUvQueueWorkEnhanced(context->GetEnv(), context, HttpAsyncWork::Request2Callback);
208 } else {
209 NapiUtils::CreateUvQueueWorkEnhanced(context->GetEnv(), context, HttpAsyncWork::RequestCallback);
210 }
211 }
212 }
213
ExecRequest(RequestContext * context)214 bool HttpExec::ExecRequest(RequestContext *context)
215 {
216 if (!CommonUtils::HasInternetPermission()) {
217 context->SetPermissionDenied(true);
218 return false;
219 }
220
221 context->options.SetRequestTime(HttpTime::GetNowTimeGMT());
222 CacheProxy proxy(context->options);
223 if (context->IsUsingCache() && proxy.ReadResponseFromCache(context)) {
224 return true;
225 }
226
227 if (!RequestWithoutCache(context)) {
228 context->SetErrorCode(NapiUtils::NETSTACK_NAPI_INTERNAL_ERROR);
229 if (context->GetManager()->IsManagerValid()) {
230 if (context->IsRequest2()) {
231 NapiUtils::CreateUvQueueWorkEnhanced(context->GetEnv(), context, HttpAsyncWork::Request2Callback);
232 } else {
233 NapiUtils::CreateUvQueueWorkEnhanced(context->GetEnv(), context, HttpAsyncWork::RequestCallback);
234 }
235 }
236 return false;
237 }
238
239 return true;
240 }
241
RequestCallback(RequestContext * context)242 napi_value HttpExec::RequestCallback(RequestContext *context)
243 {
244 napi_value object = NapiUtils::CreateObject(context->GetEnv());
245 if (NapiUtils::GetValueType(context->GetEnv(), object) != napi_object) {
246 return nullptr;
247 }
248
249 NapiUtils::SetUint32Property(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESPONSE_CODE,
250 context->response.GetResponseCode());
251 NapiUtils::SetStringPropertyUtf8(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_COOKIES,
252 context->response.GetCookies());
253
254 napi_value header = MakeResponseHeader(context);
255 if (NapiUtils::GetValueType(context->GetEnv(), header) == napi_object) {
256 OnHeaderReceive(context, header);
257 NapiUtils::SetNamedProperty(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_HEADER, header);
258 }
259
260 if (context->options.GetHttpDataType() != HttpDataType::NO_DATA_TYPE && ProcByExpectDataType(object, context)) {
261 return object;
262 }
263
264 auto contentType = CommonUtils::ToLower(const_cast<std::map<std::string, std::string> &>(
265 context->response.GetHeader())[HttpConstant::HTTP_CONTENT_TYPE]);
266 if (contentType.find(HttpConstant::HTTP_CONTENT_TYPE_OCTET_STREAM) != std::string::npos ||
267 contentType.find(HttpConstant::HTTP_CONTENT_TYPE_IMAGE) != std::string::npos) {
268 void *data = nullptr;
269 auto body = context->response.GetResult();
270 napi_value arrayBuffer = NapiUtils::CreateArrayBuffer(context->GetEnv(), body.size(), &data);
271 if (data != nullptr && arrayBuffer != nullptr) {
272 if (memcpy_s(data, body.size(), body.c_str(), body.size()) != EOK) {
273 NETSTACK_LOGE("memcpy_s failed!");
274 return object;
275 }
276 NapiUtils::SetNamedProperty(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT, arrayBuffer);
277 }
278 NapiUtils::SetUint32Property(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT_TYPE,
279 static_cast<uint32_t>(HttpDataType::ARRAY_BUFFER));
280 return object;
281 }
282
283 /* now just support utf8 */
284 NapiUtils::SetStringPropertyUtf8(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT,
285 context->response.GetResult());
286 NapiUtils::SetUint32Property(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT_TYPE,
287 static_cast<uint32_t>(HttpDataType::STRING));
288 return object;
289 }
290
Request2Callback(OHOS::NetStack::RequestContext * context)291 napi_value HttpExec::Request2Callback(OHOS::NetStack::RequestContext *context)
292 {
293 napi_value number = NapiUtils::CreateUint32(context->GetEnv(), context->response.GetResponseCode());
294 if (NapiUtils::GetValueType(context->GetEnv(), number) != napi_number) {
295 return nullptr;
296 }
297 return number;
298 }
299
MakeUrl(const std::string & url,std::string param,const std::string & extraParam)300 std::string HttpExec::MakeUrl(const std::string &url, std::string param, const std::string &extraParam)
301 {
302 if (param.empty()) {
303 param += extraParam;
304 } else {
305 param += HttpConstant::HTTP_URL_PARAM_SEPARATOR;
306 param += extraParam;
307 }
308
309 if (param.empty()) {
310 return url;
311 }
312
313 return url + HttpConstant::HTTP_URL_PARAM_START + param;
314 }
315
MethodForGet(const std::string & method)316 bool HttpExec::MethodForGet(const std::string &method)
317 {
318 return (method == HttpConstant::HTTP_METHOD_HEAD || method == HttpConstant::HTTP_METHOD_OPTIONS ||
319 method == HttpConstant::HTTP_METHOD_DELETE || method == HttpConstant::HTTP_METHOD_TRACE ||
320 method == HttpConstant::HTTP_METHOD_GET || method == HttpConstant::HTTP_METHOD_CONNECT);
321 }
322
MethodForPost(const std::string & method)323 bool HttpExec::MethodForPost(const std::string &method)
324 {
325 return (method == HttpConstant::HTTP_METHOD_POST || method == HttpConstant::HTTP_METHOD_PUT);
326 }
327
EncodeUrlParam(std::string & str)328 bool HttpExec::EncodeUrlParam(std::string &str)
329 {
330 char encoded[4];
331 std::string encodeOut;
332 size_t length = strlen(str.c_str());
333 for (size_t i = 0; i < length; ++i) {
334 auto c = static_cast<uint8_t>(str.c_str()[i]);
335 if (IsUnReserved(c)) {
336 encodeOut += static_cast<char>(c);
337 } else {
338 if (sprintf_s(encoded, sizeof(encoded), "%%%02X", c) < 0) {
339 return false;
340 }
341 encodeOut += encoded;
342 }
343 }
344
345 if (str == encodeOut) {
346 return false;
347 }
348 str = encodeOut;
349 return true;
350 }
351
AddRequestInfo()352 void HttpExec::AddRequestInfo()
353 {
354 std::lock_guard guard(staticVariable_.curlMultiMutex);
355 int num = 0;
356 while (!staticVariable_.infoQueue.empty()) {
357 if (!staticVariable_.runThread || staticVariable_.curlMulti == nullptr) {
358 break;
359 }
360
361 auto info = staticVariable_.infoQueue.top();
362 staticVariable_.infoQueue.pop();
363 auto ret = curl_multi_add_handle(staticVariable_.curlMulti, info.handle);
364 if (ret == CURLM_OK) {
365 staticVariable_.contextMap[info.handle] = info.context;
366 }
367
368 ++num;
369 if (num >= CURL_HANDLE_NUM) {
370 break;
371 }
372 }
373 }
374
RunThread()375 void HttpExec::RunThread()
376 {
377 while (staticVariable_.runThread && staticVariable_.curlMulti != nullptr) {
378 AddRequestInfo();
379 SendRequest();
380 ReadResponse();
381 std::this_thread::sleep_for(std::chrono::milliseconds(CURL_TIMEOUT_MS));
382
383 std::unique_lock l(staticVariable_.curlMultiMutex);
384 staticVariable_.conditionVariable.wait_for(l, std::chrono::seconds(CONDITION_TIMEOUT_S), [] {
385 return !staticVariable_.infoQueue.empty() || !staticVariable_.contextMap.empty();
386 });
387 }
388 }
389
SendRequest()390 void HttpExec::SendRequest()
391 {
392 std::lock_guard guard(staticVariable_.curlMultiMutex);
393
394 int runningHandle = 0;
395 int num = 0;
396 do {
397 if (!staticVariable_.runThread || staticVariable_.curlMulti == nullptr) {
398 break;
399 }
400
401 auto ret = curl_multi_perform(staticVariable_.curlMulti, &runningHandle);
402
403 if (runningHandle > 0) {
404 ret = curl_multi_poll(staticVariable_.curlMulti, nullptr, 0, CURL_MAX_WAIT_MSECS, nullptr);
405 }
406
407 if (ret != CURLM_OK) {
408 return;
409 }
410
411 ++num;
412 if (num >= CURL_HANDLE_NUM) {
413 break;
414 }
415 } while (runningHandle > 0);
416 }
417
ReadResponse()418 void HttpExec::ReadResponse()
419 {
420 std::lock_guard guard(staticVariable_.curlMultiMutex);
421 CURLMsg *msg = nullptr; /* NOLINT */
422 do {
423 if (!staticVariable_.runThread || staticVariable_.curlMulti == nullptr) {
424 break;
425 }
426
427 int leftMsg;
428 msg = curl_multi_info_read(staticVariable_.curlMulti, &leftMsg);
429 if (msg) {
430 if (msg->msg == CURLMSG_DONE) {
431 HandleCurlData(msg);
432 }
433 if (msg->easy_handle) {
434 (void)curl_multi_remove_handle(staticVariable_.curlMulti, msg->easy_handle);
435 (void)curl_easy_cleanup(msg->easy_handle);
436 }
437 }
438 } while (msg);
439 }
440
GetGlobalHttpProxyInfo(std::string & host,int32_t & port,std::string & exclusions)441 void HttpExec::GetGlobalHttpProxyInfo(std::string &host, int32_t &port, std::string &exclusions)
442 {
443 #ifdef HTTP_PROXY_ENABLE
444 char httpProxyHost[SYSPARA_MAX_SIZE] = {0};
445 char httpProxyPort[SYSPARA_MAX_SIZE] = {0};
446 char httpProxyExclusions[SYSPARA_MAX_SIZE] = {0};
447 GetParameter(HTTP_PROXY_HOST_KEY, DEFAULT_HTTP_PROXY_HOST, httpProxyHost, sizeof(httpProxyHost));
448 GetParameter(HTTP_PROXY_PORT_KEY, DEFAULT_HTTP_PROXY_PORT, httpProxyPort, sizeof(httpProxyPort));
449 GetParameter(HTTP_PROXY_EXCLUSIONS_KEY, DEFAULT_HTTP_PROXY_EXCLUSION_LIST, httpProxyExclusions,
450 sizeof(httpProxyExclusions));
451
452 host = Base64::Decode(httpProxyHost);
453 if (host == DEFAULT_HTTP_PROXY_HOST) {
454 host = std::string();
455 }
456 exclusions = httpProxyExclusions;
457 if (exclusions == DEFAULT_HTTP_PROXY_EXCLUSION_LIST) {
458 exclusions = std::string();
459 }
460
461 port = std::atoi(httpProxyPort);
462 #endif
463 }
464
Initialize()465 bool HttpExec::Initialize()
466 {
467 std::lock_guard<std::mutex> lock(staticVariable_.curlMultiMutex);
468 if (staticVariable_.initialized) {
469 return true;
470 }
471 NETSTACK_LOGI("call curl_global_init");
472 if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
473 NETSTACK_LOGE("Failed to initialize 'curl'");
474 return false;
475 }
476
477 staticVariable_.curlMulti = curl_multi_init();
478 if (staticVariable_.curlMulti == nullptr) {
479 NETSTACK_LOGE("Failed to initialize 'curl_multi'");
480 return false;
481 }
482
483 staticVariable_.workThread = std::thread(RunThread);
484
485 staticVariable_.initialized = true;
486 return staticVariable_.initialized;
487 }
488
SetOption(CURL * curl,RequestContext * context,struct curl_slist * requestHeader)489 bool HttpExec::SetOption(CURL *curl, RequestContext *context, struct curl_slist *requestHeader)
490 {
491 const std::string &method = context->options.GetMethod();
492 if (!MethodForGet(method) && !MethodForPost(method)) {
493 NETSTACK_LOGE("method %{public}s not supported", method.c_str());
494 return false;
495 }
496
497 if (context->options.GetMethod() == HttpConstant::HTTP_METHOD_HEAD) {
498 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_NOBODY, 1L, context);
499 }
500
501 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_URL, context->options.GetUrl().c_str(), context);
502 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_CUSTOMREQUEST, method.c_str(), context);
503
504 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_FORBID_REUSE, 1L, context);
505
506 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_XFERINFOFUNCTION, ProgressCallback, context);
507 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_XFERINFODATA, context, context);
508 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_NOPROGRESS, 0L, context);
509
510 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_WRITEFUNCTION, OnWritingMemoryBody, context);
511 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_WRITEDATA, context, context);
512
513 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_HEADERFUNCTION, OnWritingMemoryHeader, context);
514 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_HEADERDATA, context, context);
515
516 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_HTTPHEADER, requestHeader, context);
517
518 // Some servers don't like requests that are made without a user-agent field, so we provide one
519 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_USERAGENT, HttpConstant::HTTP_DEFAULT_USER_AGENT, context);
520
521 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_FOLLOWLOCATION, 1L, context);
522
523 /* first #undef CURL_DISABLE_COOKIES in curl config */
524 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_COOKIEFILE, "", context);
525
526 std::string host, exclusions;
527 int32_t port = 0;
528 if (context->options.GetUsingHttpProxyType() == UsingHttpProxyType::USE_DEFAULT) {
529 GetGlobalHttpProxyInfo(host, port, exclusions);
530 } else if (context->options.GetUsingHttpProxyType() == UsingHttpProxyType::USE_SPECIFIED) {
531 context->options.GetSpecifiedHttpProxy(host, port, exclusions);
532 }
533 if (!host.empty()) {
534 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_PROXY, host.c_str(), context);
535 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_PROXYPORT, port, context);
536 if (!exclusions.empty()) {
537 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_NOPROXY, exclusions.c_str(), context);
538 }
539 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP, context);
540 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_HTTPPROXYTUNNEL, 1L, context);
541 }
542 #ifdef NETSTACK_PROXY_PASS
543 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_PROXYUSERPWD, NETSTACK_PROXY_PASS, context);
544 #endif // NETSTACK_PROXY_PASS
545
546 #if NO_SSL_CERTIFICATION
547 // in real life, you should buy a ssl certification and rename it to /etc/ssl/cert.pem
548 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_VERIFYHOST, 0L, context);
549 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_VERIFYPEER, 0L, context);
550 #else
551 #ifndef WINDOWS_PLATFORM
552 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_CAINFO, HttpConstant::HTTP_DEFAULT_CA_PATH, context);
553 #endif // WINDOWS_PLATFORM
554 #endif // NO_SSL_CERTIFICATION
555
556 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_NOSIGNAL, 1L, context);
557 #if HTTP_CURL_PRINT_VERBOSE
558 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_VERBOSE, 1L, context);
559 #endif
560 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_TIMEOUT_MS, context->options.GetReadTimeout(), context);
561 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_CONNECTTIMEOUT_MS, context->options.GetConnectTimeout(), context);
562 #ifndef WINDOWS_PLATFORM
563 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_ACCEPT_ENCODING, HttpConstant::HTTP_CONTENT_ENCODING_GZIP, context);
564 #endif
565
566 if (MethodForPost(method) && !context->options.GetBody().empty()) {
567 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_POST, 1L, context);
568 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_POSTFIELDS, context->options.GetBody().c_str(), context);
569 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_POSTFIELDSIZE, context->options.GetBody().size(), context);
570 }
571
572 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_HTTP_VERSION, context->options.GetHttpVersion(), context);
573
574 return true;
575 }
576
OnWritingMemoryBody(const void * data,size_t size,size_t memBytes,void * userData)577 size_t HttpExec::OnWritingMemoryBody(const void *data, size_t size, size_t memBytes, void *userData)
578 {
579 auto context = static_cast<RequestContext *>(userData);
580 if (context == nullptr) {
581 return 0;
582 }
583 if (context->IsRequest2()) {
584 context->SetTempData(data, size * memBytes);
585 NapiUtils::CreateUvQueueWorkEnhanced(context->GetEnv(), context, OnDataReceive);
586 return size * memBytes;
587 }
588 if (context->response.GetResult().size() > MAX_LIMIT) {
589 return 0;
590 }
591 context->response.AppendResult(data, size * memBytes);
592 return size * memBytes;
593 }
594
OnWritingMemoryHeader(const void * data,size_t size,size_t memBytes,void * userData)595 size_t HttpExec::OnWritingMemoryHeader(const void *data, size_t size, size_t memBytes, void *userData)
596 {
597 auto context = static_cast<RequestContext *>(userData);
598 if (context == nullptr) {
599 return 0;
600 }
601 if (context->response.GetResult().size() > MAX_LIMIT) {
602 return 0;
603 }
604 context->response.AppendRawHeader(data, size * memBytes);
605 return size * memBytes;
606 }
607
OnDataReceive(napi_env env,napi_status status,void * data)608 void HttpExec::OnDataReceive(napi_env env, napi_status status, void *data)
609 {
610 auto context = static_cast<RequestContext *>(data);
611 if (context == nullptr) {
612 return;
613 }
614
615 void *buffer = nullptr;
616 auto tempData = context->GetTempData();
617 context->PopTempData();
618 if (tempData.empty()) {
619 NETSTACK_LOGI("[GetTempData] tempDate is empty!");
620 return;
621 }
622 napi_value arrayBuffer = NapiUtils::CreateArrayBuffer(context->GetEnv(), tempData.size(), &buffer);
623 if (buffer == nullptr || arrayBuffer == nullptr) {
624 return;
625 }
626 if (memcpy_s(buffer, tempData.size(), tempData.data(), tempData.size()) != EOK) {
627 NETSTACK_LOGE("[CreateArrayBuffer] memory copy failed");
628 return;
629 }
630 context->Emit(ON_DATA_RECEIVE, std::make_pair(NapiUtils::GetUndefined(context->GetEnv()), arrayBuffer));
631 }
632
OnDataProgress(napi_env env,napi_status status,void * data)633 void HttpExec::OnDataProgress(napi_env env, napi_status status, void *data)
634 {
635 auto context = static_cast<RequestContext *>(data);
636 if (context == nullptr) {
637 return;
638 }
639 auto progress = NapiUtils::CreateObject(context->GetEnv());
640 if (NapiUtils::GetValueType(context->GetEnv(), progress) == napi_undefined) {
641 return;
642 }
643 NapiUtils::SetUint32Property(context->GetEnv(), progress, "receiveSize",
644 static_cast<uint32_t>(context->GetNowLen()));
645 context->PopNowLen();
646 NapiUtils::SetUint32Property(context->GetEnv(), progress, "totalSize",
647 static_cast<uint32_t>(context->GetTotalLen()));
648 context->PopTotalLen();
649 context->Emit(ON_DATA_PROGRESS, std::make_pair(NapiUtils::GetUndefined(context->GetEnv()), progress));
650 }
651
OnDataEnd(napi_env env,napi_status status,void * data)652 void HttpExec::OnDataEnd(napi_env env, napi_status status, void *data)
653 {
654 auto context = static_cast<RequestContext *>(data);
655 if (context == nullptr) {
656 return;
657 }
658 auto undefined = NapiUtils::GetUndefined(context->GetEnv());
659 context->Emit(ON_DATA_END, std::make_pair(undefined, undefined));
660 }
661
ProgressCallback(void * userData,curl_off_t dltotal,curl_off_t dlnow,curl_off_t ultotal,curl_off_t ulnow)662 int HttpExec::ProgressCallback(void *userData, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal,
663 curl_off_t ulnow)
664 {
665 (void)ultotal;
666 (void)ulnow;
667 auto context = static_cast<RequestContext *>(userData);
668 if (context == nullptr || !context->IsRequest2()) {
669 return 0;
670 }
671 context->SetTotalLen(dltotal);
672 context->SetNowLen(dlnow);
673 if (dlnow != 0) {
674 NapiUtils::CreateUvQueueWorkEnhanced(context->GetEnv(), context, OnDataProgress);
675 }
676 return 0;
677 }
678
MakeHeaders(const std::vector<std::string> & vec)679 struct curl_slist *HttpExec::MakeHeaders(const std::vector<std::string> &vec)
680 {
681 struct curl_slist *header = nullptr;
682 std::for_each(vec.begin(), vec.end(), [&header](const std::string &s) {
683 if (!s.empty()) {
684 header = curl_slist_append(header, s.c_str());
685 }
686 });
687 return header;
688 }
689
MakeResponseHeader(RequestContext * context)690 napi_value HttpExec::MakeResponseHeader(RequestContext *context)
691 {
692 napi_value header = NapiUtils::CreateObject(context->GetEnv());
693 if (NapiUtils::GetValueType(context->GetEnv(), header) == napi_object) {
694 std::for_each(context->response.GetHeader().begin(), context->response.GetHeader().end(),
695 [context, header](const std::pair<std::string, std::string> &p) {
696 if (!p.first.empty() && !p.second.empty()) {
697 NapiUtils::SetStringPropertyUtf8(context->GetEnv(), header, p.first, p.second);
698 }
699 });
700 }
701 return header;
702 }
703
OnHeaderReceive(RequestContext * context,napi_value header)704 void HttpExec::OnHeaderReceive(RequestContext *context, napi_value header)
705 {
706 napi_value undefined = NapiUtils::GetUndefined(context->GetEnv());
707 context->Emit(ON_HEADER_RECEIVE, std::make_pair(undefined, header));
708 context->Emit(ON_HEADERS_RECEIVE, std::make_pair(undefined, header));
709 }
710
IsUnReserved(unsigned char in)711 bool HttpExec::IsUnReserved(unsigned char in)
712 {
713 if ((in >= '0' && in <= '9') || (in >= 'a' && in <= 'z') || (in >= 'A' && in <= 'Z')) {
714 return true;
715 }
716 switch (in) {
717 case '-':
718 case '.':
719 case '_':
720 case '~':
721 return true;
722 default:
723 break;
724 }
725 return false;
726 }
727
ProcByExpectDataType(napi_value object,RequestContext * context)728 bool HttpExec::ProcByExpectDataType(napi_value object, RequestContext *context)
729 {
730 switch (context->options.GetHttpDataType()) {
731 case HttpDataType::STRING: {
732 NapiUtils::SetStringPropertyUtf8(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT,
733 context->response.GetResult());
734 NapiUtils::SetUint32Property(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT_TYPE,
735 static_cast<uint32_t>(HttpDataType::STRING));
736 return true;
737 }
738 case HttpDataType::OBJECT: {
739 if (context->response.GetResult().size() > HttpConstant::MAX_JSON_PARSE_SIZE) {
740 return false;
741 }
742
743 napi_value obj = NapiUtils::JsonParse(context->GetEnv(), context->response.GetResult());
744 if (obj) {
745 NapiUtils::SetNamedProperty(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT, obj);
746 NapiUtils::SetUint32Property(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT_TYPE,
747 static_cast<uint32_t>(HttpDataType::OBJECT));
748 return true;
749 }
750
751 // parse maybe failed
752 return false;
753 }
754 case HttpDataType::ARRAY_BUFFER: {
755 void *data = nullptr;
756 auto body = context->response.GetResult();
757 napi_value arrayBuffer = NapiUtils::CreateArrayBuffer(context->GetEnv(), body.size(), &data);
758 if (data != nullptr && arrayBuffer != nullptr) {
759 if (memcpy_s(data, body.size(), body.c_str(), body.size()) < 0) {
760 NETSTACK_LOGE("[ProcByExpectDataType] memory copy failed");
761 return true;
762 }
763 NapiUtils::SetNamedProperty(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT, arrayBuffer);
764 NapiUtils::SetUint32Property(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT_TYPE,
765 static_cast<uint32_t>(HttpDataType::ARRAY_BUFFER));
766 }
767 return true;
768 }
769 default:
770 break;
771 }
772 return false;
773 }
774
775 #ifndef MAC_PLATFORM
AsyncRunRequest(RequestContext * context)776 void HttpExec::AsyncRunRequest(RequestContext *context)
777 {
778 HttpAsyncWork::ExecRequest(context->GetEnv(), context);
779 }
780 #endif
781
IsInitialized()782 bool HttpExec::IsInitialized()
783 {
784 return staticVariable_.initialized;
785 }
786
DeInitialize()787 void HttpExec::DeInitialize()
788 {
789 std::lock_guard<std::mutex> lock(staticVariable_.curlMultiMutex);
790 staticVariable_.runThread = false;
791 staticVariable_.conditionVariable.notify_all();
792 if (staticVariable_.workThread.joinable()) {
793 staticVariable_.workThread.join();
794 }
795 if (staticVariable_.curlMulti) {
796 curl_multi_cleanup(staticVariable_.curlMulti);
797 }
798 staticVariable_.initialized = false;
799 }
800
ExecFlush(BaseContext * context)801 bool HttpResponseCacheExec::ExecFlush(BaseContext *context)
802 {
803 (void)context;
804 CacheProxy::FlushCache();
805 return true;
806 }
807
FlushCallback(BaseContext * context)808 napi_value HttpResponseCacheExec::FlushCallback(BaseContext *context)
809 {
810 return NapiUtils::GetUndefined(context->GetEnv());
811 }
812
ExecDelete(BaseContext * context)813 bool HttpResponseCacheExec::ExecDelete(BaseContext *context)
814 {
815 (void)context;
816 CacheProxy::StopCacheAndDelete();
817 return true;
818 }
819
DeleteCallback(BaseContext * context)820 napi_value HttpResponseCacheExec::DeleteCallback(BaseContext *context)
821 {
822 return NapiUtils::GetUndefined(context->GetEnv());
823 }
824 } // namespace OHOS::NetStack
825