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