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