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