1 /*
2 * Copyright (c) 2021-2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "http_exec.h"
17
18 #include <cstddef>
19 #include <cstring>
20 #include <memory>
21 #include <thread>
22 #include <unistd.h>
23
24 #ifdef HTTP_PROXY_ENABLE
25 #include "parameter.h"
26 #endif
27 #ifdef HAS_NETMANAGER_BASE
28 #include "http_proxy.h"
29 #include "net_conn_client.h"
30 #endif
31 #include "base64_utils.h"
32 #include "cache_proxy.h"
33 #include "constant.h"
34 #include "event_list.h"
35 #include "http_async_work.h"
36 #include "http_time.h"
37 #include "napi_utils.h"
38 #include "netstack_common_utils.h"
39 #include "netstack_log.h"
40 #include "securec.h"
41 #include "secure_char.h"
42
43 #define NETSTACK_CURL_EASY_SET_OPTION(handle, opt, data, asyncContext) \
44 do { \
45 CURLcode result = curl_easy_setopt(handle, opt, data); \
46 if (result != CURLE_OK) { \
47 const char *err = curl_easy_strerror(result); \
48 NETSTACK_LOGE("Failed to set option: %{public}s, %{public}s %{public}d", #opt, err, result); \
49 (asyncContext)->SetErrorCode(result); \
50 return false; \
51 } \
52 } while (0)
53
54 namespace OHOS::NetStack::Http {
55 static constexpr int CURL_TIMEOUT_MS = 50;
56 static constexpr int CONDITION_TIMEOUT_S = 3600;
57 static constexpr int CURL_MAX_WAIT_MSECS = 10;
58 static constexpr int CURL_HANDLE_NUM = 10;
59 static constexpr const uint32_t EVENT_PARAM_ZERO = 0;
60 static constexpr const uint32_t EVENT_PARAM_ONE = 1;
61 static constexpr const uint32_t EVENT_PARAM_TWO = 2;
62 static constexpr const char *TLS12_SECURITY_CIPHER_SUITE = R"(DEFAULT:!eNULL:!EXPORT)";
63
64 #ifdef HTTP_PROXY_ENABLE
65 static constexpr int32_t SYSPARA_MAX_SIZE = 128;
66 static constexpr const char *DEFAULT_HTTP_PROXY_HOST = "NONE";
67 static constexpr const char *DEFAULT_HTTP_PROXY_PORT = "0";
68 static constexpr const char *DEFAULT_HTTP_PROXY_EXCLUSION_LIST = "NONE";
69 static constexpr const char *HTTP_PROXY_HOST_KEY = "persist.netmanager_base.http_proxy.host";
70 static constexpr const char *HTTP_PROXY_PORT_KEY = "persist.netmanager_base.http_proxy.port";
71 static constexpr const char *HTTP_PROXY_EXCLUSIONS_KEY = "persist.netmanager_base.http_proxy.exclusion_list";
72 #endif
73
RequestContextDeleter(RequestContext * context)74 static void RequestContextDeleter(RequestContext *context)
75 {
76 std::lock_guard lockGuard(HttpExec::staticContextSet_.mutexForContextVec);
77 auto it = std::find(HttpExec::staticContextSet_.contextSet.begin(), HttpExec::staticContextSet_.contextSet.end(),
78 context);
79 if (it == HttpExec::staticContextSet_.contextSet.end()) {
80 NETSTACK_LOGE("can't find request context in set");
81 } else {
82 HttpExec::staticContextSet_.contextSet.erase(it);
83 }
84
85 context->DeleteReference();
86 delete context;
87 context = nullptr;
88 }
89
AsyncWorkRequestInStreamCallback(napi_env env,napi_status status,void * data)90 static void AsyncWorkRequestInStreamCallback(napi_env env, napi_status status, void *data)
91 {
92 if (status != napi_ok) {
93 return;
94 }
95 std::unique_ptr<RequestContext, decltype(&RequestContextDeleter)> context(static_cast<RequestContext *>(data),
96 RequestContextDeleter);
97 napi_value argv[EVENT_PARAM_TWO] = {nullptr};
98 if (context->IsParseOK() && context->IsExecOK()) {
99 argv[EVENT_PARAM_ZERO] = NapiUtils::GetUndefined(env);
100 argv[EVENT_PARAM_ONE] = HttpExec::RequestInStreamCallback(context.get());
101 if (argv[EVENT_PARAM_ONE] == nullptr) {
102 return;
103 }
104 } else {
105 argv[EVENT_PARAM_ZERO] =
106 NapiUtils::CreateErrorMessage(env, context->GetErrorCode(), context->GetErrorMessage());
107 if (argv[EVENT_PARAM_ZERO] == nullptr) {
108 return;
109 }
110
111 argv[EVENT_PARAM_ONE] = NapiUtils::GetUndefined(env);
112 }
113
114 napi_value undefined = NapiUtils::GetUndefined(env);
115 if (context->GetDeferred() != nullptr) {
116 if (context->IsExecOK()) {
117 napi_resolve_deferred(env, context->GetDeferred(), argv[EVENT_PARAM_ONE]);
118 context->Emit(ON_DATA_END, std::make_pair(undefined, undefined));
119 } else {
120 napi_reject_deferred(env, context->GetDeferred(), argv[EVENT_PARAM_ZERO]);
121 }
122 return;
123 }
124 napi_value func = context->GetCallback();
125 if (NapiUtils::GetValueType(env, func) == napi_function) {
126 (void)NapiUtils::CallFunction(env, undefined, func, EVENT_PARAM_TWO, argv);
127 }
128 if (context->IsExecOK()) {
129 context->Emit(ON_DATA_END, std::make_pair(undefined, undefined));
130 }
131 }
132
AsyncWorkRequestCallback(napi_env env,napi_status status,void * data)133 static void AsyncWorkRequestCallback(napi_env env, napi_status status, void *data)
134 {
135 if (status != napi_ok) {
136 return;
137 }
138 std::unique_ptr<RequestContext, decltype(&RequestContextDeleter)> context(static_cast<RequestContext *>(data),
139 RequestContextDeleter);
140 napi_value argv[EVENT_PARAM_TWO] = {nullptr};
141 if (context->IsParseOK() && context->IsExecOK()) {
142 argv[EVENT_PARAM_ZERO] = NapiUtils::GetUndefined(env);
143 argv[EVENT_PARAM_ONE] = HttpExec::RequestCallback(context.get());
144 if (argv[EVENT_PARAM_ONE] == nullptr) {
145 return;
146 }
147 } else {
148 argv[EVENT_PARAM_ZERO] =
149 NapiUtils::CreateErrorMessage(env, context->GetErrorCode(), context->GetErrorMessage());
150 if (argv[EVENT_PARAM_ZERO] == nullptr) {
151 return;
152 }
153
154 argv[EVENT_PARAM_ONE] = NapiUtils::GetUndefined(env);
155 }
156 napi_value undefined = NapiUtils::GetUndefined(env);
157 if (context->GetDeferred() != nullptr) {
158 if (context->IsExecOK()) {
159 napi_resolve_deferred(env, context->GetDeferred(), argv[EVENT_PARAM_ONE]);
160 } else {
161 napi_reject_deferred(env, context->GetDeferred(), argv[EVENT_PARAM_ZERO]);
162 }
163 return;
164 }
165 napi_value func = context->GetCallback();
166 if (NapiUtils::GetValueType(env, func) == napi_function) {
167 (void)NapiUtils::CallFunction(env, undefined, func, EVENT_PARAM_TWO, argv);
168 }
169 }
170
AddCurlHandle(CURL * handle,RequestContext * context)171 bool HttpExec::AddCurlHandle(CURL *handle, RequestContext *context)
172 {
173 if (handle == nullptr || staticVariable_.curlMulti == nullptr) {
174 NETSTACK_LOGE("handle nullptr");
175 return false;
176 }
177
178 std::thread([context, handle] {
179 std::lock_guard guard(staticVariable_.curlMultiMutex);
180 //Do SetServerSSLCertOption here to avoid blocking the main thread.
181 SetServerSSLCertOption(handle, context);
182 staticVariable_.infoQueue.emplace(context, handle);
183 staticVariable_.conditionVariable.notify_all();
184 {
185 std::lock_guard lockGuard(staticContextSet_.mutexForContextVec);
186 HttpExec::staticContextSet_.contextSet.emplace(context);
187 }
188 }).detach();
189
190 return true;
191 }
192
193 HttpExec::StaticVariable HttpExec::staticVariable_; /* NOLINT */
194 HttpExec::StaticContextVec HttpExec::staticContextSet_;
195
RequestWithoutCache(RequestContext * context)196 bool HttpExec::RequestWithoutCache(RequestContext *context)
197 {
198 if (!staticVariable_.initialized) {
199 NETSTACK_LOGE("curl not init");
200 return false;
201 }
202
203 auto handle = curl_easy_init();
204 if (!handle) {
205 NETSTACK_LOGE("Failed to create fetch task");
206 return false;
207 }
208
209 std::vector<std::string> vec;
210 std::for_each(context->options.GetHeader().begin(), context->options.GetHeader().end(),
211 [&vec](const std::pair<std::string, std::string> &p) {
212 if (!p.second.empty()) {
213 vec.emplace_back(p.first + HttpConstant::HTTP_HEADER_SEPARATOR + p.second);
214 } else {
215 vec.emplace_back(p.first + HttpConstant::HTTP_HEADER_BLANK_SEPARATOR);
216 }
217 });
218 context->SetCurlHeaderList(MakeHeaders(vec));
219
220 if (!SetOption(handle, context, context->GetCurlHeaderList())) {
221 NETSTACK_LOGE("set option failed");
222 return false;
223 }
224
225 context->response.SetRequestTime(HttpTime::GetNowTimeGMT());
226
227 if (!AddCurlHandle(handle, context)) {
228 NETSTACK_LOGE("add handle failed");
229 return false;
230 }
231
232 return true;
233 }
234
GetCurlDataFromHandle(CURL * handle,RequestContext * context,CURLMSG curlMsg,CURLcode result)235 bool HttpExec::GetCurlDataFromHandle(CURL *handle, RequestContext *context, CURLMSG curlMsg, CURLcode result)
236 {
237 if (curlMsg != CURLMSG_DONE) {
238 NETSTACK_LOGE("CURLMSG %{public}s", std::to_string(curlMsg).c_str());
239 context->SetErrorCode(NapiUtils::NETSTACK_NAPI_INTERNAL_ERROR);
240 return false;
241 }
242
243 if (result != CURLE_OK) {
244 context->SetErrorCode(result);
245 NETSTACK_LOGE("CURLcode result %{public}s", std::to_string(result).c_str());
246 return false;
247 }
248
249 context->response.SetResponseTime(HttpTime::GetNowTimeGMT());
250
251 int64_t responseCode;
252 CURLcode code = curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &responseCode);
253 if (code != CURLE_OK) {
254 context->SetErrorCode(code);
255 return false;
256 }
257 context->response.SetResponseCode(responseCode);
258 if (context->response.GetResponseCode() == static_cast<uint32_t>(ResponseCode::NOT_MODIFIED)) {
259 NETSTACK_LOGI("cache is NOT_MODIFIED, we use the cache");
260 context->SetResponseByCache();
261 return true;
262 }
263 NETSTACK_LOGD("responseCode is %{public}s", std::to_string(responseCode).c_str());
264
265 struct curl_slist *cookies = nullptr;
266 code = curl_easy_getinfo(handle, CURLINFO_COOKIELIST, &cookies);
267 if (code != CURLE_OK) {
268 context->SetErrorCode(code);
269 return false;
270 }
271
272 std::unique_ptr<struct curl_slist, decltype(&curl_slist_free_all)> cookiesHandle(cookies, curl_slist_free_all);
273 while (cookies) {
274 context->response.AppendCookies(cookies->data, strlen(cookies->data));
275 if (cookies->next != nullptr) {
276 context->response.AppendCookies(HttpConstant::HTTP_LINE_SEPARATOR,
277 strlen(HttpConstant::HTTP_LINE_SEPARATOR));
278 }
279 cookies = cookies->next;
280 }
281 return true;
282 }
283
GetTimingFromCurl(CURL * handle,CURLINFO info)284 double HttpExec::GetTimingFromCurl(CURL *handle, CURLINFO info)
285 {
286 time_t timing;
287 CURLcode result = curl_easy_getinfo(handle, info, &timing);
288 if (result != CURLE_OK) {
289 NETSTACK_LOGE("Failed to get timing: %{public}d, %{public}s", info, curl_easy_strerror(result));
290 return 0;
291 }
292 return Timing::TimeUtils::Microseconds2Milliseconds(timing);
293 }
294
CacheCurlPerformanceTiming(CURL * handle,RequestContext * context)295 void HttpExec::CacheCurlPerformanceTiming(CURL *handle, RequestContext *context)
296 {
297 context->CachePerformanceTimingItem(HttpConstant::RESPONSE_DNS_TIMING,
298 HttpExec::GetTimingFromCurl(handle, CURLINFO_NAMELOOKUP_TIME_T));
299 context->CachePerformanceTimingItem(HttpConstant::RESPONSE_TCP_TIMING,
300 HttpExec::GetTimingFromCurl(handle, CURLINFO_CONNECT_TIME_T));
301 context->CachePerformanceTimingItem(HttpConstant::RESPONSE_TLS_TIMING,
302 HttpExec::GetTimingFromCurl(handle, CURLINFO_APPCONNECT_TIME_T));
303 context->CachePerformanceTimingItem(HttpConstant::RESPONSE_FIRST_SEND_TIMING,
304 HttpExec::GetTimingFromCurl(handle, CURLINFO_PRETRANSFER_TIME_T));
305 context->CachePerformanceTimingItem(HttpConstant::RESPONSE_FIRST_RECEIVE_TIMING,
306 HttpExec::GetTimingFromCurl(handle, CURLINFO_STARTTRANSFER_TIME_T));
307 context->CachePerformanceTimingItem(HttpConstant::RESPONSE_TOTAL_FINISH_TIMING,
308 HttpExec::GetTimingFromCurl(handle, CURLINFO_TOTAL_TIME_T));
309 context->CachePerformanceTimingItem(HttpConstant::RESPONSE_REDIRECT_TIMING,
310 HttpExec::GetTimingFromCurl(handle, CURLINFO_REDIRECT_TIME_T));
311 }
312
HandleCurlData(CURLMsg * msg)313 void HttpExec::HandleCurlData(CURLMsg *msg)
314 {
315 if (msg == nullptr) {
316 return;
317 }
318
319 auto handle = msg->easy_handle;
320 if (handle == nullptr) {
321 return;
322 }
323
324 auto it = staticVariable_.contextMap.find(handle);
325 if (it == staticVariable_.contextMap.end()) {
326 NETSTACK_LOGE("can not find context");
327 return;
328 }
329
330 auto context = it->second;
331 staticVariable_.contextMap.erase(it);
332 if (context == nullptr) {
333 NETSTACK_LOGE("can not find context");
334 return;
335 }
336 NETSTACK_LOGD("priority = %{public}d", context->options.GetPriority());
337 context->SetExecOK(GetCurlDataFromHandle(handle, context, msg->msg, msg->data.result));
338 CacheCurlPerformanceTiming(handle, context);
339 if (context->IsExecOK()) {
340 CacheProxy proxy(context->options);
341 proxy.WriteResponseToCache(context->response);
342 }
343 if (context->GetManager() == nullptr) {
344 NETSTACK_LOGE("can not find context manager");
345 return;
346 }
347
348 if (context->IsRequestInStream()) {
349 std::this_thread::sleep_for(std::chrono::milliseconds(CURL_TIMEOUT_MS));
350 NapiUtils::CreateUvQueueWorkEnhanced(context->GetEnv(), context, AsyncWorkRequestInStreamCallback);
351 } else {
352 NapiUtils::CreateUvQueueWorkEnhanced(context->GetEnv(), context, AsyncWorkRequestCallback);
353 }
354 }
355
ExecRequest(RequestContext * context)356 bool HttpExec::ExecRequest(RequestContext *context)
357 {
358 if (!CommonUtils::HasInternetPermission()) {
359 context->SetPermissionDenied(true);
360 return false;
361 }
362 if (context->GetManager()->IsEventDestroy()) {
363 return false;
364 }
365 context->options.SetRequestTime(HttpTime::GetNowTimeGMT());
366 CacheProxy proxy(context->options);
367 if (context->IsUsingCache() && proxy.ReadResponseFromCache(context)) {
368 return true;
369 }
370
371 if (!RequestWithoutCache(context)) {
372 context->SetErrorCode(NapiUtils::NETSTACK_NAPI_INTERNAL_ERROR);
373 if (EventManager::IsManagerValid(context->GetManager())) {
374 if (context->IsRequestInStream()) {
375 std::this_thread::sleep_for(std::chrono::milliseconds(CURL_TIMEOUT_MS));
376 NapiUtils::CreateUvQueueWorkEnhanced(context->GetEnv(), context, AsyncWorkRequestInStreamCallback);
377 } else {
378 NapiUtils::CreateUvQueueWorkEnhanced(context->GetEnv(), context, AsyncWorkRequestCallback);
379 }
380 }
381 return false;
382 }
383
384 return true;
385 }
386
BuildRequestCallback(RequestContext * context)387 napi_value HttpExec::BuildRequestCallback(RequestContext *context)
388 {
389 napi_value object = NapiUtils::CreateObject(context->GetEnv());
390 if (NapiUtils::GetValueType(context->GetEnv(), object) != napi_object) {
391 return nullptr;
392 }
393
394 NapiUtils::SetUint32Property(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESPONSE_CODE,
395 context->response.GetResponseCode());
396 NapiUtils::SetStringPropertyUtf8(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_COOKIES,
397 context->response.GetCookies());
398
399 napi_value header = MakeResponseHeader(context->GetEnv(), context);
400 if (NapiUtils::GetValueType(context->GetEnv(), header) == napi_object) {
401 NapiUtils::SetNamedProperty(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_HEADER, header);
402 }
403
404 if (context->options.GetHttpDataType() != HttpDataType::NO_DATA_TYPE && ProcByExpectDataType(object, context)) {
405 return object;
406 }
407
408 auto contentType = CommonUtils::ToLower(const_cast<std::map<std::string, std::string> &>(
409 context->response.GetHeader())[HttpConstant::HTTP_CONTENT_TYPE]);
410 if (contentType.find(HttpConstant::HTTP_CONTENT_TYPE_OCTET_STREAM) != std::string::npos ||
411 contentType.find(HttpConstant::HTTP_CONTENT_TYPE_IMAGE) != std::string::npos) {
412 void *data = nullptr;
413 auto body = context->response.GetResult();
414 napi_value arrayBuffer = NapiUtils::CreateArrayBuffer(context->GetEnv(), body.size(), &data);
415 if (data != nullptr && arrayBuffer != nullptr) {
416 if (memcpy_s(data, body.size(), body.c_str(), body.size()) != EOK) {
417 NETSTACK_LOGE("memcpy_s failed!");
418 return object;
419 }
420 NapiUtils::SetNamedProperty(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT, arrayBuffer);
421 }
422 NapiUtils::SetUint32Property(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT_TYPE,
423 static_cast<uint32_t>(HttpDataType::ARRAY_BUFFER));
424 return object;
425 }
426
427 /* now just support utf8 */
428 NapiUtils::SetStringPropertyUtf8(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT,
429 context->response.GetResult());
430 NapiUtils::SetUint32Property(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT_TYPE,
431 static_cast<uint32_t>(HttpDataType::STRING));
432 return object;
433 }
434
RequestCallback(RequestContext * context)435 napi_value HttpExec::RequestCallback(RequestContext *context)
436 {
437 napi_value result = HttpExec::BuildRequestCallback(context);
438 context->StopAndCacheNapiPerformanceTiming(HttpConstant::RESPONSE_TOTAL_TIMING);
439 context->SetPerformanceTimingToResult(result);
440 return result;
441 }
442
RequestInStreamCallback(OHOS::NetStack::Http::RequestContext * context)443 napi_value HttpExec::RequestInStreamCallback(OHOS::NetStack::Http::RequestContext *context)
444 {
445 napi_value number = NapiUtils::CreateUint32(context->GetEnv(), context->response.GetResponseCode());
446 if (NapiUtils::GetValueType(context->GetEnv(), number) != napi_number) {
447 return nullptr;
448 }
449 return number;
450 }
451
MakeUrl(const std::string & url,std::string param,const std::string & extraParam)452 std::string HttpExec::MakeUrl(const std::string &url, std::string param, const std::string &extraParam)
453 {
454 if (param.empty()) {
455 param += extraParam;
456 } else {
457 param += HttpConstant::HTTP_URL_PARAM_SEPARATOR;
458 param += extraParam;
459 }
460
461 if (param.empty()) {
462 return url;
463 }
464
465 return url + HttpConstant::HTTP_URL_PARAM_START + param;
466 }
467
MethodForGet(const std::string & method)468 bool HttpExec::MethodForGet(const std::string &method)
469 {
470 return (method == HttpConstant::HTTP_METHOD_HEAD || method == HttpConstant::HTTP_METHOD_OPTIONS ||
471 method == HttpConstant::HTTP_METHOD_TRACE || method == HttpConstant::HTTP_METHOD_GET ||
472 method == HttpConstant::HTTP_METHOD_CONNECT);
473 }
474
MethodForPost(const std::string & method)475 bool HttpExec::MethodForPost(const std::string &method)
476 {
477 return (method == HttpConstant::HTTP_METHOD_POST || method == HttpConstant::HTTP_METHOD_PUT ||
478 method == HttpConstant::HTTP_METHOD_DELETE);
479 }
480
EncodeUrlParam(std::string & str)481 bool HttpExec::EncodeUrlParam(std::string &str)
482 {
483 char encoded[4];
484 std::string encodeOut;
485 size_t length = strlen(str.c_str());
486 for (size_t i = 0; i < length; ++i) {
487 auto c = static_cast<uint8_t>(str.c_str()[i]);
488 if (IsUnReserved(c)) {
489 encodeOut += static_cast<char>(c);
490 } else {
491 if (sprintf_s(encoded, sizeof(encoded), "%%%02X", c) < 0) {
492 return false;
493 }
494 encodeOut += encoded;
495 }
496 }
497
498 if (str == encodeOut) {
499 return false;
500 }
501 str = encodeOut;
502 return true;
503 }
504
AddRequestInfo()505 void HttpExec::AddRequestInfo()
506 {
507 std::lock_guard guard(staticVariable_.curlMultiMutex);
508 int num = 0;
509 while (!staticVariable_.infoQueue.empty()) {
510 if (!staticVariable_.runThread || staticVariable_.curlMulti == nullptr) {
511 break;
512 }
513
514 auto info = staticVariable_.infoQueue.top();
515 staticVariable_.infoQueue.pop();
516 auto ret = curl_multi_add_handle(staticVariable_.curlMulti, info.handle);
517 if (ret == CURLM_OK) {
518 staticVariable_.contextMap[info.handle] = info.context;
519 }
520
521 ++num;
522 if (num >= CURL_HANDLE_NUM) {
523 break;
524 }
525 }
526 }
527
IsContextDeleted(RequestContext * context)528 bool HttpExec::IsContextDeleted(RequestContext *context)
529 {
530 if (context == nullptr) {
531 return true;
532 }
533 {
534 std::lock_guard<std::mutex> lockGuard(HttpExec::staticContextSet_.mutexForContextVec);
535 auto it = std::find(HttpExec::staticContextSet_.contextSet.begin(),
536 HttpExec::staticContextSet_.contextSet.end(), context);
537 if (it == HttpExec::staticContextSet_.contextSet.end()) {
538 NETSTACK_LOGI("context has been deleted in libuv thread");
539 return true;
540 }
541 }
542 return false;
543 }
544
RunThread()545 void HttpExec::RunThread()
546 {
547 while (staticVariable_.runThread && staticVariable_.curlMulti != nullptr) {
548 AddRequestInfo();
549 SendRequest();
550 ReadResponse();
551 std::this_thread::sleep_for(std::chrono::milliseconds(CURL_TIMEOUT_MS));
552 std::unique_lock l(staticVariable_.curlMultiMutex);
553 staticVariable_.conditionVariable.wait_for(l, std::chrono::seconds(CONDITION_TIMEOUT_S), [] {
554 return !staticVariable_.infoQueue.empty() || !staticVariable_.contextMap.empty();
555 });
556 }
557 }
558
SendRequest()559 void HttpExec::SendRequest()
560 {
561 std::lock_guard guard(staticVariable_.curlMultiMutex);
562
563 int runningHandle = 0;
564 int num = 0;
565 do {
566 if (!staticVariable_.runThread || staticVariable_.curlMulti == nullptr) {
567 break;
568 }
569
570 auto ret = curl_multi_perform(staticVariable_.curlMulti, &runningHandle);
571
572 if (runningHandle > 0) {
573 ret = curl_multi_poll(staticVariable_.curlMulti, nullptr, 0, CURL_MAX_WAIT_MSECS, nullptr);
574 }
575
576 if (ret != CURLM_OK) {
577 return;
578 }
579
580 ++num;
581 if (num >= CURL_HANDLE_NUM) {
582 break;
583 }
584 } while (runningHandle > 0);
585 }
586
ReadResponse()587 void HttpExec::ReadResponse()
588 {
589 std::lock_guard guard(staticVariable_.curlMultiMutex);
590 CURLMsg *msg = nullptr; /* NOLINT */
591 do {
592 if (!staticVariable_.runThread || staticVariable_.curlMulti == nullptr) {
593 break;
594 }
595
596 int leftMsg;
597 msg = curl_multi_info_read(staticVariable_.curlMulti, &leftMsg);
598 if (msg) {
599 if (msg->msg == CURLMSG_DONE) {
600 HandleCurlData(msg);
601 }
602 if (msg->easy_handle) {
603 (void)curl_multi_remove_handle(staticVariable_.curlMulti, msg->easy_handle);
604 (void)curl_easy_cleanup(msg->easy_handle);
605 }
606 }
607 } while (msg);
608 }
609
GetGlobalHttpProxyInfo(std::string & host,int32_t & port,std::string & exclusions)610 void HttpExec::GetGlobalHttpProxyInfo(std::string &host, int32_t &port, std::string &exclusions)
611 {
612 #ifdef HTTP_PROXY_ENABLE
613 char httpProxyHost[SYSPARA_MAX_SIZE] = {0};
614 char httpProxyPort[SYSPARA_MAX_SIZE] = {0};
615 char httpProxyExclusions[SYSPARA_MAX_SIZE] = {0};
616 GetParameter(HTTP_PROXY_HOST_KEY, DEFAULT_HTTP_PROXY_HOST, httpProxyHost, sizeof(httpProxyHost));
617 GetParameter(HTTP_PROXY_PORT_KEY, DEFAULT_HTTP_PROXY_PORT, httpProxyPort, sizeof(httpProxyPort));
618 GetParameter(HTTP_PROXY_EXCLUSIONS_KEY, DEFAULT_HTTP_PROXY_EXCLUSION_LIST, httpProxyExclusions,
619 sizeof(httpProxyExclusions));
620
621 host = Base64::Decode(httpProxyHost);
622 if (host == DEFAULT_HTTP_PROXY_HOST) {
623 host = std::string();
624 }
625 exclusions = httpProxyExclusions;
626 if (exclusions == DEFAULT_HTTP_PROXY_EXCLUSION_LIST) {
627 exclusions = std::string();
628 }
629
630 port = std::atoi(httpProxyPort);
631 #endif
632 }
633
GetHttpProxyInfo(RequestContext * context,std::string & host,int32_t & port,std::string & exclusions)634 void HttpExec::GetHttpProxyInfo(RequestContext *context, std::string &host, int32_t &port, std::string &exclusions)
635 {
636 if (context->options.GetUsingHttpProxyType() == UsingHttpProxyType::USE_DEFAULT) {
637 #ifdef HAS_NETMANAGER_BASE
638 using namespace NetManagerStandard;
639 HttpProxy httpProxy;
640 NetConnClient::GetInstance().GetDefaultHttpProxy(httpProxy);
641 host = httpProxy.GetHost();
642 port = httpProxy.GetPort();
643 exclusions = CommonUtils::ToString(httpProxy.GetExclusionList());
644 #else
645 GetGlobalHttpProxyInfo(host, port, exclusions);
646 #endif
647 } else if (context->options.GetUsingHttpProxyType() == UsingHttpProxyType::USE_SPECIFIED) {
648 context->options.GetSpecifiedHttpProxy(host, port, exclusions);
649 }
650 }
651
Initialize()652 bool HttpExec::Initialize()
653 {
654 std::lock_guard<std::mutex> lock(staticVariable_.mutexForInitialize);
655 if (staticVariable_.initialized) {
656 return true;
657 }
658 NETSTACK_LOGD("call curl_global_init");
659 if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
660 NETSTACK_LOGE("Failed to initialize 'curl'");
661 return false;
662 }
663
664 staticVariable_.curlMulti = curl_multi_init();
665 if (staticVariable_.curlMulti == nullptr) {
666 NETSTACK_LOGE("Failed to initialize 'curl_multi'");
667 return false;
668 }
669
670 staticVariable_.workThread = std::thread(RunThread);
671
672 staticVariable_.initialized = true;
673 return staticVariable_.initialized;
674 }
675
SetOtherOption(CURL * curl,OHOS::NetStack::Http::RequestContext * context)676 bool HttpExec::SetOtherOption(CURL *curl, OHOS::NetStack::Http::RequestContext *context)
677 {
678 std::string url = context->options.GetUrl();
679 std::string host, exclusions;
680 int32_t port = 0;
681 GetHttpProxyInfo(context, host, port, exclusions);
682 if (!host.empty() && !CommonUtils::IsHostNameExcluded(url, exclusions, ",")) {
683 NETSTACK_LOGD("Set CURLOPT_PROXY: %{public}s:%{public}d, %{public}s", host.c_str(), port, exclusions.c_str());
684 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_PROXY, host.c_str(), context);
685 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_PROXYPORT, port, context);
686 auto curlTunnelValue = (url.find("https://") != std::string::npos) ? 1L : 0L;
687 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_HTTPPROXYTUNNEL, curlTunnelValue, context);
688 auto proxyType = (host.find("https://") != std::string::npos) ? CURLPROXY_HTTPS : CURLPROXY_HTTP;
689 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_PROXYTYPE, proxyType, context);
690 }
691 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2, context);
692 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_CIPHER_LIST, TLS12_SECURITY_CIPHER_SUITE, context);
693
694 #ifdef NETSTACK_PROXY_PASS
695 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_PROXYUSERPWD, NETSTACK_PROXY_PASS, context);
696 #endif // NETSTACK_PROXY_PASS
697
698 #ifdef HTTP_CURL_PRINT_VERBOSE
699 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_VERBOSE, 1L, context);
700 #endif
701
702 #ifndef WINDOWS_PLATFORM
703 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_ACCEPT_ENCODING, "", context);
704 #endif
705 return true;
706 }
707
SetSSLCertOption(CURL * curl,OHOS::NetStack::Http::RequestContext * context)708 bool HttpExec::SetSSLCertOption(CURL *curl, OHOS::NetStack::Http::RequestContext *context)
709 {
710 std::string cert;
711 std::string certType;
712 std::string key;
713 Secure::SecureChar keyPasswd;
714 context->options.GetClientCert(cert, certType, key, keyPasswd);
715 if (cert.empty()) {
716 NETSTACK_LOGD("SetSSLCertOption param is empty.");
717 return false;
718 }
719 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSLCERT, cert.c_str(), context);
720 if (!key.empty()) {
721 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSLKEY, key.c_str(), context);
722 }
723 if (!certType.empty()) {
724 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSLCERTTYPE, certType.c_str(), context);
725 }
726 if (keyPasswd.Length() > 0) {
727 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_KEYPASSWD, keyPasswd.Data(), context);
728 }
729 return true;
730 }
731
SetServerSSLCertOption(CURL * curl,OHOS::NetStack::Http::RequestContext * context)732 bool HttpExec::SetServerSSLCertOption(CURL *curl, OHOS::NetStack::Http::RequestContext *context)
733 {
734 #ifndef NO_SSL_CERTIFICATION
735 #ifdef HAS_NETMANAGER_BASE
736 auto hostname = CommonUtils::GetHostnameFromURL(context->options.GetUrl());
737 #ifndef WINDOWS_PLATFORM
738 // customize trusted CAs.
739 std::vector<std::string> certs;
740 auto ret = NetManagerStandard::NetConnClient::GetInstance().GetTrustAnchorsForHostName(hostname, certs);
741 if (ret != 0) {
742 NETSTACK_LOGD("Get no trust anchor by host name[%{public}s], ret[%{public}d]", hostname.c_str(), ret);
743 } else {
744 std::string *pCert = nullptr;
745 for (auto &cert : certs) {
746 if (!cert.empty()) {
747 pCert = &cert;
748 break;
749 }
750 }
751 if (pCert != nullptr) {
752 NETSTACK_LOGD("curl set option capath: capath=%{public}s.", pCert->c_str());
753 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_CAINFO, nullptr, context);
754 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_CAPATH, pCert->c_str(), context);
755 } else {
756 NETSTACK_LOGD("Get no trust anchor by host name[%{public}s]", hostname.c_str());
757 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_CAINFO, context->options.GetCaPath().c_str(), context);
758 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_CAPATH, HttpConstant::HTTP_PREPARE_CA_PATH, context);
759 }
760 }
761 #endif // WINDOWS_PLATFORM
762 // pin trusted certifcate keys.
763 std::string pins;
764 auto ret1 = NetManagerStandard::NetConnClient::GetInstance().GetPinSetForHostName(hostname, pins);
765 if (ret1 != 0 || pins.empty()) {
766 NETSTACK_LOGD("Get no pinset by host name[%{public}s]", hostname.c_str());
767 } else {
768 NETSTACK_LOGD("curl set pin =[%{public}s]", pins.c_str());
769 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_PINNEDPUBLICKEY, pins.c_str(), context);
770 }
771 #endif // HAS_NETMANAGER_BASE
772 #else
773 // in real life, you should buy a ssl certification and rename it to /etc/ssl/cert.pem
774 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_VERIFYHOST, 0L, context);
775 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_VERIFYPEER, 0L, context);
776 #endif // NO_SSL_CERTIFICATION
777
778 return true;
779 }
780
SetDnsOption(CURL * curl,RequestContext * context)781 bool HttpExec::SetDnsOption(CURL *curl, RequestContext *context)
782 {
783 std::vector<std::string> dnsServers = context->options.GetDnsServers();
784 if (dnsServers.empty()) {
785 return true;
786 }
787 std::string serverList;
788 for (auto &server : dnsServers) {
789 serverList += server + ",";
790 NETSTACK_LOGD("SetDns server: %{public}s", CommonUtils::AnonymizeIp(server).c_str());
791 }
792 serverList.pop_back();
793 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_DNS_SERVERS, serverList.c_str(), context);
794 return true;
795 }
796
SetRequestOption(CURL * curl,RequestContext * context)797 bool HttpExec::SetRequestOption(CURL *curl, RequestContext *context)
798 {
799 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_HTTP_VERSION, context->options.GetHttpVersion(), context);
800 const std::string range = context->options.GetRangeString();
801 if (range.empty()) {
802 // Some servers don't like requests that are made without a user-agent field, so we provide one
803 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_USERAGENT, HttpConstant::HTTP_DEFAULT_USER_AGENT, context);
804 } else {
805 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_RANGE, range.c_str(), context);
806 }
807 if (!context->options.GetDohUrl().empty()) {
808 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_DOH_URL, context->options.GetDohUrl().c_str(), context);
809 }
810 SetDnsOption(curl, context);
811 SetSSLCertOption(curl, context);
812 SetMultiPartOption(curl, context);
813 return true;
814 }
815
SetOption(CURL * curl,RequestContext * context,struct curl_slist * requestHeader)816 bool HttpExec::SetOption(CURL *curl, RequestContext *context, struct curl_slist *requestHeader)
817 {
818 const std::string &method = context->options.GetMethod();
819 if (!MethodForGet(method) && !MethodForPost(method)) {
820 NETSTACK_LOGE("method %{public}s not supported", method.c_str());
821 return false;
822 }
823
824 if (context->options.GetMethod() == HttpConstant::HTTP_METHOD_HEAD) {
825 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_NOBODY, 1L, context);
826 }
827
828 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_URL, context->options.GetUrl().c_str(), context);
829 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_CUSTOMREQUEST, method.c_str(), context);
830
831 if (MethodForPost(method) && !context->options.GetBody().empty()) {
832 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_POST, 1L, context);
833 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_POSTFIELDS, context->options.GetBody().c_str(), context);
834 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_POSTFIELDSIZE, context->options.GetBody().size(), context);
835 }
836
837 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_XFERINFOFUNCTION, ProgressCallback, context);
838 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_XFERINFODATA, context, context);
839 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_NOPROGRESS, 0L, context);
840
841 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_WRITEFUNCTION, OnWritingMemoryBody, context);
842 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_WRITEDATA, context, context);
843
844 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_HEADERFUNCTION, OnWritingMemoryHeader, context);
845 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_HEADERDATA, context, context);
846 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_HTTPHEADER, requestHeader, context);
847 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_FOLLOWLOCATION, 1L, context);
848
849 /* first #undef CURL_DISABLE_COOKIES in curl config */
850 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_COOKIEFILE, "", context);
851 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_NOSIGNAL, 1L, context);
852 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_TIMEOUT_MS, context->options.GetReadTimeout(), context);
853 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_CONNECTTIMEOUT_MS, context->options.GetConnectTimeout(), context);
854
855 SetRequestOption(curl, context);
856
857 if (!SetOtherOption(curl, context)) {
858 return false;
859 }
860 return true;
861 }
862
OnWritingMemoryBody(const void * data,size_t size,size_t memBytes,void * userData)863 size_t HttpExec::OnWritingMemoryBody(const void *data, size_t size, size_t memBytes, void *userData)
864 {
865 auto context = static_cast<RequestContext *>(userData);
866 if (context == nullptr) {
867 return 0;
868 }
869 if (context->GetManager()->IsEventDestroy()) {
870 context->StopAndCacheNapiPerformanceTiming(HttpConstant::RESPONSE_BODY_TIMING);
871 return 0;
872 }
873 if (context->IsRequestInStream()) {
874 context->SetTempData(data, size * memBytes);
875 NapiUtils::CreateUvQueueWorkEnhanced(context->GetEnv(), context, OnDataReceive);
876 context->StopAndCacheNapiPerformanceTiming(HttpConstant::RESPONSE_BODY_TIMING);
877 return size * memBytes;
878 }
879 if (context->response.GetResult().size() > context->options.GetMaxLimit()) {
880 NETSTACK_LOGE("response data exceeds the maximum limit");
881 context->StopAndCacheNapiPerformanceTiming(HttpConstant::RESPONSE_BODY_TIMING);
882 return 0;
883 }
884 context->response.AppendResult(data, size * memBytes);
885 context->StopAndCacheNapiPerformanceTiming(HttpConstant::RESPONSE_BODY_TIMING);
886 return size * memBytes;
887 }
888
MakeSetCookieArray(napi_env env,napi_value header,const std::pair<const std::basic_string<char>,std::basic_string<char>> & headerElement)889 static void MakeSetCookieArray(napi_env env, napi_value header,
890 const std::pair<const std::basic_string<char>, std::basic_string<char>> &headerElement)
891 {
892 std::vector<std::string> cookieVec =
893 CommonUtils::Split(headerElement.second, HttpConstant::RESPONSE_KEY_SET_COOKIE_SEPARATOR);
894 uint32_t index = 0;
895 auto len = cookieVec.size();
896 auto array = NapiUtils::CreateArray(env, len);
897 for (const auto &setCookie : cookieVec) {
898 auto str = NapiUtils::CreateStringUtf8(env, setCookie);
899 NapiUtils::SetArrayElement(env, array, index, str);
900 ++index;
901 }
902 NapiUtils::SetArrayProperty(env, header, HttpConstant::RESPONSE_KEY_SET_COOKIE, array);
903 }
904
MakeHeaderWithSetCookieArray(napi_env env,napi_value header,std::map<std::string,std::string> * headerMap)905 static void MakeHeaderWithSetCookieArray(napi_env env, napi_value header, std::map<std::string, std::string> *headerMap)
906 {
907 for (const auto &it : *headerMap) {
908 if (!it.first.empty() && !it.second.empty()) {
909 if (it.first == HttpConstant::RESPONSE_KEY_SET_COOKIE) {
910 MakeSetCookieArray(env, header, it);
911 continue;
912 }
913 NapiUtils::SetStringPropertyUtf8(env, header, it.first, it.second);
914 }
915 }
916 }
917
ResponseHeaderCallback(uv_work_t * work,int status)918 static void ResponseHeaderCallback(uv_work_t *work, int status)
919 {
920 (void)status;
921
922 auto workWrapper = static_cast<UvWorkWrapper *>(work->data);
923 napi_env env = workWrapper->env;
924 auto headerMap = static_cast<std::map<std::string, std::string> *>(workWrapper->data);
925 auto closeScope = [env](napi_handle_scope scope) { NapiUtils::CloseScope(env, scope); };
926 std::unique_ptr<napi_handle_scope__, decltype(closeScope)> scope(NapiUtils::OpenScope(env), closeScope);
927 napi_value header = NapiUtils::CreateObject(env);
928 if (NapiUtils::GetValueType(env, header) == napi_object) {
929 MakeHeaderWithSetCookieArray(env, header, headerMap);
930 }
931 std::pair<napi_value, napi_value> arg = {NapiUtils::GetUndefined(env), header};
932 workWrapper->manager->Emit(workWrapper->type, arg);
933 delete headerMap;
934 headerMap = nullptr;
935 delete workWrapper;
936 workWrapper = nullptr;
937 delete work;
938 work = nullptr;
939 }
940
MakeHeaderWithSetCookie(RequestContext * context)941 static std::map<std::string, std::string> MakeHeaderWithSetCookie(RequestContext * context)
942 {
943 std::map<std::string, std::string> tempMap = context->response.GetHeader();
944 std::string setCookies;
945 size_t loop = 0;
946 for (const auto &setCookie : context->response.GetsetCookie()) {
947 setCookies += setCookie;
948 if (loop + 1 < context->response.GetsetCookie().size()) {
949 setCookies += HttpConstant::RESPONSE_KEY_SET_COOKIE_SEPARATOR;
950 }
951 ++loop;
952 }
953 tempMap[HttpConstant::RESPONSE_KEY_SET_COOKIE] = setCookies;
954 return tempMap;
955 }
956
OnWritingMemoryHeader(const void * data,size_t size,size_t memBytes,void * userData)957 size_t HttpExec::OnWritingMemoryHeader(const void *data, size_t size, size_t memBytes, void *userData)
958 {
959 auto context = static_cast<RequestContext *>(userData);
960 if (context == nullptr) {
961 return 0;
962 }
963 if (context->GetManager()->IsEventDestroy()) {
964 context->StopAndCacheNapiPerformanceTiming(HttpConstant::RESPONSE_HEADER_TIMING);
965 return 0;
966 }
967 if (context->response.GetResult().size() > context->options.GetMaxLimit()) {
968 NETSTACK_LOGE("response data exceeds the maximum limit");
969 context->StopAndCacheNapiPerformanceTiming(HttpConstant::RESPONSE_HEADER_TIMING);
970 return 0;
971 }
972 context->response.AppendRawHeader(data, size * memBytes);
973 if (CommonUtils::EndsWith(context->response.GetRawHeader(), HttpConstant::HTTP_RESPONSE_HEADER_SEPARATOR)) {
974 context->response.ParseHeaders();
975 if (context->GetManager() && EventManager::IsManagerValid(context->GetManager())) {
976 auto headerMap = new std::map<std::string, std::string>(MakeHeaderWithSetCookie(context));
977 context->GetManager()->EmitByUv(ON_HEADER_RECEIVE, headerMap, ResponseHeaderCallback);
978 auto headersMap = new std::map<std::string, std::string>(MakeHeaderWithSetCookie(context));
979 context->GetManager()->EmitByUv(ON_HEADERS_RECEIVE, headersMap, ResponseHeaderCallback);
980 }
981 }
982 context->StopAndCacheNapiPerformanceTiming(HttpConstant::RESPONSE_HEADER_TIMING);
983 return size * memBytes;
984 }
985
OnDataReceive(napi_env env,napi_status status,void * data)986 void HttpExec::OnDataReceive(napi_env env, napi_status status, void *data)
987 {
988 auto context = static_cast<RequestContext *>(data);
989 if (context == nullptr) {
990 NETSTACK_LOGE("context is nullptr");
991 return;
992 }
993
994 void *buffer = nullptr;
995 auto tempData = context->GetTempData();
996 context->PopTempData();
997 if (tempData.empty()) {
998 NETSTACK_LOGI("[GetTempData] tempDate is empty!");
999 return;
1000 }
1001 napi_value arrayBuffer = NapiUtils::CreateArrayBuffer(context->GetEnv(), tempData.size(), &buffer);
1002 if (buffer == nullptr || arrayBuffer == nullptr) {
1003 return;
1004 }
1005 if (memcpy_s(buffer, tempData.size(), tempData.data(), tempData.size()) != EOK) {
1006 NETSTACK_LOGE("[CreateArrayBuffer] memory copy failed");
1007 return;
1008 }
1009 context->Emit(ON_DATA_RECEIVE, std::make_pair(NapiUtils::GetUndefined(context->GetEnv()), arrayBuffer));
1010 }
1011
OnDataProgress(napi_env env,napi_status status,void * data)1012 void HttpExec::OnDataProgress(napi_env env, napi_status status, void *data)
1013 {
1014 auto context = static_cast<RequestContext *>(data);
1015 if (IsContextDeleted(context)) {
1016 return;
1017 }
1018 auto progress = NapiUtils::CreateObject(context->GetEnv());
1019 if (NapiUtils::GetValueType(context->GetEnv(), progress) == napi_undefined) {
1020 return;
1021 }
1022 auto dlLen = context->GetDlLen();
1023 if (dlLen.tLen && dlLen.nLen) {
1024 NapiUtils::SetUint32Property(context->GetEnv(), progress, "receiveSize", static_cast<uint32_t>(dlLen.nLen));
1025 NapiUtils::SetUint32Property(context->GetEnv(), progress, "totalSize", static_cast<uint32_t>(dlLen.tLen));
1026
1027 context->Emit(ON_DATA_RECEIVE_PROGRESS, std::make_pair(NapiUtils::GetUndefined(context->GetEnv()), progress));
1028 }
1029 }
1030
OnDataUploadProgress(napi_env env,napi_status status,void * data)1031 void HttpExec::OnDataUploadProgress(napi_env env, napi_status status, void *data)
1032 {
1033 auto context = static_cast<RequestContext *>(data);
1034 if (IsContextDeleted(context)) {
1035 NETSTACK_LOGD("[OnDataUploadProgress] context is null.");
1036 return;
1037 }
1038 auto progress = NapiUtils::CreateObject(context->GetEnv());
1039 if (NapiUtils::GetValueType(context->GetEnv(), progress) == napi_undefined) {
1040 NETSTACK_LOGD("[OnDataUploadProgress] napi_undefined.");
1041 return;
1042 }
1043 NapiUtils::SetUint32Property(context->GetEnv(), progress, "sendSize",
1044 static_cast<uint32_t>(context->GetUlLen().nLen));
1045 NapiUtils::SetUint32Property(context->GetEnv(), progress, "totalSize",
1046 static_cast<uint32_t>(context->GetUlLen().tLen));
1047 context->Emit(ON_DATA_SEND_PROGRESS, std::make_pair(NapiUtils::GetUndefined(context->GetEnv()), progress));
1048 }
1049
ProgressCallback(void * userData,curl_off_t dltotal,curl_off_t dlnow,curl_off_t ultotal,curl_off_t ulnow)1050 int HttpExec::ProgressCallback(void *userData, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal,
1051 curl_off_t ulnow)
1052 {
1053 auto context = static_cast<RequestContext *>(userData);
1054 if (context == nullptr) {
1055 return 0;
1056 }
1057 if (ultotal != 0 && ultotal >= ulnow && !context->CompareWithLastElement(ulnow, ultotal)) {
1058 context->SetUlLen(ulnow, ultotal);
1059 NapiUtils::CreateUvQueueWorkEnhanced(context->GetEnv(), context, OnDataUploadProgress);
1060 }
1061 if (!context->IsRequestInStream()) {
1062 return 0;
1063 }
1064 if (context->GetManager()->IsEventDestroy()) {
1065 return 0;
1066 }
1067 if (dltotal != 0) {
1068 context->SetDlLen(dlnow, dltotal);
1069 NapiUtils::CreateUvQueueWorkEnhanced(context->GetEnv(), context, OnDataProgress);
1070 }
1071 return 0;
1072 }
1073
MakeHeaders(const std::vector<std::string> & vec)1074 struct curl_slist *HttpExec::MakeHeaders(const std::vector<std::string> &vec)
1075 {
1076 struct curl_slist *header = nullptr;
1077 std::for_each(vec.begin(), vec.end(), [&header](const std::string &s) {
1078 if (!s.empty()) {
1079 header = curl_slist_append(header, s.c_str());
1080 }
1081 });
1082 return header;
1083 }
1084
MakeResponseHeader(napi_env env,void * ctx)1085 napi_value HttpExec::MakeResponseHeader(napi_env env, void *ctx)
1086 {
1087 auto context = reinterpret_cast<RequestContext *>(ctx);
1088 (void)env;
1089 napi_value header = NapiUtils::CreateObject(context->GetEnv());
1090 if (NapiUtils::GetValueType(context->GetEnv(), header) == napi_object) {
1091 for (const auto &it : context->response.GetHeader()) {
1092 if (!it.first.empty() && !it.second.empty()) {
1093 NapiUtils::SetStringPropertyUtf8(context->GetEnv(), header, it.first, it.second);
1094 }
1095 }
1096 if (!context->response.GetsetCookie().empty()) {
1097 uint32_t index = 0;
1098 auto len = context->response.GetsetCookie().size();
1099 auto array = NapiUtils::CreateArray(context->GetEnv(), len);
1100 for (const auto &setCookie : context->response.GetsetCookie()) {
1101 auto str = NapiUtils::CreateStringUtf8(context->GetEnv(), setCookie);
1102 NapiUtils::SetArrayElement(context->GetEnv(), array, index, str);
1103 ++index;
1104 }
1105 NapiUtils::SetArrayProperty(context->GetEnv(), header, HttpConstant::RESPONSE_KEY_SET_COOKIE, array);
1106 }
1107 }
1108 return header;
1109 }
1110
IsUnReserved(unsigned char in)1111 bool HttpExec::IsUnReserved(unsigned char in)
1112 {
1113 if ((in >= '0' && in <= '9') || (in >= 'a' && in <= 'z') || (in >= 'A' && in <= 'Z')) {
1114 return true;
1115 }
1116 switch (in) {
1117 case '-':
1118 case '.':
1119 case '_':
1120 case '~':
1121 return true;
1122 default:
1123 break;
1124 }
1125 return false;
1126 }
1127
ProcByExpectDataType(napi_value object,RequestContext * context)1128 bool HttpExec::ProcByExpectDataType(napi_value object, RequestContext *context)
1129 {
1130 switch (context->options.GetHttpDataType()) {
1131 case HttpDataType::STRING: {
1132 NapiUtils::SetStringPropertyUtf8(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT,
1133 context->response.GetResult());
1134 NapiUtils::SetUint32Property(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT_TYPE,
1135 static_cast<uint32_t>(HttpDataType::STRING));
1136 return true;
1137 }
1138 case HttpDataType::OBJECT: {
1139 if (context->response.GetResult().size() > HttpConstant::MAX_JSON_PARSE_SIZE) {
1140 return false;
1141 }
1142
1143 napi_value obj = NapiUtils::JsonParse(context->GetEnv(), context->response.GetResult());
1144 if (obj) {
1145 NapiUtils::SetNamedProperty(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT, obj);
1146 NapiUtils::SetUint32Property(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT_TYPE,
1147 static_cast<uint32_t>(HttpDataType::OBJECT));
1148 return true;
1149 }
1150
1151 // parse maybe failed
1152 return false;
1153 }
1154 case HttpDataType::ARRAY_BUFFER: {
1155 void *data = nullptr;
1156 auto body = context->response.GetResult();
1157 napi_value arrayBuffer = NapiUtils::CreateArrayBuffer(context->GetEnv(), body.size(), &data);
1158 if (data != nullptr && arrayBuffer != nullptr) {
1159 if (memcpy_s(data, body.size(), body.c_str(), body.size()) < 0) {
1160 NETSTACK_LOGE("[ProcByExpectDataType] memory copy failed");
1161 return true;
1162 }
1163 NapiUtils::SetNamedProperty(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT, arrayBuffer);
1164 NapiUtils::SetUint32Property(context->GetEnv(), object, HttpConstant::RESPONSE_KEY_RESULT_TYPE,
1165 static_cast<uint32_t>(HttpDataType::ARRAY_BUFFER));
1166 }
1167 return true;
1168 }
1169 default:
1170 break;
1171 }
1172 return false;
1173 }
1174
1175 #ifndef MAC_PLATFORM
AsyncRunRequest(RequestContext * context)1176 void HttpExec::AsyncRunRequest(RequestContext *context)
1177 {
1178 HttpAsyncWork::ExecRequest(context->GetEnv(), context);
1179 }
1180 #endif
1181
IsInitialized()1182 bool HttpExec::IsInitialized()
1183 {
1184 return staticVariable_.initialized;
1185 }
1186
DeInitialize()1187 void HttpExec::DeInitialize()
1188 {
1189 std::lock_guard<std::mutex> lock(staticVariable_.curlMultiMutex);
1190 staticVariable_.runThread = false;
1191 staticVariable_.conditionVariable.notify_all();
1192 if (staticVariable_.workThread.joinable()) {
1193 staticVariable_.workThread.join();
1194 }
1195 if (staticVariable_.curlMulti) {
1196 curl_multi_cleanup(staticVariable_.curlMulti);
1197 }
1198 staticVariable_.initialized = false;
1199 }
1200
ExecFlush(BaseContext * context)1201 bool HttpResponseCacheExec::ExecFlush(BaseContext *context)
1202 {
1203 (void)context;
1204 CacheProxy::FlushCache();
1205 return true;
1206 }
1207
FlushCallback(BaseContext * context)1208 napi_value HttpResponseCacheExec::FlushCallback(BaseContext *context)
1209 {
1210 return NapiUtils::GetUndefined(context->GetEnv());
1211 }
1212
ExecDelete(BaseContext * context)1213 bool HttpResponseCacheExec::ExecDelete(BaseContext *context)
1214 {
1215 (void)context;
1216 CacheProxy::StopCacheAndDelete();
1217 return true;
1218 }
1219
DeleteCallback(BaseContext * context)1220 napi_value HttpResponseCacheExec::DeleteCallback(BaseContext *context)
1221 {
1222 return NapiUtils::GetUndefined(context->GetEnv());
1223 }
1224
SetMultiPartOption(CURL * curl,RequestContext * context)1225 bool HttpExec::SetMultiPartOption(CURL *curl, RequestContext *context)
1226 {
1227 auto header = context->options.GetHeader();
1228 auto type = CommonUtils::ToLower(header[HttpConstant::HTTP_CONTENT_TYPE]);
1229 if (type != HttpConstant::HTTP_CONTENT_TYPE_MULTIPART) {
1230 return true;
1231 }
1232 auto multiPartDataList = context->options.GetMultiPartDataList();
1233 if (multiPartDataList.empty()) {
1234 return true;
1235 }
1236 curl_mime *multipart = curl_mime_init(curl);
1237 if (multipart == nullptr) {
1238 return false;
1239 }
1240 context->SetMultipart(multipart);
1241 curl_mimepart *part = nullptr;
1242 bool hasData = false;
1243 for (auto &multiFormData : multiPartDataList) {
1244 if (multiFormData.name.empty()) {
1245 continue;
1246 }
1247 if (multiFormData.data.empty() && multiFormData.filePath.empty()) {
1248 NETSTACK_LOGE("Failed to set name %{public}s, error no data and filepath at the same time",
1249 multiFormData.name.c_str());
1250 continue;
1251 }
1252 part = curl_mime_addpart(multipart);
1253 SetFormDataOption(multiFormData, part, curl, context);
1254 hasData = true;
1255 }
1256 if (hasData) {
1257 NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_MIMEPOST, multipart, context);
1258 }
1259 return true;
1260 }
1261
SetFormDataOption(MultiFormData & multiFormData,curl_mimepart * part,CURL * curl,RequestContext * context)1262 void HttpExec::SetFormDataOption(MultiFormData &multiFormData, curl_mimepart *part, CURL *curl,
1263 RequestContext *context)
1264 {
1265 CURLcode result = curl_mime_name(part, multiFormData.name.c_str());
1266 if (result != CURLE_OK) {
1267 NETSTACK_LOGE("Failed to set name %{public}s, error: %{public}s", multiFormData.name.c_str(),
1268 curl_easy_strerror(result));
1269 return;
1270 }
1271 if (!multiFormData.contentType.empty()) {
1272 result = curl_mime_type(part, multiFormData.contentType.c_str());
1273 if (result != CURLE_OK) {
1274 NETSTACK_LOGE("Failed to set contentType: %{public}s, error: %{public}s", multiFormData.name.c_str(),
1275 curl_easy_strerror(result));
1276 }
1277 }
1278 if (!multiFormData.remoteFileName.empty()) {
1279 result = curl_mime_filename(part, multiFormData.remoteFileName.c_str());
1280 if (result != CURLE_OK) {
1281 NETSTACK_LOGE("Failed to set remoteFileName: %{public}s, error: %{public}s", multiFormData.name.c_str(),
1282 curl_easy_strerror(result));
1283 }
1284 }
1285 if (!multiFormData.data.empty()) {
1286 result = curl_mime_data(part, multiFormData.data.c_str(), CURL_ZERO_TERMINATED);
1287 if (result != CURLE_OK) {
1288 NETSTACK_LOGE("Failed to set data: %{public}s, error: %{public}s", multiFormData.name.c_str(),
1289 curl_easy_strerror(result));
1290 }
1291 } else {
1292 result = curl_mime_filedata(part, multiFormData.filePath.c_str());
1293 if (result != CURLE_OK) {
1294 NETSTACK_LOGE("Failed to set file data: %{public}s, error: %{public}s", multiFormData.name.c_str(),
1295 curl_easy_strerror(result));
1296 }
1297 }
1298 }
1299 } // namespace OHOS::NetStack::Http
1300