1/* 2 * Copyright (c) 2022-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 "napi_utils.h" 17 18#include <atomic> 19#include <cstring> 20#include <initializer_list> 21#include <memory> 22#include <mutex> 23#include <queue> 24#include <unordered_set> 25#include <unordered_map> 26 27#include "netmanager_base_log.h" 28#include "securec.h" 29 30namespace OHOS { 31namespace NetManagerStandard { 32namespace NapiUtils { 33namespace { 34static constexpr const int MAX_STRING_LENGTH = 65536; 35constexpr const char *CODE = "code"; 36constexpr const char *MSG = "message"; 37} // namespace 38 39static std::unordered_set<napi_env> unorderedSetEnv; 40static std::recursive_mutex mutexForEnv; 41 42class WorkData { 43public: 44 WorkData() = delete; 45 46 WorkData(napi_env env, void *data, void (*handler)(napi_env env, napi_status status, void *data)) 47 : env_(env), data_(data), handler_(handler) 48 { 49 } 50 51 napi_env env_; 52 void *data_; 53 void (*handler_)(napi_env env, napi_status status, void *data); 54}; 55 56struct UvHandlerQueue : public std::queue<UvHandler> { 57 UvHandler Pop(); 58 void Push(const UvHandler &handler); 59 60private: 61 std::mutex mutex; 62}; 63 64static std::mutex g_mutex; 65static std::unordered_map<uint64_t, std::shared_ptr<UvHandlerQueue>> g_handlerQueueMap; 66static const char *const HTTP_UV_SYNC_QUEUE_NAME = "NET_CONNECTION_UV_SYNC_QUEUE_NAME"; 67 68UvHandler UvHandlerQueue::Pop() 69{ 70 std::lock_guard lock(mutex); 71 if (empty()) { 72 return {}; 73 } 74 auto s = front(); 75 pop(); 76 return s; 77} 78 79void UvHandlerQueue::Push(const UvHandler &handler) 80{ 81 std::lock_guard lock(mutex); 82 push(handler); 83} 84 85napi_value GetGlobal(napi_env env) 86{ 87 napi_value undefined = GetUndefined(env); 88 napi_value global = nullptr; 89 NAPI_CALL_BASE(env, napi_get_global(env, &global), undefined); 90 return global; 91} 92 93uint64_t CreateUvHandlerQueue(napi_env env) 94{ 95 static std::atomic<uint64_t> id = 1; // start from 1 96 uint64_t newId = id++; 97 NETMANAGER_BASE_LOGI("newId = %{public}s, id = %{public}s", std::to_string(newId).c_str(), 98 std::to_string(id).c_str()); 99 100 auto global = GetGlobal(env); 101 auto queueWrapper = CreateObject(env); 102 SetNamedProperty(env, global, HTTP_UV_SYNC_QUEUE_NAME, queueWrapper); 103 { 104 std::lock_guard lock(g_mutex); 105 g_handlerQueueMap.emplace(newId, std::make_shared<UvHandlerQueue>()); 106 } 107 napi_wrap( 108 env, queueWrapper, reinterpret_cast<void *>(newId), 109 [](napi_env env, void *data, void *) { 110 auto id = reinterpret_cast<uint64_t>(data); 111 std::lock_guard lock(g_mutex); 112 g_handlerQueueMap.erase(id); 113 }, 114 nullptr, nullptr); 115 return newId; 116} 117 118napi_value GetValueFromGlobal(napi_env env, const std::string &className) 119{ 120 auto global = NapiUtils::GetGlobal(env); 121 if (NapiUtils::GetValueType(env, global) == napi_undefined) { 122 return GetUndefined(env); 123 } 124 return NapiUtils::GetNamedProperty(env, global, className); 125} 126 127static uv_after_work_cb MakeUvCallback() 128{ 129 return [](uv_work_t *work, int status) { 130 if (!work) { 131 return; 132 } 133 std::unique_ptr<uv_work_t> workHandle(work); 134 135 if (!work->data) { 136 return; 137 } 138 auto env = reinterpret_cast<napi_env>(work->data); 139 if (!env) { 140 return; 141 } 142 143 auto closeScope = [env](napi_handle_scope scope) { NapiUtils::CloseScope(env, scope); }; 144 std::unique_ptr<napi_handle_scope__, decltype(closeScope)> scope(NapiUtils::OpenScope(env), closeScope); 145 auto queueWrapper = GetValueFromGlobal(env, HTTP_UV_SYNC_QUEUE_NAME); 146 if (!queueWrapper) { 147 return; 148 } 149 void *theId = nullptr; 150 napi_unwrap(env, queueWrapper, &theId); 151 if (!theId) { // that is why moduleId is started from 1 152 return; 153 } 154 UvHandler handler; 155 { 156 std::lock_guard lock(g_mutex); 157 auto it = g_handlerQueueMap.find(reinterpret_cast<uint64_t>(theId)); 158 if (it == g_handlerQueueMap.end()) { 159 return; 160 } 161 handler = it->second->Pop(); 162 } 163 if (handler) { 164 handler(env); 165 } 166 }; 167} 168 169void CreateUvQueueWorkByModuleId(napi_env env, const UvHandler &handler, uint64_t id) 170{ 171 uv_loop_s *loop = nullptr; 172 if (!IsEnvValid(env)) { 173 NETMANAGER_BASE_LOGE("the env is invalid"); 174 return; 175 } 176 napi_get_uv_event_loop(env, &loop); 177 if (!loop) { 178 return; 179 } 180 uv_work_t *work = nullptr; 181 { 182 std::lock_guard lock(g_mutex); 183 auto it = g_handlerQueueMap.find(id); 184 if (it == g_handlerQueueMap.end()) { 185 return; 186 } 187 work = new (std::nothrow) uv_work_t; 188 if (work == nullptr) { 189 return; 190 } 191 work->data = env; 192 it->second->Push(handler); 193 } 194 195 if (work) { 196 (void)uv_queue_work_with_qos( 197 loop, work, [](uv_work_t *) {}, MakeUvCallback(), uv_qos_default); 198 } 199} 200 201napi_valuetype GetValueType(napi_env env, napi_value value) 202{ 203 if (value == nullptr) { 204 return napi_undefined; 205 } 206 207 napi_valuetype valueType = napi_undefined; 208 NAPI_CALL_BASE(env, napi_typeof(env, value, &valueType), napi_undefined); 209 return valueType; 210} 211 212/* named property */ 213bool HasNamedProperty(napi_env env, napi_value object, const std::string &propertyName) 214{ 215 bool hasProperty = false; 216 NAPI_CALL_BASE(env, napi_has_named_property(env, object, propertyName.c_str(), &hasProperty), false); 217 return hasProperty; 218} 219 220napi_value GetNamedProperty(napi_env env, napi_value object, const std::string &propertyName) 221{ 222 napi_value value = nullptr; 223 NAPI_CALL(env, napi_get_named_property(env, object, propertyName.c_str(), &value)); 224 return value; 225} 226 227void SetNamedProperty(napi_env env, napi_value object, const std::string &name, napi_value value) 228{ 229 NAPI_CALL_RETURN_VOID(env, napi_set_named_property(env, object, name.c_str(), value)); 230} 231 232std::vector<std::string> GetPropertyNames(napi_env env, napi_value object) 233{ 234 std::vector<std::string> ret; 235 napi_value names = nullptr; 236 NAPI_CALL_BASE(env, napi_get_property_names(env, object, &names), ret); 237 uint32_t length = 0; 238 NAPI_CALL_BASE(env, napi_get_array_length(env, names, &length), ret); 239 for (uint32_t index = 0; index < length; ++index) { 240 napi_value name = nullptr; 241 if (napi_get_element(env, names, index, &name) != napi_ok) { 242 continue; 243 } 244 if (GetValueType(env, name) != napi_string) { 245 continue; 246 } 247 ret.emplace_back(GetStringFromValueUtf8(env, name)); 248 } 249 return ret; 250} 251 252/* UINT32 */ 253napi_value CreateUint32(napi_env env, uint32_t code) 254{ 255 napi_value value = nullptr; 256 if (napi_create_uint32(env, code, &value) != napi_ok) { 257 return nullptr; 258 } 259 return value; 260} 261 262uint32_t GetUint32FromValue(napi_env env, napi_value value) 263{ 264 uint32_t ret = 0; 265 NAPI_CALL_BASE(env, napi_get_value_uint32(env, value, &ret), 0); 266 return ret; 267} 268 269uint32_t GetUint32Property(napi_env env, napi_value object, const std::string &propertyName) 270{ 271 if (!HasNamedProperty(env, object, propertyName)) { 272 return 0; 273 } 274 napi_value value = GetNamedProperty(env, object, propertyName); 275 return GetUint32FromValue(env, value); 276} 277 278void SetUint32Property(napi_env env, napi_value object, const std::string &name, uint32_t value) 279{ 280 napi_value jsValue = CreateUint32(env, value); 281 if (GetValueType(env, jsValue) != napi_number) { 282 return; 283 } 284 285 napi_set_named_property(env, object, name.c_str(), jsValue); 286} 287 288/* INT32 */ 289napi_value CreateInt32(napi_env env, int32_t code) 290{ 291 napi_value value = nullptr; 292 if (napi_create_int32(env, code, &value) != napi_ok) { 293 return nullptr; 294 } 295 return value; 296} 297 298int32_t GetInt32FromValue(napi_env env, napi_value value) 299{ 300 int32_t ret = 0; 301 NAPI_CALL_BASE(env, napi_get_value_int32(env, value, &ret), 0); 302 return ret; 303} 304 305int32_t GetInt32Property(napi_env env, napi_value object, const std::string &propertyName) 306{ 307 if (!HasNamedProperty(env, object, propertyName)) { 308 return 0; 309 } 310 napi_value value = GetNamedProperty(env, object, propertyName); 311 return GetInt32FromValue(env, value); 312} 313 314void SetInt32Property(napi_env env, napi_value object, const std::string &name, int32_t value) 315{ 316 napi_value jsValue = CreateInt32(env, value); 317 if (GetValueType(env, jsValue) != napi_number) { 318 return; 319 } 320 321 napi_set_named_property(env, object, name.c_str(), jsValue); 322} 323 324/* INT64 */ 325napi_value CreateInt64(napi_env env, int64_t code) 326{ 327 napi_value value = nullptr; 328 if (napi_create_int64(env, code, &value) != napi_ok) { 329 return nullptr; 330 } 331 return value; 332} 333 334int64_t GetInt64Property(napi_env env, napi_value object, const std::string &propertyName) 335{ 336 if (!HasNamedProperty(env, object, propertyName)) { 337 return 0; 338 } 339 napi_value value = GetNamedProperty(env, object, propertyName); 340 return GetInt64FromValue(env, value); 341} 342int64_t GetInt64FromValue(napi_env env, napi_value value) 343{ 344 int64_t ret = 0; 345 NAPI_CALL_BASE(env, napi_get_value_int64(env, value, &ret), 0); 346 return ret; 347} 348void SetInt64Property(napi_env env, napi_value object, const std::string &name, int64_t value) 349{ 350 napi_value jsValue = CreateInt64(env, value); 351 if (GetValueType(env, jsValue) != napi_number) { 352 return; 353 } 354 355 napi_set_named_property(env, object, name.c_str(), jsValue); 356} 357 358/* String UTF8 */ 359napi_value CreateStringUtf8(napi_env env, const std::string &str) 360{ 361 napi_value value = nullptr; 362 if (napi_create_string_utf8(env, str.c_str(), strlen(str.c_str()), &value) != napi_ok) { 363 return nullptr; 364 } 365 return value; 366} 367 368std::string GetStringFromValueUtf8(napi_env env, napi_value value) 369{ 370 std::string result; 371 char str[MAX_STRING_LENGTH] = {0}; 372 size_t length = 0; 373 NAPI_CALL_BASE(env, napi_get_value_string_utf8(env, value, str, MAX_STRING_LENGTH, &length), result); 374 if (length > MAX_STRING_LENGTH) { 375 result.append(str, MAX_STRING_LENGTH); 376 return result; 377 } 378 if (length > 0) { 379 return result.append(str, length); 380 } 381 return result; 382} 383 384SecureData GetSecureDataFromValueUtf8(napi_env env, napi_value value) 385{ 386 SecureData result; 387 char str[MAX_STRING_LENGTH] = {0}; 388 size_t length = 0; 389 NAPI_CALL_BASE(env, napi_get_value_string_utf8(env, value, str, MAX_STRING_LENGTH, &length), result); 390 if (length > 0) { 391 result.append(str, length); 392 } 393 return result; 394} 395 396std::string GetStringPropertyUtf8(napi_env env, napi_value object, const std::string &propertyName) 397{ 398 if (!HasNamedProperty(env, object, propertyName)) { 399 return ""; 400 } 401 napi_value value = GetNamedProperty(env, object, propertyName); 402 return GetStringFromValueUtf8(env, value); 403} 404 405SecureData GetSecureDataPropertyUtf8(napi_env env, napi_value object, const std::string &propertyName) 406{ 407 napi_value value = GetNamedProperty(env, object, propertyName); 408 return GetSecureDataFromValueUtf8(env, value); 409} 410 411void SetStringPropertyUtf8(napi_env env, napi_value object, const std::string &name, const std::string &value) 412{ 413 napi_value jsValue = CreateStringUtf8(env, value); 414 if (GetValueType(env, jsValue) != napi_string) { 415 return; 416 } 417 napi_set_named_property(env, object, name.c_str(), jsValue); 418} 419 420/* array buffer */ 421bool ValueIsArrayBuffer(napi_env env, napi_value value) 422{ 423 bool isArrayBuffer = false; 424 NAPI_CALL_BASE(env, napi_is_arraybuffer(env, value, &isArrayBuffer), false); 425 return isArrayBuffer; 426} 427 428void *GetInfoFromArrayBufferValue(napi_env env, napi_value value, size_t *length) 429{ 430 if (length == nullptr) { 431 return nullptr; 432 } 433 434 void *data = nullptr; 435 NAPI_CALL(env, napi_get_arraybuffer_info(env, value, &data, length)); 436 return data; 437} 438 439napi_value CreateArrayBuffer(napi_env env, size_t length, void **data) 440{ 441 if (length == 0) { 442 return nullptr; 443 } 444 napi_value result = nullptr; 445 NAPI_CALL(env, napi_create_arraybuffer(env, length, data, &result)); 446 return result; 447} 448 449/* object */ 450napi_value CreateObject(napi_env env) 451{ 452 napi_value object = nullptr; 453 NAPI_CALL(env, napi_create_object(env, &object)); 454 return object; 455} 456 457/* undefined */ 458napi_value GetUndefined(napi_env env) 459{ 460 napi_value undefined = nullptr; 461 NAPI_CALL(env, napi_get_undefined(env, &undefined)); 462 return undefined; 463} 464 465/* function */ 466napi_value CallFunction(napi_env env, napi_value recv, napi_value func, size_t argc, const napi_value *argv) 467{ 468 napi_value res = nullptr; 469 NAPI_CALL(env, napi_call_function(env, recv, func, argc, argv, &res)); 470 return res; 471} 472 473napi_value CreateFunction(napi_env env, const std::string &name, napi_callback func, void *arg) 474{ 475 napi_value res = nullptr; 476 NAPI_CALL(env, napi_create_function(env, name.c_str(), strlen(name.c_str()), func, arg, &res)); 477 return res; 478} 479 480/* reference */ 481napi_ref CreateReference(napi_env env, napi_value callback) 482{ 483 napi_ref callbackRef = nullptr; 484 NAPI_CALL(env, napi_create_reference(env, callback, 1, &callbackRef)); 485 return callbackRef; 486} 487 488napi_value GetReference(napi_env env, napi_ref callbackRef) 489{ 490 napi_value callback = nullptr; 491 NAPI_CALL(env, napi_get_reference_value(env, callbackRef, &callback)); 492 return callback; 493} 494 495void DeleteReference(napi_env env, napi_ref callbackRef) 496{ 497 (void)napi_delete_reference(env, callbackRef); 498} 499 500/* boolean */ 501bool GetBooleanProperty(napi_env env, napi_value object, const std::string &propertyName) 502{ 503 if (!HasNamedProperty(env, object, propertyName)) { 504 return false; 505 } 506 napi_value value = GetNamedProperty(env, object, propertyName); 507 bool ret = false; 508 NAPI_CALL_BASE(env, napi_get_value_bool(env, value, &ret), false); 509 return ret; 510} 511 512void SetBooleanProperty(napi_env env, napi_value object, const std::string &name, bool value) 513{ 514 napi_value jsValue = nullptr; 515 NAPI_CALL_RETURN_VOID(env, napi_get_boolean(env, value, &jsValue)); 516 if (GetValueType(env, jsValue) != napi_boolean) { 517 return; 518 } 519 520 napi_set_named_property(env, object, name.c_str(), jsValue); 521} 522 523napi_value GetBoolean(napi_env env, bool value) 524{ 525 napi_value jsValue = nullptr; 526 NAPI_CALL(env, napi_get_boolean(env, value, &jsValue)); 527 return jsValue; 528} 529 530bool GetBooleanValue(napi_env env, napi_value value) 531{ 532 bool ret = false; 533 NAPI_CALL_BASE(env, napi_get_value_bool(env, value, &ret), 0); 534 return ret; 535} 536 537/* define properties */ 538void DefineProperties(napi_env env, napi_value object, 539 const std::initializer_list<napi_property_descriptor> &properties) 540{ 541 napi_property_descriptor descriptors[properties.size()]; 542 std::copy(properties.begin(), properties.end(), descriptors); 543 544 (void)napi_define_properties(env, object, properties.size(), descriptors); 545} 546 547/* array */ 548napi_value CreateArray(napi_env env, size_t length) 549{ 550 if (length == 0) { 551 napi_value res = nullptr; 552 NAPI_CALL(env, napi_create_array(env, &res)); 553 return res; 554 } 555 napi_value res = nullptr; 556 NAPI_CALL(env, napi_create_array_with_length(env, length, &res)); 557 return res; 558} 559 560void SetArrayElement(napi_env env, napi_value array, uint32_t index, napi_value value) 561{ 562 (void)napi_set_element(env, array, index, value); 563} 564 565bool IsArray(napi_env env, napi_value value) 566{ 567 bool result = false; 568 NAPI_CALL_BASE(env, napi_is_array(env, value, &result), false); 569 return result; 570} 571 572uint32_t GetArrayLength(napi_env env, napi_value arr) 573{ 574 uint32_t arrayLength = 0; 575 NAPI_CALL_BASE(env, napi_get_array_length(env, arr, &arrayLength), 0); 576 return arrayLength; 577} 578 579napi_value GetArrayElement(napi_env env, napi_value arr, uint32_t index) 580{ 581 napi_value elementValue = nullptr; 582 NAPI_CALL(env, napi_get_element(env, arr, index, &elementValue)); 583 return elementValue; 584} 585 586/* libuv */ 587void CreateUvQueueWork(napi_env env, void *data, void(handler)(uv_work_t *, int status)) 588{ 589 uv_loop_s *loop = nullptr; 590 if (!IsEnvValid(env)) { 591 NETMANAGER_BASE_LOGE("the env is invalid"); 592 return; 593 } 594 NAPI_CALL_RETURN_VOID(env, napi_get_uv_event_loop(env, &loop)); 595 596 auto work = new uv_work_t; 597 work->data = data; 598 599 (void)uv_queue_work_with_qos( 600 loop, work, [](uv_work_t *) {}, handler, uv_qos_default); 601} 602 603/* scope */ 604napi_handle_scope OpenScope(napi_env env) 605{ 606 napi_handle_scope scope = nullptr; 607 NAPI_CALL(env, napi_open_handle_scope(env, &scope)); 608 return scope; 609} 610 611void CloseScope(napi_env env, napi_handle_scope scope) 612{ 613 (void)napi_close_handle_scope(env, scope); 614} 615 616napi_value CreateEnumConstructor(napi_env env, napi_callback_info info) 617{ 618 napi_value thisArg = nullptr; 619 void *data = nullptr; 620 napi_get_cb_info(env, info, nullptr, nullptr, &thisArg, &data); 621 napi_value global = nullptr; 622 napi_get_global(env, &global); 623 return thisArg; 624} 625 626/* error */ 627napi_value CreateErrorMessage(napi_env env, int32_t errorCode, const std::string &errorMessage) 628{ 629 napi_value result = CreateObject(env); 630 SetNamedProperty(env, result, CODE, CreateInt32(env, errorCode)); 631 SetNamedProperty(env, result, MSG, CreateStringUtf8(env, errorMessage)); 632 return result; 633} 634 635void HookForEnvCleanup(void *data) 636{ 637 std::lock_guard<std::recursive_mutex> lock(mutexForEnv); 638 auto env = static_cast<napi_env>(data); 639 auto pos = unorderedSetEnv.find(env); 640 if (pos == unorderedSetEnv.end()) { 641 NETMANAGER_BASE_LOGE("The env is not in the unordered set"); 642 return; 643 } 644 NETMANAGER_BASE_LOGD("env clean up, erase from the unordered set"); 645 unorderedSetEnv.erase(pos); 646} 647 648void SetEnvValid(napi_env env) 649{ 650 std::lock_guard<std::recursive_mutex> lock(mutexForEnv); 651 unorderedSetEnv.emplace(env); 652} 653 654bool IsEnvValid(napi_env env) 655{ 656 std::lock_guard<std::recursive_mutex> lock(mutexForEnv); 657 auto pos = unorderedSetEnv.find(env); 658 if (pos == unorderedSetEnv.end()) { 659 return false; 660 } 661 return true; 662} 663} // namespace NapiUtils 664} // namespace NetManagerStandard 665} // namespace OHOS 666