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