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