• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2024 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 "http_exec.h"
17 
18 #include <cstddef>
19 #include <cstring>
20 #include <memory>
21 #include <pthread.h>
22 #include <sstream>
23 #include <thread>
24 #include <unistd.h>
25 #ifdef HTTP_MULTIPATH_CERT_ENABLE
26 #include <openssl/ssl.h>
27 #endif
28 #ifdef HTTP_ONLY_VERIFY_ROOT_CA_ENABLE
29 #ifndef HTTP_MULTIPATH_CERT_ENABLE
30 #include <openssl/ssl.h>
31 #endif
32 #include <openssl/pem.h>
33 #include <openssl/sha.h>
34 #include <openssl/x509.h>
35 #endif
36 #if HAS_NETMANAGER_BASE
37 #include <netdb.h>
38 #endif
39 
40 #ifdef HTTP_PROXY_ENABLE
41 #include "parameter.h"
42 #endif
43 #ifdef HAS_NETMANAGER_BASE
44 #include "http_proxy.h"
45 #include "net_conn_client.h"
46 #include "netsys_client.h"
47 #endif
48 #include "base64_utils.h"
49 #include "cache_proxy.h"
50 #include "constant.h"
51 #if HAS_NETMANAGER_BASE
52 #include "epoll_request_handler.h"
53 #endif
54 #include "event_list.h"
55 #if HAS_NETMANAGER_BASE
56 #include "hitrace_meter.h"
57 #endif
58 #include "http_async_work.h"
59 #include "http_time.h"
60 #include "napi_utils.h"
61 #include "netstack_common_utils.h"
62 #include "netstack_log.h"
63 #include "secure_char.h"
64 #include "securec.h"
65 
66 #define NETSTACK_CURL_EASY_SET_OPTION(handle, opt, data, asyncContext)                                   \
67     do {                                                                                                 \
68         CURLcode result = curl_easy_setopt(handle, opt, data);                                           \
69         if (result != CURLE_OK) {                                                                        \
70             const char *err = curl_easy_strerror(result);                                                \
71             NETSTACK_LOGE("Failed to set option: %{public}s, %{public}s %{public}d", #opt, err, result); \
72             (asyncContext)->SetErrorCode(result);                                                        \
73             return false;                                                                                \
74         }                                                                                                \
75     } while (0)
76 
77 namespace OHOS::NetStack::Http {
78 #if !HAS_NETMANAGER_BASE
79 static constexpr int CURL_TIMEOUT_MS = 20;
80 static constexpr int CONDITION_TIMEOUT_S = 3600;
81 static constexpr int CURL_MAX_WAIT_MSECS = 10;
82 static constexpr int CURL_HANDLE_NUM = 10;
83 #endif
84 static constexpr const uint32_t EVENT_PARAM_ZERO = 0;
85 static constexpr const uint32_t EVENT_PARAM_ONE = 1;
86 static constexpr const uint32_t EVENT_PARAM_TWO = 2;
87 static constexpr const char *TLS12_SECURITY_CIPHER_SUITE = R"(DEFAULT:!eNULL:!EXPORT)";
88 #if !HAS_NETMANAGER_BASE
89 static constexpr const char *HTTP_TASK_RUN_THREAD = "OS_NET_TaskHttp";
90 static constexpr const char *HTTP_CLIENT_TASK_THREAD = "OS_NET_HttpJs";
91 #endif
92 
93 #if HAS_NETMANAGER_BASE
94 static constexpr const char *HTTP_REQ_TRACE_NAME = "HttpRequest";
95 #endif
96 #ifdef HTTP_MULTIPATH_CERT_ENABLE
97 static constexpr const int32_t UID_TRANSFORM_DIVISOR = 200000;
98 static constexpr const char *BASE_PATH = "/data/certificates/user_cacerts/";
99 static constexpr const char *USER_CERT_ROOT_PATH = "/data/certificates/user_cacerts/0/";
100 static constexpr int32_t SYSPARA_MAX_SIZE = 128;
101 static constexpr const char *DEFAULT_HTTP_PROXY_HOST = "NONE";
102 static constexpr const char *DEFAULT_HTTP_PROXY_PORT = "0";
103 static constexpr const char *DEFAULT_HTTP_PROXY_EXCLUSION_LIST = "NONE";
104 static constexpr const char *HTTP_PROXY_HOST_KEY = "persist.netmanager_base.http_proxy.host";
105 static constexpr const char *HTTP_PROXY_PORT_KEY = "persist.netmanager_base.http_proxy.port";
106 static constexpr const char *HTTP_PROXY_EXCLUSIONS_KEY = "persist.netmanager_base.http_proxy.exclusion_list";
107 #endif
108 
109 #ifdef HTTP_ONLY_VERIFY_ROOT_CA_ENABLE
110 static constexpr const int SSL_CTX_EX_DATA_REQUEST_CONTEXT_INDEX = 1;
111 static constexpr unsigned int SHA256_LEN = 32;
112 static constexpr int SHA256_BASE64_LEN = 44;  // 32-byte base64 -> 44 bytes
113 #endif
114 
115 static constexpr const char *HTTP_AF_ONLYV4 = "ONLY_V4";
116 static constexpr const char *HTTP_AF_ONLYV6 = "ONLY_V6";
117 
RequestContextDeleter(RequestContext * context)118 static void RequestContextDeleter(RequestContext *context)
119 {
120     context->DeleteReference();
121     delete context;
122     context = nullptr;
123 }
124 
AsyncWorkRequestInStreamCallback(napi_env env,napi_status status,void * data)125 static void AsyncWorkRequestInStreamCallback(napi_env env, napi_status status, void *data)
126 {
127     if (status != napi_ok) {
128         return;
129     }
130     std::unique_ptr<RequestContext, decltype(&RequestContextDeleter)> context(static_cast<RequestContext *>(data),
131                                                                               RequestContextDeleter);
132     napi_value undefined = NapiUtils::GetUndefined(env);
133     napi_value argv[EVENT_PARAM_TWO] = {nullptr};
134     if (context->IsParseOK() && context->IsExecOK()) {
135         context->EmitSharedManager(ON_DATA_END, std::make_pair(undefined, undefined));
136         argv[EVENT_PARAM_ZERO] = undefined;
137         argv[EVENT_PARAM_ONE] = HttpExec::RequestInStreamCallback(context.get());
138         if (argv[EVENT_PARAM_ONE] == nullptr) {
139             return;
140         }
141     } else {
142         argv[EVENT_PARAM_ZERO] =
143             NapiUtils::CreateErrorMessage(env, context->GetErrorCode(), context->GetErrorMessage());
144         if (argv[EVENT_PARAM_ZERO] == nullptr) {
145             return;
146         }
147 
148         argv[EVENT_PARAM_ONE] = undefined;
149     }
150 
151     if (context->GetDeferred() != nullptr) {
152         if (context->IsExecOK()) {
153             napi_resolve_deferred(env, context->GetDeferred(), argv[EVENT_PARAM_ONE]);
154         } else {
155             napi_reject_deferred(env, context->GetDeferred(), argv[EVENT_PARAM_ZERO]);
156         }
157         return;
158     }
159     napi_value func = context->GetCallback();
160     if (NapiUtils::GetValueType(env, func) == napi_function) {
161         (void)NapiUtils::CallFunction(env, undefined, func, EVENT_PARAM_TWO, argv);
162     }
163 }
164 
AsyncWorkRequestCallback(napi_env env,napi_status status,void * data)165 static void AsyncWorkRequestCallback(napi_env env, napi_status status, void *data)
166 {
167     if (status != napi_ok) {
168         return;
169     }
170     std::unique_ptr<RequestContext, decltype(&RequestContextDeleter)> context(static_cast<RequestContext *>(data),
171                                                                               RequestContextDeleter);
172     napi_value argv[EVENT_PARAM_TWO] = {nullptr};
173     if (context->IsParseOK() && context->IsExecOK()) {
174         argv[EVENT_PARAM_ZERO] = NapiUtils::GetUndefined(env);
175         argv[EVENT_PARAM_ONE] = HttpExec::RequestCallback(context.get());
176         if (argv[EVENT_PARAM_ONE] == nullptr) {
177             return;
178         }
179     } else {
180         argv[EVENT_PARAM_ZERO] =
181             NapiUtils::CreateErrorMessage(env, context->GetErrorCode(), context->GetErrorMessage());
182         if (argv[EVENT_PARAM_ZERO] == nullptr) {
183             return;
184         }
185 
186         argv[EVENT_PARAM_ONE] = NapiUtils::GetUndefined(env);
187     }
188     napi_value undefined = NapiUtils::GetUndefined(env);
189     if (context->GetDeferred() != nullptr) {
190         if (context->IsExecOK()) {
191             napi_resolve_deferred(env, context->GetDeferred(), argv[EVENT_PARAM_ONE]);
192         } else {
193             napi_reject_deferred(env, context->GetDeferred(), argv[EVENT_PARAM_ZERO]);
194         }
195         return;
196     }
197     napi_value func = context->GetCallback();
198     if (NapiUtils::GetValueType(env, func) == napi_function) {
199         (void)NapiUtils::CallFunction(env, undefined, func, EVENT_PARAM_TWO, argv);
200     }
201 }
202 
AddCurlHandle(CURL * handle,RequestContext * context)203 bool HttpExec::AddCurlHandle(CURL *handle, RequestContext *context)
204 {
205 #if HAS_NETMANAGER_BASE
206     if (handle == nullptr) {
207 #else
208     if (handle == nullptr || staticVariable_.curlMulti == nullptr) {
209 #endif
210         NETSTACK_LOGE("handle nullptr");
211         return false;
212     }
213 
214 #if HAS_NETMANAGER_BASE
215     std::stringstream name;
216     name << HTTP_REQ_TRACE_NAME << "_" << std::this_thread::get_id();
217     context->SetTraceName(name.str());
218     StartAsyncTrace(HITRACE_TAG_NET, context->GetTraceName(), context->GetTaskId());
219     SetServerSSLCertOption(handle, context);
220 
221     static HttpOverCurl::EpollRequestHandler requestHandler;
222 
223     static auto startedCallback = +[](CURL *easyHandle, void *opaqueData) {
224         char *url = nullptr;
225         curl_easy_getinfo(easyHandle, CURLINFO_EFFECTIVE_URL, &url);
226     };
227 
228     static auto responseCallback = +[](CURLMsg *curlMessage, void *opaqueData) {
229         auto context = static_cast<RequestContext *>(opaqueData);
230         HttpExec::HandleCurlData(curlMessage, context);
231     };
232 
233     requestHandler.Process(handle, startedCallback, responseCallback, context);
234     return true;
235 #else
236     std::thread([context, handle] {
237         std::lock_guard guard(staticVariable_.curlMultiMutex);
238         // Do SetServerSSLCertOption here to avoid blocking the main thread.
239 #if defined(MAC_PLATFORM) || defined(IOS_PLATFORM)
240         pthread_setname_np(HTTP_CLIENT_TASK_THREAD);
241 #else
242         pthread_setname_np(pthread_self(), HTTP_CLIENT_TASK_THREAD);
243 #endif
244         SetServerSSLCertOption(handle, context);
245         staticVariable_.infoQueue.emplace(context, handle);
246         staticVariable_.conditionVariable.notify_all();
247     }).detach();
248 
249     return true;
250 #endif
251 }
252 
253 #if !HAS_NETMANAGER_BASE
254 HttpExec::StaticVariable HttpExec::staticVariable_; /* NOLINT */
255 #endif
256 
257 bool HttpExec::RequestWithoutCache(RequestContext *context)
258 {
259 #if !HAS_NETMANAGER_BASE
260     if (!staticVariable_.initialized) {
261         NETSTACK_LOGE("curl not init");
262         return false;
263     }
264 #endif
265 
266     auto handle = curl_easy_init();
267     if (!handle) {
268         NETSTACK_LOGE("Failed to create fetch task");
269         return false;
270     }
271 
272 #if HAS_NETMANAGER_BASE
273     NETSTACK_CURL_EASY_SET_OPTION(handle, CURLOPT_PRIVATE, context, context);
274 #endif
275 
276     std::vector<std::string> vec;
277     std::for_each(context->options.GetHeader().begin(), context->options.GetHeader().end(),
278                   [&vec](const std::pair<std::string, std::string> &p) {
279                       if (!p.second.empty()) {
280                           vec.emplace_back(p.first + HttpConstant::HTTP_HEADER_SEPARATOR + p.second);
281                       } else {
282                           vec.emplace_back(p.first + HttpConstant::HTTP_HEADER_BLANK_SEPARATOR);
283                       }
284                   });
285     context->SetCurlHeaderList(MakeHeaders(vec));
286 
287     if (!SetOption(handle, context, context->GetCurlHeaderList())) {
288         NETSTACK_LOGE("set option failed");
289         return false;
290     }
291 
292     context->response.SetRequestTime(HttpTime::GetNowTimeGMT());
293     context->SetCurlHandle(handle);
294 
295     if (!AddCurlHandle(handle, context)) {
296         NETSTACK_LOGE("add handle failed");
297         return false;
298     }
299 
300     return true;
301 }
302 
303 bool HttpExec::GetCurlDataFromHandle(CURL *handle, RequestContext *context, CURLMSG curlMsg, CURLcode result)
304 {
305     if (curlMsg != CURLMSG_DONE) {
306         NETSTACK_LOGE("taskid=%{public}d, CURLMSG %{public}s", context->GetTaskId(), std::to_string(curlMsg).c_str());
307         context->SetErrorCode(NapiUtils::NETSTACK_NAPI_INTERNAL_ERROR);
308         return false;
309     }
310 
311     if (result != CURLE_OK) {
312         context->SetErrorCode(result);
313         NETSTACK_LOGE("CURLcode result %{public}s", std::to_string(result).c_str());
314         return false;
315     }
316 
317     context->response.SetResponseTime(HttpTime::GetNowTimeGMT());
318 
319     int64_t responseCode;
320     CURLcode code = curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &responseCode);
321     if (code != CURLE_OK) {
322         context->SetErrorCode(code);
323         return false;
324     }
325     context->response.SetResponseCode(responseCode);
326     NETSTACK_LOGD("responseCode is %{public}s", std::to_string(responseCode).c_str());
327 
328     struct curl_slist *cookies = nullptr;
329     code = curl_easy_getinfo(handle, CURLINFO_COOKIELIST, &cookies);
330     if (code != CURLE_OK) {
331         context->SetErrorCode(code);
332         return false;
333     }
334 
335     std::unique_ptr<struct curl_slist, decltype(&curl_slist_free_all)> cookiesHandle(cookies, curl_slist_free_all);
336     while (cookies) {
337         context->response.AppendCookies(cookies->data, strlen(cookies->data));
338         if (cookies->next != nullptr) {
339             context->response.AppendCookies(HttpConstant::HTTP_LINE_SEPARATOR,
340                                             strlen(HttpConstant::HTTP_LINE_SEPARATOR));
341         }
342         cookies = cookies->next;
343     }
344     return true;
345 }
346 
347 double HttpExec::GetTimingFromCurl(CURL *handle, CURLINFO info)
348 {
349     time_t timing;
350     CURLcode result = curl_easy_getinfo(handle, info, &timing);
351     if (result != CURLE_OK) {
352         NETSTACK_LOGE("Failed to get timing: %{public}d, %{public}s", info, curl_easy_strerror(result));
353         return 0;
354     }
355     return Timing::TimeUtils::Microseconds2Milliseconds(timing);
356 }
357 
358 curl_off_t HttpExec::GetSizeFromCurl(CURL *handle, RequestContext *context)
359 {
360     auto info = CURLINFO_SIZE_DOWNLOAD_T;
361     auto method = context->options.GetMethod();
362     NETSTACK_LOGD("method is %{public}s", method.c_str());
363     if (MethodForPost(method)) {
364         info = CURLINFO_SIZE_UPLOAD_T;
365     }
366 
367     curl_off_t size = 0;
368     CURLcode result = curl_easy_getinfo(handle, info, &size);
369     if (result != CURLE_OK) {
370         NETSTACK_LOGE("Failed to get timing: %{public}d, %{public}s", info, curl_easy_strerror(result));
371         return 0;
372     }
373     return size;
374 }
375 
376 void HttpExec::CacheCurlPerformanceTiming(CURL *handle, RequestContext *context)
377 {
378     auto dnsTime = HttpExec::GetTimingFromCurl(handle, CURLINFO_NAMELOOKUP_TIME_T);
379     auto connectTime = HttpExec::GetTimingFromCurl(handle, CURLINFO_CONNECT_TIME_T);
380     auto tlsTime = HttpExec::GetTimingFromCurl(handle, CURLINFO_APPCONNECT_TIME_T);
381     auto firstSendTime = HttpExec::GetTimingFromCurl(handle, CURLINFO_PRETRANSFER_TIME_T);
382     auto firstRecvTime = HttpExec::GetTimingFromCurl(handle, CURLINFO_STARTTRANSFER_TIME_T);
383     auto totalTime = HttpExec::GetTimingFromCurl(handle, CURLINFO_TOTAL_TIME_T);
384     auto redirectTime = HttpExec::GetTimingFromCurl(handle, CURLINFO_REDIRECT_TIME_T);
385 
386     context->CachePerformanceTimingItem(HttpConstant::RESPONSE_DNS_TIMING, dnsTime);
387     context->CachePerformanceTimingItem(HttpConstant::RESPONSE_TCP_TIMING, connectTime);
388     context->CachePerformanceTimingItem(HttpConstant::RESPONSE_TLS_TIMING, tlsTime);
389     context->CachePerformanceTimingItem(HttpConstant::RESPONSE_FIRST_SEND_TIMING, firstSendTime);
390     context->CachePerformanceTimingItem(HttpConstant::RESPONSE_FIRST_RECEIVE_TIMING, firstRecvTime);
391     context->CachePerformanceTimingItem(HttpConstant::RESPONSE_TOTAL_FINISH_TIMING, totalTime);
392     context->CachePerformanceTimingItem(HttpConstant::RESPONSE_REDIRECT_TIMING, redirectTime);
393 
394     int64_t responseCode = 0;
395     (void)curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &responseCode);
396 
397     /*
398     CURL_HTTP_VERSION_NONE         0
399     CURL_HTTP_VERSION_1_0          1
400     CURL_HTTP_VERSION_1_1          2
401     CURL_HTTP_VERSION_2            3
402     */
403     int64_t httpVer = CURL_HTTP_VERSION_NONE;
404     (void)curl_easy_getinfo(handle, CURLINFO_HTTP_VERSION, &httpVer);
405 
406     NETSTACK_LOGI(
407         "taskid=%{public}d"
408         ", size:%{public}" CURL_FORMAT_CURL_OFF_T
409         ", dns:%{public}.3f"
410         ", connect:%{public}.3f"
411         ", tls:%{public}.3f"
412         ", firstSend:%{public}.3f"
413         ", firstRecv:%{public}.3f"
414         ", total:%{public}.3f"
415         ", redirect:%{public}.3f"
416         ", errCode:%{public}d"
417         ", RespCode:%{public}s"
418         ", httpVer:%{public}s"
419         ", method:%{public}s",
420         context->GetTaskId(), GetSizeFromCurl(handle, context), dnsTime, connectTime == 0 ? 0 : connectTime - dnsTime,
421         tlsTime == 0 ? 0 : tlsTime - connectTime,
422         firstSendTime == 0 ? 0 : firstSendTime - std::max({dnsTime, connectTime, tlsTime}),
423         firstRecvTime == 0 ? 0 : firstRecvTime - firstSendTime, totalTime, redirectTime,
424         context->IsExecOK() ? 0: context->GetErrorCode(), std::to_string(responseCode).c_str(),
425         std::to_string(httpVer).c_str(), context->options.GetMethod().c_str());
426 }
427 
428 #if HAS_NETMANAGER_BASE
429 void HttpExec::HandleCurlData(CURLMsg *msg, RequestContext *context)
430 #else
431 void HttpExec::HandleCurlData(CURLMsg *msg)
432 #endif
433 {
434     if (msg == nullptr) {
435         return;
436     }
437 
438     auto handle = msg->easy_handle;
439     if (handle == nullptr) {
440         return;
441     }
442 
443 #if !HAS_NETMANAGER_BASE
444     auto it = staticVariable_.contextMap.find(handle);
445     if (it == staticVariable_.contextMap.end()) {
446         NETSTACK_LOGE("can not find context");
447         return;
448     }
449 
450     auto context = it->second;
451     staticVariable_.contextMap.erase(it);
452     if (context == nullptr) {
453         NETSTACK_LOGE("can not find context");
454         return;
455     }
456 #endif
457     NETSTACK_LOGD("priority = %{public}d", context->options.GetPriority());
458     context->SetExecOK(GetCurlDataFromHandle(handle, context, msg->msg, msg->data.result));
459     CacheCurlPerformanceTiming(handle, context);
460     if (context->IsExecOK()) {
461         CacheProxy proxy(context->options);
462         proxy.WriteResponseToCache(context->response);
463     }
464     if (context->GetSharedManager() == nullptr) {
465         NETSTACK_LOGE("can not find context manager");
466         return;
467     }
468 #if HAS_NETMANAGER_BASE
469     FinishAsyncTrace(HITRACE_TAG_NET, context->GetTraceName(), context->GetTaskId());
470 #endif
471     context->SendNetworkProfiler();
472     if (handle) {
473         (void)curl_easy_cleanup(handle);
474     }
475     if (context->IsRequestInStream()) {
476         NapiUtils::CreateUvQueueWorkByModuleId(
477             context->GetEnv(), std::bind(AsyncWorkRequestInStreamCallback, context->GetEnv(), napi_ok, context),
478             context->GetModuleId());
479     } else {
480         NapiUtils::CreateUvQueueWorkByModuleId(context->GetEnv(),
481                                                std::bind(AsyncWorkRequestCallback, context->GetEnv(), napi_ok, context),
482                                                context->GetModuleId());
483     }
484 }
485 
486 bool HttpExec::ExecRequest(RequestContext *context)
487 {
488     if (!CommonUtils::HasInternetPermission()) {
489         context->SetPermissionDenied(true);
490         return false;
491     }
492     if (context->IsAtomicService() &&
493         !CommonUtils::IsAllowedHostname(context->GetBundleName(), CommonUtils::DOMAIN_TYPE_HTTP_REQUEST,
494                                         context->options.GetUrl())) {
495         context->SetNoAllowedHost(true);
496         return false;
497     }
498     if (context->GetSharedManager()->IsEventDestroy()) {
499         return false;
500     }
501     context->options.SetRequestTime(HttpTime::GetNowTimeGMT());
502     CacheProxy proxy(context->options);
503     if (context->IsUsingCache() && proxy.ReadResponseFromCache(context)) {
504         if (context->GetSharedManager()) {
505             if (context->IsRequestInStream()) {
506                 NapiUtils::CreateUvQueueWorkByModuleId(
507                     context->GetEnv(), std::bind(AsyncWorkRequestInStreamCallback, context->GetEnv(), napi_ok, context),
508                     context->GetModuleId());
509             } else {
510                 NapiUtils::CreateUvQueueWorkByModuleId(
511                     context->GetEnv(), std::bind(AsyncWorkRequestCallback, context->GetEnv(), napi_ok, context),
512                     context->GetModuleId());
513             }
514         }
515         return true;
516     }
517 
518     if (!RequestWithoutCache(context)) {
519         context->SetErrorCode(NapiUtils::NETSTACK_NAPI_INTERNAL_ERROR);
520         if (context->GetSharedManager()) {
521             if (context->IsRequestInStream()) {
522                 NapiUtils::CreateUvQueueWorkByModuleId(
523                     context->GetEnv(), std::bind(AsyncWorkRequestInStreamCallback, context->GetEnv(), napi_ok, context),
524                     context->GetModuleId());
525             } else {
526                 NapiUtils::CreateUvQueueWorkByModuleId(
527                     context->GetEnv(), std::bind(AsyncWorkRequestCallback, context->GetEnv(), napi_ok, context),
528                     context->GetModuleId());
529             }
530         }
531         return false;
532     }
533 
534     return true;
535 }
536 
537 napi_value HttpExec::BuildRequestCallback(RequestContext *context)
538 {
539     napi_value object = NapiUtils::CreateObject(context->GetEnv());
540     if (NapiUtils::GetValueType(context->GetEnv(), object) != napi_object) {
541         return nullptr;
542     }
543 
544     NapiUtils::SetUint32Property(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESPONSE_CODE,
545                                  context->response.GetResponseCode());
546     NapiUtils::SetStringPropertyUtf8(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_COOKIES,
547                                      context->response.GetCookies());
548 
549     napi_value header = MakeResponseHeader(context->GetEnv(), context);
550     if (NapiUtils::GetValueType(context->GetEnv(), header) == napi_object) {
551         NapiUtils::SetNamedProperty(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_HEADER, header);
552     }
553 
554     if (context->options.GetHttpDataType() != HttpDataType::NO_DATA_TYPE && ProcByExpectDataType(object, context)) {
555         return object;
556     }
557 
558     auto contentType = CommonUtils::ToLower(const_cast<std::map<std::string, std::string> &>(
559         context->response.GetHeader())[HttpConstant::HTTP_CONTENT_TYPE]);
560     if (contentType.find(HttpConstant::HTTP_CONTENT_TYPE_OCTET_STREAM) != std::string::npos ||
561         contentType.find(HttpConstant::HTTP_CONTENT_TYPE_IMAGE) != std::string::npos) {
562         void *data = nullptr;
563         auto body = context->response.GetResult();
564         napi_value arrayBuffer = NapiUtils::CreateArrayBuffer(context->GetEnv(), body.size(), &data);
565         if (data != nullptr && arrayBuffer != nullptr) {
566             if (memcpy_s(data, body.size(), body.c_str(), body.size()) != EOK) {
567                 NETSTACK_LOGE("memcpy_s failed!");
568                 return object;
569             }
570             NapiUtils::SetNamedProperty(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT, arrayBuffer);
571         }
572         NapiUtils::SetUint32Property(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT_TYPE,
573                                      static_cast<uint32_t>(HttpDataType::ARRAY_BUFFER));
574         return object;
575     }
576 
577     /* now just support utf8 */
578     NapiUtils::SetStringPropertyUtf8(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT,
579                                      context->response.GetResult());
580     NapiUtils::SetUint32Property(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT_TYPE,
581                                  static_cast<uint32_t>(HttpDataType::STRING));
582     return object;
583 }
584 
585 napi_value HttpExec::RequestCallback(RequestContext *context)
586 {
587     napi_value result = HttpExec::BuildRequestCallback(context);
588     context->StopAndCacheNapiPerformanceTiming(HttpConstant::RESPONSE_TOTAL_TIMING);
589     context->SetPerformanceTimingToResult(result);
590     return result;
591 }
592 
593 napi_value HttpExec::RequestInStreamCallback(OHOS::NetStack::Http::RequestContext *context)
594 {
595     napi_value number = NapiUtils::CreateUint32(context->GetEnv(), context->response.GetResponseCode());
596     if (NapiUtils::GetValueType(context->GetEnv(), number) != napi_number) {
597         return nullptr;
598     }
599     return number;
600 }
601 
602 std::string HttpExec::MakeUrl(const std::string &url, std::string param, const std::string &extraParam)
603 {
604     if (param.empty()) {
605         param += extraParam;
606     } else {
607         param += HttpConstant::HTTP_URL_PARAM_SEPARATOR;
608         param += extraParam;
609     }
610 
611     if (param.empty()) {
612         return url;
613     }
614 
615     return url + HttpConstant::HTTP_URL_PARAM_START + param;
616 }
617 
618 bool HttpExec::MethodForGet(const std::string &method)
619 {
620     return (method == HttpConstant::HTTP_METHOD_HEAD || method == HttpConstant::HTTP_METHOD_OPTIONS ||
621             method == HttpConstant::HTTP_METHOD_TRACE || method == HttpConstant::HTTP_METHOD_GET ||
622             method == HttpConstant::HTTP_METHOD_CONNECT);
623 }
624 
625 bool HttpExec::MethodForPost(const std::string &method)
626 {
627     return (method == HttpConstant::HTTP_METHOD_POST || method == HttpConstant::HTTP_METHOD_PUT ||
628             method == HttpConstant::HTTP_METHOD_DELETE || method.empty());
629 }
630 
631 bool HttpExec::EncodeUrlParam(std::string &str)
632 {
633     char encoded[4];
634     std::string encodeOut;
635     size_t length = strlen(str.c_str());
636     for (size_t i = 0; i < length; ++i) {
637         auto c = static_cast<uint8_t>(str.c_str()[i]);
638         if (IsUnReserved(c)) {
639             encodeOut += static_cast<char>(c);
640         } else {
641             if (sprintf_s(encoded, sizeof(encoded), "%%%02X", c) < 0) {
642                 return false;
643             }
644             encodeOut += encoded;
645         }
646     }
647 
648     if (str == encodeOut) {
649         return false;
650     }
651     str = encodeOut;
652     return true;
653 }
654 
655 #if !HAS_NETMANAGER_BASE
656 void HttpExec::AddRequestInfo()
657 {
658     std::lock_guard guard(staticVariable_.curlMultiMutex);
659     int num = 0;
660     while (!staticVariable_.infoQueue.empty()) {
661         if (!staticVariable_.runThread || staticVariable_.curlMulti == nullptr) {
662             break;
663         }
664 
665         auto info = staticVariable_.infoQueue.top();
666         staticVariable_.infoQueue.pop();
667         auto ret = curl_multi_add_handle(staticVariable_.curlMulti, info.handle);
668         if (ret == CURLM_OK) {
669             staticVariable_.contextMap[info.handle] = info.context;
670         }
671 
672         ++num;
673         if (num >= CURL_HANDLE_NUM) {
674             break;
675         }
676     }
677 }
678 #endif
679 
680 #if !HAS_NETMANAGER_BASE
681 void HttpExec::RunThread()
682 {
683 #if defined(MAC_PLATFORM) || defined(IOS_PLATFORM)
684     pthread_setname_np(HTTP_TASK_RUN_THREAD);
685 #else
686     pthread_setname_np(pthread_self(), HTTP_TASK_RUN_THREAD);
687 #endif
688     while (staticVariable_.runThread && staticVariable_.curlMulti != nullptr) {
689         AddRequestInfo();
690         SendRequest();
691         ReadResponse();
692         std::this_thread::sleep_for(std::chrono::milliseconds(CURL_TIMEOUT_MS));
693         std::unique_lock l(staticVariable_.curlMultiMutex);
694         staticVariable_.conditionVariable.wait_for(l, std::chrono::seconds(CONDITION_TIMEOUT_S), [] {
695             return !staticVariable_.infoQueue.empty() || !staticVariable_.contextMap.empty();
696         });
697     }
698 }
699 
700 void HttpExec::SendRequest()
701 {
702     std::lock_guard guard(staticVariable_.curlMultiMutex);
703 
704     int runningHandle = 0;
705     int num = 0;
706     do {
707         if (!staticVariable_.runThread || staticVariable_.curlMulti == nullptr) {
708             break;
709         }
710 
711         auto ret = curl_multi_perform(staticVariable_.curlMulti, &runningHandle);
712 
713         if (runningHandle > 0) {
714             ret = curl_multi_poll(staticVariable_.curlMulti, nullptr, 0, CURL_MAX_WAIT_MSECS, nullptr);
715         }
716 
717         if (ret != CURLM_OK) {
718             return;
719         }
720 
721         ++num;
722         if (num >= CURL_HANDLE_NUM) {
723             break;
724         }
725     } while (runningHandle > 0);
726 }
727 
728 void HttpExec::ReadResponse()
729 {
730     std::lock_guard guard(staticVariable_.curlMultiMutex);
731     CURLMsg *msg = nullptr; /* NOLINT */
732     do {
733         if (!staticVariable_.runThread || staticVariable_.curlMulti == nullptr) {
734             break;
735         }
736 
737         int leftMsg;
738         msg = curl_multi_info_read(staticVariable_.curlMulti, &leftMsg);
739         if (msg) {
740             if (msg->msg == CURLMSG_DONE) {
741                 HandleCurlData(msg);
742             }
743             if (msg->easy_handle) {
744                 (void)curl_multi_remove_handle(staticVariable_.curlMulti, msg->easy_handle);
745                 (void)curl_easy_cleanup(msg->easy_handle);
746             }
747         }
748     } while (msg);
749 }
750 #endif
751 
752 void HttpExec::GetGlobalHttpProxyInfo(std::string &host, int32_t &port, std::string &exclusions)
753 {
754 #ifdef HTTP_PROXY_ENABLE
755     char httpProxyHost[SYSPARA_MAX_SIZE] = {0};
756     char httpProxyPort[SYSPARA_MAX_SIZE] = {0};
757     char httpProxyExclusions[SYSPARA_MAX_SIZE] = {0};
758     GetParameter(HTTP_PROXY_HOST_KEY, DEFAULT_HTTP_PROXY_HOST, httpProxyHost, sizeof(httpProxyHost));
759     GetParameter(HTTP_PROXY_PORT_KEY, DEFAULT_HTTP_PROXY_PORT, httpProxyPort, sizeof(httpProxyPort));
760     GetParameter(HTTP_PROXY_EXCLUSIONS_KEY, DEFAULT_HTTP_PROXY_EXCLUSION_LIST, httpProxyExclusions,
761                  sizeof(httpProxyExclusions));
762 
763     host = Base64::Decode(httpProxyHost);
764     if (host == DEFAULT_HTTP_PROXY_HOST) {
765         host = std::string();
766     }
767     exclusions = httpProxyExclusions;
768     if (exclusions == DEFAULT_HTTP_PROXY_EXCLUSION_LIST) {
769         exclusions = std::string();
770     }
771 
772     port = std::atoi(httpProxyPort);
773 #endif
774 }
775 
776 void HttpExec::GetHttpProxyInfo(RequestContext *context, std::string &host, int32_t &port, std::string &exclusions)
777 {
778     if (context->options.GetUsingHttpProxyType() == UsingHttpProxyType::USE_DEFAULT) {
779 #ifdef HAS_NETMANAGER_BASE
780         using namespace NetManagerStandard;
781         HttpProxy httpProxy;
782         NetConnClient::GetInstance().GetDefaultHttpProxy(httpProxy);
783         host = httpProxy.GetHost();
784         port = httpProxy.GetPort();
785         exclusions = CommonUtils::ToString(httpProxy.GetExclusionList());
786 #else
787         GetGlobalHttpProxyInfo(host, port, exclusions);
788 #endif
789     } else if (context->options.GetUsingHttpProxyType() == UsingHttpProxyType::USE_SPECIFIED) {
790         context->options.GetSpecifiedHttpProxy(host, port, exclusions);
791     }
792 }
793 
794 #if !HAS_NETMANAGER_BASE
795 bool HttpExec::Initialize()
796 {
797     std::lock_guard<std::mutex> lock(staticVariable_.mutexForInitialize);
798     if (staticVariable_.initialized) {
799         return true;
800     }
801     NETSTACK_LOGD("call curl_global_init");
802     if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
803         NETSTACK_LOGE("Failed to initialize 'curl'");
804         return false;
805     }
806 
807     staticVariable_.curlMulti = curl_multi_init();
808     if (staticVariable_.curlMulti == nullptr) {
809         NETSTACK_LOGE("Failed to initialize 'curl_multi'");
810         return false;
811     }
812 
813     staticVariable_.workThread = std::thread(RunThread);
814     staticVariable_.initialized = true;
815     return staticVariable_.initialized;
816 }
817 #endif
818 
819 bool HttpExec::SetOtherOption(CURL *curl, OHOS::NetStack::Http::RequestContext *context)
820 {
821     std::string url = context->options.GetUrl();
822     std::string host, exclusions;
823     int32_t port = 0;
824     GetHttpProxyInfo(context, host, port, exclusions);
825     if (!host.empty() && !CommonUtils::IsHostNameExcluded(url, exclusions, ",")) {
826         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_PROXY, host.c_str(), context);
827         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_PROXYPORT, port, context);
828         auto curlTunnelValue = (url.find("https://") != std::string::npos) ? 1L : 0L;
829         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_HTTPPROXYTUNNEL, curlTunnelValue, context);
830         auto proxyType = (host.find("https://") != std::string::npos) ? CURLPROXY_HTTPS : CURLPROXY_HTTP;
831         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_PROXYTYPE, proxyType, context);
832     }
833     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2, context);
834     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_CIPHER_LIST, TLS12_SECURITY_CIPHER_SUITE, context);
835 #ifdef NETSTACK_PROXY_PASS
836     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_PROXYUSERPWD, NETSTACK_PROXY_PASS, context);
837 #endif // NETSTACK_PROXY_PASS
838 
839 #ifdef HTTP_CURL_PRINT_VERBOSE
840     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_VERBOSE, 1L, context);
841 #endif
842 
843 #ifndef WINDOWS_PLATFORM
844     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_ACCEPT_ENCODING, "", context);
845 #endif
846     return true;
847 }
848 
849 bool HttpExec::SetSSLCertOption(CURL *curl, OHOS::NetStack::Http::RequestContext *context)
850 {
851     std::string cert;
852     std::string certType;
853     std::string key;
854     Secure::SecureChar keyPasswd;
855     context->options.GetClientCert(cert, certType, key, keyPasswd);
856     if (cert.empty()) {
857         NETSTACK_LOGD("SetSSLCertOption param is empty.");
858         return false;
859     }
860     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSLCERT, cert.c_str(), context);
861     if (!key.empty()) {
862         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSLKEY, key.c_str(), context);
863     }
864     if (!certType.empty()) {
865         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSLCERTTYPE, certType.c_str(), context);
866     }
867     if (keyPasswd.Length() > 0) {
868         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_KEYPASSWD, keyPasswd.Data(), context);
869     }
870     return true;
871 }
872 
873 CURLcode HttpExec::SslCtxFunction(CURL *curl, void *sslCtx, void *request_context)
874 {
875     auto requestContext = static_cast<RequestContext *>(request_context);
876     if (requestContext == nullptr) {
877         NETSTACK_LOGE("requestContext is null");
878         return CURLE_SSL_CERTPROBLEM;
879     }
880     CURLcode result = MultiPathSslCtxFunction(curl, sslCtx, requestContext);
881     if (result != CURLE_OK) {
882         return result;
883     }
884     if (!requestContext->GetPinnedPubkey().empty()) {
885         return VerifyRootCaSslCtxFunction(curl, sslCtx, requestContext);
886     }
887     return CURLE_OK;
888 }
889 
890 CURLcode HttpExec::MultiPathSslCtxFunction(CURL *curl, void *sslCtx, void *parm)
891 {
892 #ifdef HTTP_MULTIPATH_CERT_ENABLE
893     auto requestContext = static_cast<RequestContext *>(parm);
894     if (requestContext == nullptr) {
895         NETSTACK_LOGE("requestContext is null");
896         return CURLE_SSL_CERTPROBLEM;
897     }
898     auto &certsPath = requestContext->GetCertsPath();
899     if (sslCtx == nullptr) {
900         NETSTACK_LOGE("ssl_ctx is null");
901         return CURLE_SSL_CERTPROBLEM;
902     }
903 
904     for (const auto &path : certsPath.certPathList) {
905         if (path.empty() || access(path.c_str(), F_OK) != 0) {
906             NETSTACK_LOGD("certificate directory path is not exist");
907             continue;
908         }
909         if (!SSL_CTX_load_verify_locations(static_cast<SSL_CTX *>(sslCtx), nullptr, path.c_str())) {
910             NETSTACK_LOGE("loading certificates from directory error.");
911             continue;
912         }
913     }
914     if (access(certsPath.certFile.c_str(), F_OK) != 0) {
915         NETSTACK_LOGD("certificate directory path is not exist");
916     } else if (!SSL_CTX_load_verify_locations(static_cast<SSL_CTX *>(sslCtx), certsPath.certFile.c_str(), nullptr)) {
917         NETSTACK_LOGE("loading certificates from context cert error.");
918     }
919 #endif // HTTP_MULTIPATH_CERT_ENABLE
920     return CURLE_OK;
921 }
922 
923 #ifdef HTTP_ONLY_VERIFY_ROOT_CA_ENABLE
924 bool Sha256sum(unsigned char *buf, size_t buflen, std::string &digestStr)
925 {
926     EVP_MD_CTX *mdctx = EVP_MD_CTX_create();
927     unsigned int digestLen = 0;
928     unsigned char digest[SHA256_LEN];
929     unsigned char out[SHA256_BASE64_LEN + 1] = {0};
930     if (!mdctx) {
931         NETSTACK_LOGE("create MD_CTX failed.");
932         return false;
933     }
934     if (!EVP_DigestInit(mdctx, EVP_sha256())) {
935         NETSTACK_LOGE("EVP_DigestInit failed.");
936         return false;
937     }
938     if (!EVP_DigestUpdate(mdctx, buf, buflen)) {
939         NETSTACK_LOGE("EVP_DigestUpdate failed.");
940         return false;
941     }
942     if (!EVP_DigestFinal_ex(mdctx, digest, &digestLen)) {
943         NETSTACK_LOGE("EVP_DigestFinal_ex failed.");
944         return false;
945     }
946     EVP_MD_CTX_free(mdctx);
947     if (digestLen != SHA256_LEN) {
948         NETSTACK_LOGE("SHA256 length invalid");
949         return false;
950     }
951     int base64Len = EVP_EncodeBlock(out, digest, SHA256_LEN);
952     if (base64Len != SHA256_BASE64_LEN) {
953         NETSTACK_LOGE("SHA256-Base64 length invalid.");
954         return false;
955     }
956     digestStr = std::string(reinterpret_cast<const char *>(out), SHA256_BASE64_LEN);
957     return true;
958 }
959 
960 static int VerifyCertPubkey(X509 *cert, const std::string &pinnedPubkey)
961 {
962     if (pinnedPubkey.empty()) {
963         // if no pinned pubkey specified, don't pin (Curl default)
964         return CURLE_OK;
965     }
966     if (cert == nullptr) {
967         NETSTACK_LOGE("no cert specified.");
968         return CURLE_BAD_FUNCTION_ARGUMENT;
969     }
970     unsigned char *certPubkey = nullptr;
971     int pubkeyLen = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert), &certPubkey);
972     std::string certPubKeyDigest;
973     if (Sha256sum(certPubkey, pubkeyLen, certPubKeyDigest)) {
974         return CURLE_BAD_FUNCTION_ARGUMENT;
975     }
976     NETSTACK_LOGI("pubkey sha256: %{public}s", certPubKeyDigest.c_str());
977     if (CommonUtils::IsCertPubKeyInPinned(certPubKeyDigest, pinnedPubkey)) {
978         return CURLE_OK;
979     }
980     return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
981 }
982 
983 static int VerifyCallback(int preverifyOk, X509_STORE_CTX *ctx)
984 {
985     X509 *cert;
986     int err, depth;
987     SSL *ssl;
988 
989     cert = X509_STORE_CTX_get_current_cert(ctx);
990     err = X509_STORE_CTX_get_error(ctx);
991     depth = X509_STORE_CTX_get_error_depth(ctx);
992 
993     NETSTACK_LOGI("X509_STORE_CTX error code %{public}d, depth %{public}d", err, depth);
994 
995     ssl = static_cast<SSL *>(X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()));
996     SSL_CTX *sslCtx = SSL_get_SSL_CTX(ssl);
997     RequestContext *requestContext = static_cast<RequestContext *>(SSL_CTX_get_ex_data(sslCtx,
998         SSL_CTX_EX_DATA_REQUEST_CONTEXT_INDEX));
999     if (requestContext->IsRootCaVerifiedOk()) {
1000         // root CA hash verified, normal procedure.
1001         return preverifyOk;
1002     }
1003     int verifyResult = VerifyCertPubkey(cert, requestContext->GetPinnedPubkey());
1004     if (!requestContext->IsRootCaVerified()) {
1005         // not verified yet, so this is the root CA verifying.
1006         NETSTACK_LOGD("Verifying Root CA.");
1007         requestContext->SetRootCaVerifiedOk(verifyResult == CURLE_OK);
1008         requestContext->SetRootCaVerified();
1009     }
1010     if (verifyResult != CURLE_OK && depth == 0) {
1011         // peer site certificate, since root ca verify not ok, and peer site is also not ok
1012         // return failed.
1013         return 0;
1014     }
1015     return preverifyOk;
1016 }
1017 #endif
1018 
1019 CURLcode HttpExec::VerifyRootCaSslCtxFunction(CURL *curl, void *sslCtx, void *context)
1020 {
1021 #ifdef HTTP_ONLY_VERIFY_ROOT_CA_ENABLE
1022     SSL_CTX *ctx = static_cast<SSL_CTX *>(sslCtx);
1023     SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, VerifyCallback);
1024     SSL_CTX_set_ex_data(ctx, SSL_CTX_EX_DATA_REQUEST_CONTEXT_INDEX, context);
1025 #endif
1026     return CURLE_OK;
1027 }
1028 
1029 bool HttpExec::SetServerSSLCertOption(CURL *curl, OHOS::NetStack::Http::RequestContext *context)
1030 {
1031 #ifndef NO_SSL_CERTIFICATION
1032 #ifdef HAS_NETMANAGER_BASE
1033     auto hostname = CommonUtils::GetHostnameFromURL(context->options.GetUrl());
1034 
1035 #if !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM)
1036     std::vector<std::string> certs;
1037     // add app cert path
1038     auto ret = NetManagerStandard::NetConnClient::GetInstance().GetTrustAnchorsForHostName(hostname, certs);
1039     if (ret != 0) {
1040         NETSTACK_LOGE("GetTrustAnchorsForHostName error. ret [%{public}d]", ret);
1041     }
1042 #ifdef HTTP_MULTIPATH_CERT_ENABLE
1043     // add user cert path
1044     certs.emplace_back(USER_CERT_ROOT_PATH);
1045     certs.emplace_back(BASE_PATH + std::to_string(getuid() / UID_TRANSFORM_DIVISOR));
1046     // add system cert path
1047     certs.emplace_back(HttpConstant::HTTP_PREPARE_CA_PATH);
1048     context->SetCertsPath(std::move(certs), context->options.GetCaPath());
1049     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_VERIFYPEER, 1L, context);
1050     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_VERIFYHOST, 2L, context);
1051     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_CAINFO, nullptr, context);
1052 #else
1053     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_CAINFO, nullptr, context);
1054     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_VERIFYPEER, 0L, context);
1055     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_VERIFYHOST, 0L, context);
1056 #endif // HTTP_MULTIPATH_CERT_ENABLE
1057 #else
1058     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_VERIFYPEER, 0L, context);
1059     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_VERIFYHOST, 0L, context);
1060 #endif //  !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM)
1061     // pin trusted certifcate keys.
1062     if (!NetManagerStandard::NetConnClient::GetInstance().IsPinOpenMode(hostname) ||
1063         NetManagerStandard::NetConnClient::GetInstance().IsPinOpenModeVerifyRootCa(hostname)) {
1064         std::string pins;
1065         auto ret1 = NetManagerStandard::NetConnClient::GetInstance().GetPinSetForHostName(hostname, pins);
1066         if (ret1 != 0 || pins.empty()) {
1067             NETSTACK_LOGD("Get no pinset by host name failed");
1068         } else if (NetManagerStandard::NetConnClient::GetInstance().IsPinOpenModeVerifyRootCa(hostname)) {
1069             context->SetPinnedPubkey(pins);
1070         } else {
1071             NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_PINNEDPUBLICKEY, pins.c_str(), context);
1072         }
1073     }
1074 #if defined(HTTP_MULTIPATH_CERT_ENABLE) || defined(HTTP_ONLY_VERIFY_ROOT_CA_ENABLE)
1075     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_CTX_FUNCTION, SslCtxFunction, context);
1076     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_CTX_DATA, context, context);
1077 #endif
1078 #else
1079     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_CAINFO, nullptr, context);
1080     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_VERIFYPEER, 0L, context);
1081     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_VERIFYHOST, 0L, context);
1082 #endif // HAS_NETMANAGER_BASE
1083 #else
1084     // in real life, you should buy a ssl certification and rename it to /etc/ssl/cert.pem
1085     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_VERIFYHOST, 0L, context);
1086     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_VERIFYPEER, 0L, context);
1087 #endif // NO_SSL_CERTIFICATION
1088 
1089     return true;
1090 }
1091 
1092 bool HttpExec::SetCertPinnerOption(CURL *curl, RequestContext *context)
1093 {
1094     auto certPIN = context->options.GetCertificatePinning();
1095     if (certPIN.empty()) {
1096         NETSTACK_LOGD("CertificatePinning is empty");
1097         return true;
1098     }
1099 
1100     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_PINNEDPUBLICKEY, certPIN.c_str(), context);
1101     return true;
1102 }
1103 
1104 bool HttpExec::SetDnsOption(CURL *curl, RequestContext *context)
1105 {
1106     std::vector<std::string> dnsServers = context->options.GetDnsServers();
1107     if (dnsServers.empty()) {
1108         return true;
1109     }
1110     std::string serverList;
1111     for (auto &server : dnsServers) {
1112         serverList += server + ",";
1113         NETSTACK_LOGD("SetDns server: %{public}s", CommonUtils::AnonymizeIp(server).c_str());
1114     }
1115     serverList.pop_back();
1116     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_DNS_SERVERS, serverList.c_str(), context);
1117     return true;
1118 }
1119 
1120 bool HttpExec::ParseHostAndPortFromUrl(const std::string &url, std::string &host, uint16_t &port)
1121 {
1122     CURLU *cu = curl_url();
1123     if (!cu) {
1124         NETSTACK_LOGE("out of memory");
1125         return false;
1126     }
1127     if (curl_url_set(cu, CURLUPART_URL, url.c_str(), 0)) {
1128         NETSTACK_LOGE("not a normalized URL");
1129         curl_url_cleanup(cu);
1130         return false;
1131     }
1132     char *chost = nullptr;
1133     char *cport = nullptr;
1134 
1135     (void)curl_url_get(cu, CURLUPART_HOST, &chost, 0);
1136     (void)curl_url_get(cu, CURLUPART_PORT, &cport, CURLU_DEFAULT_PORT);
1137     if (chost != nullptr) {
1138         host = chost;
1139         curl_free(chost);
1140     }
1141     if (cport != nullptr) {
1142         port = atoi(cport);
1143         curl_free(cport);
1144     }
1145     curl_url_cleanup(cu);
1146     return !host.empty();
1147 }
1148 
1149 bool HttpExec::SetDnsResolvOption(CURL *curl, RequestContext *context)
1150 {
1151     std::string host = "";
1152     uint16_t port = 0;
1153     if (!ParseHostAndPortFromUrl(context->options.GetUrl(), host, port)) {
1154         NETSTACK_LOGE("get host and port failed");
1155         return true;
1156     }
1157 #ifdef HAS_NETMANAGER_BASE
1158     struct addrinfo *res = nullptr;
1159     int ret = getaddrinfo_hook(host.c_str(), nullptr, nullptr, &res);
1160     if (ret < 0) {
1161         return true;
1162     }
1163 
1164     struct curl_slist *hostSlist = nullptr;
1165     for (struct addrinfo *p = res; p != nullptr; p = p->ai_next) {
1166         char ipstr[INET6_ADDRSTRLEN];
1167         void *addr = nullptr;
1168 
1169         if (p->ai_family == AF_INET) {
1170             struct sockaddr_in *ipv4 = reinterpret_cast<struct sockaddr_in *>(p->ai_addr);
1171             addr = &ipv4->sin_addr;
1172         } else {
1173             struct sockaddr_in6 *ipv6 = reinterpret_cast<struct sockaddr_in6 *>(p->ai_addr);
1174             addr = &ipv6->sin6_addr;
1175         }
1176         if (inet_ntop(p->ai_family, addr, ipstr, sizeof(ipstr)) == NULL) {
1177             continue;
1178         }
1179         std::string resolvHost = host + ":" + std::to_string(port) + ":" + ipstr;
1180         hostSlist = curl_slist_append(hostSlist, resolvHost.c_str());
1181     }
1182     freeaddrinfo(res);
1183     if (hostSlist == nullptr) {
1184         NETSTACK_LOGE("no valid ip");
1185         return true;
1186     }
1187     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_RESOLVE, hostSlist, context);
1188     context->SetCurlHostList(hostSlist);
1189 #endif
1190     return true;
1191 }
1192 
1193 bool HttpExec::SetRequestOption(CURL *curl, RequestContext *context)
1194 {
1195     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_HTTP_VERSION, context->options.GetHttpVersion(), context);
1196     const std::string range = context->options.GetRangeString();
1197     if (range.empty()) {
1198         // Some servers don't like requests that are made without a user-agent field, so we provide one
1199         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_USERAGENT, HttpConstant::HTTP_DEFAULT_USER_AGENT, context);
1200     } else {
1201         // https://curl.se/libcurl/c/CURLOPT_RANGE.html
1202         if (context->options.GetMethod() == HttpConstant::HTTP_METHOD_PUT) {
1203             context->SetErrorCode(CURLE_RANGE_ERROR);
1204             NETSTACK_LOGE(
1205                 "For HTTP PUT uploads this option should not be used, since it may conflict with other options.");
1206             return false;
1207         }
1208         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_RANGE, range.c_str(), context);
1209     }
1210     if (!context->options.GetDohUrl().empty()) {
1211         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_DOH_URL, context->options.GetDohUrl().c_str(), context);
1212     }
1213 
1214     SetCertPinnerOption(curl, context);
1215     SetDnsOption(curl, context);
1216     SetSSLCertOption(curl, context);
1217     SetMultiPartOption(curl, context);
1218     SetDnsResolvOption(curl, context);
1219     SetIpResolve(curl, context);
1220     return true;
1221 }
1222 
1223 bool HttpExec::SetOption(CURL *curl, RequestContext *context, struct curl_slist *requestHeader)
1224 {
1225     const std::string &method = context->options.GetMethod();
1226     if (!MethodForGet(method) && !MethodForPost(method)) {
1227         NETSTACK_LOGE("method %{public}s not supported", method.c_str());
1228         return false;
1229     }
1230 
1231     if (context->options.GetMethod() == HttpConstant::HTTP_METHOD_HEAD) {
1232         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_NOBODY, 1L, context);
1233     }
1234 
1235     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_URL, context->options.GetUrl().c_str(), context);
1236 #ifdef HAS_NETMANAGER_BASE
1237     if (!NetSysIsIpv6Enable(0)) {
1238         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4, context);
1239     }
1240 #endif
1241     if (!method.empty()) {
1242         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_CUSTOMREQUEST, method.c_str(), context);
1243     }
1244 
1245     if (MethodForPost(method) && !context->options.GetBody().empty()) {
1246         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_POST, 1L, context);
1247         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_POSTFIELDS, context->options.GetBody().c_str(), context);
1248         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_POSTFIELDSIZE, context->options.GetBody().size(), context);
1249     }
1250 
1251     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_XFERINFOFUNCTION, ProgressCallback, context);
1252     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_XFERINFODATA, context, context);
1253     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_NOPROGRESS, 0L, context);
1254 
1255     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_WRITEFUNCTION, OnWritingMemoryBody, context);
1256     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_WRITEDATA, context, context);
1257 
1258     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_HEADERFUNCTION, OnWritingMemoryHeader, context);
1259     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_HEADERDATA, context, context);
1260     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_HTTPHEADER, requestHeader, context);
1261     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_FOLLOWLOCATION, 1L, context);
1262 
1263     /* first #undef CURL_DISABLE_COOKIES in curl config */
1264     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_COOKIEFILE, "", context);
1265     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_NOSIGNAL, 1L, context);
1266     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_TIMEOUT_MS, context->options.GetReadTimeout(), context);
1267     NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_CONNECTTIMEOUT_MS, context->options.GetConnectTimeout(), context);
1268 
1269     if (!SetRequestOption(curl, context)) {
1270         return false;
1271     }
1272 
1273     if (!SetOtherOption(curl, context)) {
1274         return false;
1275     }
1276     return true;
1277 }
1278 
1279 size_t HttpExec::OnWritingMemoryBody(const void *data, size_t size, size_t memBytes, void *userData)
1280 {
1281     auto context = static_cast<RequestContext *>(userData);
1282     if (context == nullptr || !context->GetSharedManager()) {
1283         return 0;
1284     }
1285     if (context->GetSharedManager()->IsEventDestroy()) {
1286         context->StopAndCacheNapiPerformanceTiming(HttpConstant::RESPONSE_BODY_TIMING);
1287         return 0;
1288     }
1289     if (context->IsRequestInStream()) {
1290         context->SetTempData(data, size * memBytes);
1291         NapiUtils::CreateUvQueueWorkByModuleId(
1292             context->GetEnv(), std::bind(OnDataReceive, context->GetEnv(), napi_ok, context), context->GetModuleId());
1293         context->StopAndCacheNapiPerformanceTiming(HttpConstant::RESPONSE_BODY_TIMING);
1294         return size * memBytes;
1295     }
1296     if (context->response.GetResult().size() > context->options.GetMaxLimit() ||
1297         size * memBytes > context->options.GetMaxLimit()) {
1298         NETSTACK_LOGE("response data exceeds the maximum limit");
1299         context->StopAndCacheNapiPerformanceTiming(HttpConstant::RESPONSE_BODY_TIMING);
1300         return 0;
1301     }
1302     context->response.AppendResult(data, size * memBytes);
1303     context->StopAndCacheNapiPerformanceTiming(HttpConstant::RESPONSE_BODY_TIMING);
1304     return size * memBytes;
1305 }
1306 
1307 static void MakeSetCookieArray(napi_env env, napi_value header,
1308                                const std::pair<const std::basic_string<char>, std::basic_string<char>> &headerElement)
1309 {
1310     std::vector<std::string> cookieVec =
1311         CommonUtils::Split(headerElement.second, HttpConstant::RESPONSE_KEY_SET_COOKIE_SEPARATOR);
1312     uint32_t index = 0;
1313     auto len = cookieVec.size();
1314     auto array = NapiUtils::CreateArray(env, len);
1315     for (const auto &setCookie : cookieVec) {
1316         auto str = NapiUtils::CreateStringUtf8(env, setCookie);
1317         NapiUtils::SetArrayElement(env, array, index, str);
1318         ++index;
1319     }
1320     NapiUtils::SetArrayProperty(env, header, HttpConstant::RESPONSE_KEY_SET_COOKIE, array);
1321 }
1322 
1323 static void MakeHeaderWithSetCookieArray(napi_env env, napi_value header, std::map<std::string, std::string> *headerMap)
1324 {
1325     for (const auto &it : *headerMap) {
1326         if (!it.first.empty() && !it.second.empty()) {
1327             if (it.first == HttpConstant::RESPONSE_KEY_SET_COOKIE) {
1328                 MakeSetCookieArray(env, header, it);
1329                 continue;
1330             }
1331             NapiUtils::SetStringPropertyUtf8(env, header, it.first, it.second);
1332         }
1333     }
1334 }
1335 
1336 static void ResponseHeaderCallback(uv_work_t *work, int status)
1337 {
1338     (void)status;
1339 
1340     auto workWrapper = static_cast<UvWorkWrapperShared *>(work->data);
1341     napi_env env = workWrapper->env;
1342     auto headerMap = static_cast<std::map<std::string, std::string> *>(workWrapper->data);
1343     auto closeScope = [env](napi_handle_scope scope) { NapiUtils::CloseScope(env, scope); };
1344     std::unique_ptr<napi_handle_scope__, decltype(closeScope)> scope(NapiUtils::OpenScope(env), closeScope);
1345     napi_value header = NapiUtils::CreateObject(env);
1346     if (NapiUtils::GetValueType(env, header) == napi_object) {
1347         MakeHeaderWithSetCookieArray(env, header, headerMap);
1348     }
1349     std::pair<napi_value, napi_value> arg = {NapiUtils::GetUndefined(env), header};
1350     workWrapper->manager->Emit(workWrapper->type, arg);
1351     delete headerMap;
1352     headerMap = nullptr;
1353     delete workWrapper;
1354     workWrapper = nullptr;
1355     delete work;
1356     work = nullptr;
1357 }
1358 
1359 static std::map<std::string, std::string> MakeHeaderWithSetCookie(RequestContext *context)
1360 {
1361     std::map<std::string, std::string> tempMap = context->response.GetHeader();
1362     std::string setCookies;
1363     size_t loop = 0;
1364     for (const auto &setCookie : context->response.GetsetCookie()) {
1365         setCookies += setCookie;
1366         if (loop + 1 < context->response.GetsetCookie().size()) {
1367             setCookies += HttpConstant::RESPONSE_KEY_SET_COOKIE_SEPARATOR;
1368         }
1369         ++loop;
1370     }
1371     tempMap[HttpConstant::RESPONSE_KEY_SET_COOKIE] = setCookies;
1372     return tempMap;
1373 }
1374 
1375 size_t HttpExec::OnWritingMemoryHeader(const void *data, size_t size, size_t memBytes, void *userData)
1376 {
1377     auto context = static_cast<RequestContext *>(userData);
1378     if (context == nullptr) {
1379         return 0;
1380     }
1381     if (context->GetSharedManager()->IsEventDestroy()) {
1382         context->StopAndCacheNapiPerformanceTiming(HttpConstant::RESPONSE_HEADER_TIMING);
1383         return 0;
1384     }
1385     context->response.AppendRawHeader(data, size * memBytes);
1386     if (CommonUtils::EndsWith(context->response.GetRawHeader(), HttpConstant::HTTP_RESPONSE_HEADER_SEPARATOR)) {
1387         context->response.ParseHeaders();
1388         if (context->GetSharedManager()) {
1389             auto headerMap = new std::map<std::string, std::string>(MakeHeaderWithSetCookie(context));
1390             context->GetSharedManager()->EmitByUvWithoutCheckShared(ON_HEADER_RECEIVE, headerMap,
1391                                                                     ResponseHeaderCallback);
1392             auto headersMap = new std::map<std::string, std::string>(MakeHeaderWithSetCookie(context));
1393             context->GetSharedManager()->EmitByUvWithoutCheckShared(ON_HEADERS_RECEIVE, headersMap,
1394                                                                     ResponseHeaderCallback);
1395         }
1396     }
1397     context->StopAndCacheNapiPerformanceTiming(HttpConstant::RESPONSE_HEADER_TIMING);
1398     return size * memBytes;
1399 }
1400 
1401 void HttpExec::OnDataReceive(napi_env env, napi_status status, void *data)
1402 {
1403     auto context = static_cast<RequestContext *>(data);
1404     if (context == nullptr) {
1405         NETSTACK_LOGE("context is nullptr");
1406         return;
1407     }
1408 
1409     void *buffer = nullptr;
1410     auto tempData = context->GetTempData();
1411     context->PopTempData();
1412     if (tempData.empty()) {
1413         NETSTACK_LOGI("[GetTempData] tempDate is empty!");
1414         return;
1415     }
1416     napi_value arrayBuffer = NapiUtils::CreateArrayBuffer(context->GetEnv(), tempData.size(), &buffer);
1417     if (buffer == nullptr || arrayBuffer == nullptr) {
1418         return;
1419     }
1420     if (memcpy_s(buffer, tempData.size(), tempData.data(), tempData.size()) != EOK) {
1421         NETSTACK_LOGE("[CreateArrayBuffer] memory copy failed");
1422         return;
1423     }
1424     context->EmitSharedManager(ON_DATA_RECEIVE,
1425                                std::make_pair(NapiUtils::GetUndefined(context->GetEnv()), arrayBuffer));
1426 }
1427 
1428 void HttpExec::OnDataProgress(napi_env env, napi_status status, void *data)
1429 {
1430     auto context = static_cast<RequestContext *>(data);
1431     if (context == nullptr) {
1432         NETSTACK_LOGD("OnDataProgress context is null");
1433         return;
1434     }
1435     auto progress = NapiUtils::CreateObject(context->GetEnv());
1436     if (NapiUtils::GetValueType(context->GetEnv(), progress) == napi_undefined) {
1437         return;
1438     }
1439     auto dlLen = context->GetDlLen();
1440     if (dlLen.tLen && dlLen.nLen) {
1441         NapiUtils::SetUint32Property(context->GetEnv(), progress, "receiveSize", static_cast<uint32_t>(dlLen.nLen));
1442         NapiUtils::SetUint32Property(context->GetEnv(), progress, "totalSize", static_cast<uint32_t>(dlLen.tLen));
1443 
1444         context->EmitSharedManager(ON_DATA_RECEIVE_PROGRESS,
1445                                    std::make_pair(NapiUtils::GetUndefined(context->GetEnv()), progress));
1446     }
1447 }
1448 
1449 __attribute__((no_sanitize("cfi"))) void HttpExec::OnDataUploadProgress(napi_env env, napi_status status, void *data)
1450 {
1451     auto context = static_cast<RequestContext *>(data);
1452     if (context == nullptr) {
1453         NETSTACK_LOGD("OnDataUploadProgress context is null");
1454         return;
1455     }
1456     auto progress = NapiUtils::CreateObject(context->GetEnv());
1457     if (NapiUtils::GetValueType(context->GetEnv(), progress) == napi_undefined) {
1458         NETSTACK_LOGD("OnDataUploadProgress napi_undefined.");
1459         return;
1460     }
1461     NapiUtils::SetUint32Property(context->GetEnv(), progress, "sendSize",
1462                                  static_cast<uint32_t>(context->GetUlLen().nLen));
1463     NapiUtils::SetUint32Property(context->GetEnv(), progress, "totalSize",
1464                                  static_cast<uint32_t>(context->GetUlLen().tLen));
1465     context->EmitSharedManager(ON_DATA_SEND_PROGRESS,
1466                                std::make_pair(NapiUtils::GetUndefined(context->GetEnv()), progress));
1467 }
1468 
1469 __attribute__((no_sanitize("cfi"))) int HttpExec::ProgressCallback(void *userData, curl_off_t dltotal, curl_off_t dlnow,
1470                                                                    curl_off_t ultotal, curl_off_t ulnow)
1471 {
1472     auto context = static_cast<RequestContext *>(userData);
1473     if (context == nullptr) {
1474         return 0;
1475     }
1476     if (ultotal != 0 && ultotal >= ulnow && !context->CompareWithLastElement(ulnow, ultotal)) {
1477         context->SetUlLen(ulnow, ultotal);
1478         NapiUtils::CreateUvQueueWorkByModuleId(context->GetEnv(),
1479                                                std::bind(OnDataUploadProgress, context->GetEnv(), napi_ok, context),
1480                                                context->GetModuleId());
1481     }
1482     if (!context->IsRequestInStream()) {
1483         return 0;
1484     }
1485     if (context->GetSharedManager()->IsEventDestroy()) {
1486         return 0;
1487     }
1488     if (dltotal != 0) {
1489         context->SetDlLen(dlnow, dltotal);
1490         NapiUtils::CreateUvQueueWorkByModuleId(
1491             context->GetEnv(), std::bind(OnDataProgress, context->GetEnv(), napi_ok, context), context->GetModuleId());
1492     }
1493     return 0;
1494 }
1495 
1496 struct curl_slist *HttpExec::MakeHeaders(const std::vector<std::string> &vec)
1497 {
1498     struct curl_slist *header = nullptr;
1499     std::for_each(vec.begin(), vec.end(), [&header](const std::string &s) {
1500         if (!s.empty()) {
1501             header = curl_slist_append(header, s.c_str());
1502         }
1503     });
1504     return header;
1505 }
1506 
1507 napi_value HttpExec::MakeResponseHeader(napi_env env, void *ctx)
1508 {
1509     auto context = reinterpret_cast<RequestContext *>(ctx);
1510     (void)env;
1511     napi_value header = NapiUtils::CreateObject(context->GetEnv());
1512     if (NapiUtils::GetValueType(context->GetEnv(), header) == napi_object) {
1513         for (const auto &it : context->response.GetHeader()) {
1514             if (!it.first.empty() && !it.second.empty()) {
1515                 NapiUtils::SetStringPropertyUtf8(context->GetEnv(), header, it.first, it.second);
1516             }
1517         }
1518         if (!context->response.GetsetCookie().empty()) {
1519             uint32_t index = 0;
1520             auto len = context->response.GetsetCookie().size();
1521             auto array = NapiUtils::CreateArray(context->GetEnv(), len);
1522             for (const auto &setCookie : context->response.GetsetCookie()) {
1523                 auto str = NapiUtils::CreateStringUtf8(context->GetEnv(), setCookie);
1524                 NapiUtils::SetArrayElement(context->GetEnv(), array, index, str);
1525                 ++index;
1526             }
1527             NapiUtils::SetArrayProperty(context->GetEnv(), header, HttpConstant::RESPONSE_KEY_SET_COOKIE, array);
1528         }
1529     }
1530     return header;
1531 }
1532 
1533 bool HttpExec::IsUnReserved(unsigned char in)
1534 {
1535     if ((in >= '0' && in <= '9') || (in >= 'a' && in <= 'z') || (in >= 'A' && in <= 'Z')) {
1536         return true;
1537     }
1538     switch (in) {
1539         case '-':
1540         case '.':
1541         case '_':
1542         case '~':
1543             return true;
1544         default:
1545             break;
1546     }
1547     return false;
1548 }
1549 
1550 bool HttpExec::ProcByExpectDataType(napi_value object, RequestContext *context)
1551 {
1552     switch (context->options.GetHttpDataType()) {
1553         case HttpDataType::STRING: {
1554             NapiUtils::SetStringPropertyUtf8(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT,
1555                                              context->response.GetResult());
1556             NapiUtils::SetUint32Property(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT_TYPE,
1557                                          static_cast<uint32_t>(HttpDataType::STRING));
1558             return true;
1559         }
1560         case HttpDataType::OBJECT: {
1561             if (context->response.GetResult().size() > HttpConstant::MAX_JSON_PARSE_SIZE) {
1562                 return false;
1563             }
1564 
1565             napi_value obj = NapiUtils::JsonParse(context->GetEnv(), context->response.GetResult());
1566             if (obj) {
1567                 NapiUtils::SetNamedProperty(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT, obj);
1568                 NapiUtils::SetUint32Property(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT_TYPE,
1569                                              static_cast<uint32_t>(HttpDataType::OBJECT));
1570                 return true;
1571             }
1572 
1573             // parse maybe failed
1574             return false;
1575         }
1576         case HttpDataType::ARRAY_BUFFER: {
1577             void *data = nullptr;
1578             auto body = context->response.GetResult();
1579             napi_value arrayBuffer = NapiUtils::CreateArrayBuffer(context->GetEnv(), body.size(), &data);
1580             if (data != nullptr && arrayBuffer != nullptr) {
1581                 if (memcpy_s(data, body.size(), body.c_str(), body.size()) < 0) {
1582                     NETSTACK_LOGE("[ProcByExpectDataType] memory copy failed");
1583                     return true;
1584                 }
1585                 NapiUtils::SetNamedProperty(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT, arrayBuffer);
1586                 NapiUtils::SetUint32Property(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT_TYPE,
1587                                              static_cast<uint32_t>(HttpDataType::ARRAY_BUFFER));
1588             }
1589             return true;
1590         }
1591         default:
1592             break;
1593     }
1594     return false;
1595 }
1596 
1597 void HttpExec::AsyncRunRequest(RequestContext *context)
1598 {
1599     HttpAsyncWork::ExecRequest(context->GetEnv(), context);
1600 }
1601 
1602 #if !HAS_NETMANAGER_BASE
1603 bool HttpExec::IsInitialized()
1604 {
1605     return staticVariable_.initialized;
1606 }
1607 
1608 void HttpExec::DeInitialize()
1609 {
1610     std::lock_guard<std::mutex> lock(staticVariable_.curlMultiMutex);
1611     staticVariable_.runThread = false;
1612     staticVariable_.conditionVariable.notify_all();
1613     if (staticVariable_.workThread.joinable()) {
1614         staticVariable_.workThread.join();
1615     }
1616     if (staticVariable_.curlMulti) {
1617         curl_multi_cleanup(staticVariable_.curlMulti);
1618     }
1619     staticVariable_.initialized = false;
1620 }
1621 #endif
1622 
1623 bool HttpResponseCacheExec::ExecFlush(BaseContext *context)
1624 {
1625     (void)context;
1626     CacheProxy::FlushCache();
1627     return true;
1628 }
1629 
1630 napi_value HttpResponseCacheExec::FlushCallback(BaseContext *context)
1631 {
1632     return NapiUtils::GetUndefined(context->GetEnv());
1633 }
1634 
1635 bool HttpResponseCacheExec::ExecDelete(BaseContext *context)
1636 {
1637     (void)context;
1638     CacheProxy::StopCacheAndDelete();
1639     return true;
1640 }
1641 
1642 napi_value HttpResponseCacheExec::DeleteCallback(BaseContext *context)
1643 {
1644     return NapiUtils::GetUndefined(context->GetEnv());
1645 }
1646 
1647 bool HttpExec::SetMultiPartOption(CURL *curl, RequestContext *context)
1648 {
1649     auto header = context->options.GetHeader();
1650     auto type = CommonUtils::ToLower(header[HttpConstant::HTTP_CONTENT_TYPE]);
1651     if (type != HttpConstant::HTTP_CONTENT_TYPE_MULTIPART) {
1652         return true;
1653     }
1654     auto multiPartDataList = context->options.GetMultiPartDataList();
1655     if (multiPartDataList.empty()) {
1656         return true;
1657     }
1658     curl_mime *multipart = curl_mime_init(curl);
1659     if (multipart == nullptr) {
1660         return false;
1661     }
1662     context->SetMultipart(multipart);
1663     curl_mimepart *part = nullptr;
1664     bool hasData = false;
1665     for (auto &multiFormData : multiPartDataList) {
1666         if (multiFormData.name.empty()) {
1667             continue;
1668         }
1669         if (multiFormData.data.empty() && multiFormData.filePath.empty()) {
1670             NETSTACK_LOGE("Failed to set multiFormData error no data and filepath at the same time");
1671             continue;
1672         }
1673         part = curl_mime_addpart(multipart);
1674         SetFormDataOption(multiFormData, part, curl, context);
1675         hasData = true;
1676     }
1677     if (hasData) {
1678         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_MIMEPOST, multipart, context);
1679     }
1680     return true;
1681 }
1682 
1683 void HttpExec::SetFormDataOption(MultiFormData &multiFormData, curl_mimepart *part, CURL *curl,
1684                                  RequestContext *context)
1685 {
1686     CURLcode result = curl_mime_name(part, multiFormData.name.c_str());
1687     if (result != CURLE_OK) {
1688         NETSTACK_LOGE("Failed to set name error: %{public}s", curl_easy_strerror(result));
1689         return;
1690     }
1691     if (!multiFormData.contentType.empty()) {
1692         result = curl_mime_type(part, multiFormData.contentType.c_str());
1693         if (result != CURLE_OK) {
1694             NETSTACK_LOGE("Failed to set contentType error: %{public}s", curl_easy_strerror(result));
1695         }
1696     }
1697     if (!multiFormData.remoteFileName.empty()) {
1698         result = curl_mime_filename(part, multiFormData.remoteFileName.c_str());
1699         if (result != CURLE_OK) {
1700             NETSTACK_LOGE("Failed to set remoteFileName error: %{public}s", curl_easy_strerror(result));
1701         }
1702     }
1703     if (!multiFormData.data.empty()) {
1704         result = curl_mime_data(part, multiFormData.data.c_str(), multiFormData.data.length());
1705         if (result != CURLE_OK) {
1706             NETSTACK_LOGE("Failed to set data error: %{public}s", curl_easy_strerror(result));
1707         }
1708     } else {
1709         if (!multiFormData.remoteFileName.empty()) {
1710             std::string fileData;
1711             bool isReadFile = CommonUtils::GetFileDataFromFilePath(multiFormData.filePath.c_str(), fileData);
1712             if (isReadFile) {
1713                 result = curl_mime_data(part, fileData.c_str(), fileData.size());
1714             } else {
1715                 result = curl_mime_filedata(part, multiFormData.filePath.c_str());
1716             }
1717         } else {
1718             result = curl_mime_filedata(part, multiFormData.filePath.c_str());
1719         }
1720         if (result != CURLE_OK) {
1721             NETSTACK_LOGE("Failed to set file data error: %{public}s", curl_easy_strerror(result));
1722         }
1723     }
1724 }
1725 
1726 bool HttpExec::SetIpResolve(CURL *curl, RequestContext *context)
1727 {
1728     std::string addressFamily = context->options.GetAddressFamily();
1729     if (addressFamily.empty()) {
1730         return true;
1731     }
1732     if (addressFamily.compare(HTTP_AF_ONLYV4) == 0) {
1733         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4, context);
1734     } else if (addressFamily.compare(HTTP_AF_ONLYV6) == 0) {
1735         NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6, context);
1736     }
1737     return true;
1738 }
1739 } // namespace OHOS::NetStack::Http
1740