diff --git a/CMakeLists.txt b/CMakeLists.txt index 3fece9a9fde7e77636485f3ad413a04fadeea618..d79253f6da837104fee4098cbdc71e32c8cf2d4f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,6 +30,8 @@ include_directories(utils/base_context/include) include_directories(utils/base_async_work/include) include_directories(utils/module_template/include) +include_directories(../../../for_linux) + include_directories(../../../utils/native/base/include/) include_directories(utils/log/include) @@ -63,4 +65,6 @@ add_subdirectory(utils) add_subdirectory(test/napi/http) add_subdirectory(test/napi/socket) add_subdirectory(test/napi/fetch) +add_subdirectory(test/napi/websocket) +add_subdirectory(test/napi/websocket/unittest) add_subdirectory(test/utils/napi_utils/unittest) \ No newline at end of file diff --git a/bundle.json b/bundle.json index fbddcf79c606042ab1c1996b50a8cd5f37e6dc22..1a05899ffdb3892aa3d96da3590aba197f6de951 100644 --- a/bundle.json +++ b/bundle.json @@ -48,7 +48,9 @@ "//foundation/communication/netstack/frameworks/js/napi:fetch" ], "inner_kits": [], - "test": [] + "test": [ + "//foundation/communication/netstack/test/napi/websocket/unittest:unittest" + ] } } } diff --git a/frameworks/js/napi/BUILD.gn b/frameworks/js/napi/BUILD.gn index 2836e3a6659f1ab48938ac53d0e2cc6629b092b6..374ae69a3a21514e614f50e60f9a14bc8e4b9494 100644 --- a/frameworks/js/napi/BUILD.gn +++ b/frameworks/js/napi/BUILD.gn @@ -136,20 +136,63 @@ ohos_shared_library("socket") { subsystem_name = common_subsystem_name } -ohos_shared_library("websocket") { - include_dirs = [ "$NETSTACK_NAPI_ROOT/websocket/include" ] - include_dirs += utils_include - include_dirs += common_include - - sources = [ "$NETSTACK_NAPI_ROOT/websocket/src/websocket_napi.cpp" ] - - deps = common_deps - - external_deps = common_external_deps - - relative_install_dir = common_relative_install_dir - part_name = common_part_name - subsystem_name = common_subsystem_name +if ("${product_name}" == "m40") { + ohos_shared_library("websocket") { + include_dirs = [ + "websocket/async_context/include", + "websocket/async_work/include", + "websocket/constant/include", + "websocket/websocket_exec/include", + "websocket/websocket_module/include", + ] + include_dirs += utils_include + include_dirs += common_include + include_dirs += [ + "//third_party/libwebsockets/include", + "//third_party/openssl/include", + ] + + sources = [ + "websocket/async_context/src/close_context.cpp", + "websocket/async_context/src/connect_context.cpp", + "websocket/async_context/src/send_context.cpp", + "websocket/async_work/src/websocket_async_work.cpp", + "websocket/constant/src/constant.cpp", + "websocket/websocket_exec/src/websocket_exec.cpp", + "websocket/websocket_module/src/websocket_module.cpp", + ] + sources += utils_source + + deps = common_deps + deps += [ + "//third_party/libwebsockets:websockets", + "//third_party/openssl:libcrypto_static", + "//third_party/openssl:ssl_source", + "//third_party/zlib:libz", + ] + + external_deps = common_external_deps + + relative_install_dir = common_relative_install_dir + part_name = common_part_name + subsystem_name = common_subsystem_name + } +} else { + ohos_shared_library("websocket") { + include_dirs = [ "websocket/include" ] + include_dirs += utils_include + include_dirs += common_include + + sources = [ "websocket/src/websocket_napi.cpp" ] + + deps = common_deps + + external_deps = common_external_deps + + relative_install_dir = common_relative_install_dir + part_name = common_part_name + subsystem_name = common_subsystem_name + } } ohos_shared_library("fetch") { diff --git a/frameworks/js/napi/websocket/async_context/include/close_context.h b/frameworks/js/napi/websocket/async_context/include/close_context.h new file mode 100644 index 0000000000000000000000000000000000000000..62c0307ae33b1653f69558f0de024de91705bce0 --- /dev/null +++ b/frameworks/js/napi/websocket/async_context/include/close_context.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef COMMUNICATIONNETSTACK_CLOSE_CONTEXT_H +#define COMMUNICATIONNETSTACK_CLOSE_CONTEXT_H + +#include + +#include "netstack_base_context.h" +#include "noncopyable.h" + +namespace OHOS::NetStack { +class CloseContext final : public BaseContext { +public: + ACE_DISALLOW_COPY_AND_MOVE(CloseContext); + + CloseContext() = delete; + + explicit CloseContext(napi_env env, EventManager *manager); + + void ParseParams(napi_value *params, size_t paramsCount); + + uint32_t code; + + std::string reason; + +private: + bool CheckParamsType(napi_value *params, size_t paramsCount); +}; +} // namespace OHOS::NetStack + +#endif /* COMMUNICATIONNETSTACK_CLOSE_CONTEXT_H */ diff --git a/frameworks/js/napi/websocket/async_context/include/connect_context.h b/frameworks/js/napi/websocket/async_context/include/connect_context.h new file mode 100644 index 0000000000000000000000000000000000000000..9bfc38e753492d289f381d504c9a75d9bf329eaf --- /dev/null +++ b/frameworks/js/napi/websocket/async_context/include/connect_context.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef COMMUNICATIONNETSTACK_CONNECT_CONTEXT_H +#define COMMUNICATIONNETSTACK_CONNECT_CONTEXT_H + +#include +#include + +#include "netstack_base_context.h" +#include "noncopyable.h" +#include "libwebsockets.h" + +namespace OHOS::NetStack { +class ConnectContext final : public BaseContext { +public: + ACE_DISALLOW_COPY_AND_MOVE(ConnectContext); + + ConnectContext() = delete; + + explicit ConnectContext(napi_env env, EventManager *manager); + + ~ConnectContext() override; + + void ParseParams(napi_value *params, size_t paramsCount); + + std::string url; + + std::map header; + +private: + void ParseHeader(napi_value optionsValue); + + bool CheckParamsType(napi_value *params, size_t paramsCount); +}; +} // namespace OHOS::NetStack + +#endif /* COMMUNICATIONNETSTACK_CONNECT_CONTEXT_H */ diff --git a/frameworks/js/napi/websocket/async_context/include/send_context.h b/frameworks/js/napi/websocket/async_context/include/send_context.h new file mode 100644 index 0000000000000000000000000000000000000000..d853f9fcc5c98297441960e4f2ce0d53afb43a85 --- /dev/null +++ b/frameworks/js/napi/websocket/async_context/include/send_context.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef COMMUNICATIONNETSTACK_SEND_CONTEXT_H +#define COMMUNICATIONNETSTACK_SEND_CONTEXT_H + +#include + +#include "libwebsockets.h" +#include "netstack_base_context.h" +#include "noncopyable.h" + +namespace OHOS::NetStack { +class SendContext final : public BaseContext { +public: + ACE_DISALLOW_COPY_AND_MOVE(SendContext); + + SendContext() = delete; + + explicit SendContext(napi_env env, EventManager *manager); + + void ParseParams(napi_value *params, size_t paramsCount); + + void *data; + + size_t length; + + lws_write_protocol protocol; + +private: + bool CheckParamsType(napi_value *params, size_t paramsCount); +}; +} // namespace OHOS::NetStack + +#endif /* COMMUNICATIONNETSTACK_SEND_CONTEXT_H */ diff --git a/frameworks/js/napi/websocket/async_context/src/close_context.cpp b/frameworks/js/napi/websocket/async_context/src/close_context.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e3e8c7c3f1389c9334cff7476b6e6f4b6e245eef --- /dev/null +++ b/frameworks/js/napi/websocket/async_context/src/close_context.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "close_context.h" + +#include "constant.h" +#include "netstack_common_utils.h" +#include "netstack_log.h" +#include "netstack_napi_utils.h" + +namespace OHOS::NetStack { +CloseContext::CloseContext(napi_env env, EventManager *manager) + : BaseContext(env, manager), code(CLOSE_REASON_NORMAL_CLOSE) +{ +} + +void CloseContext::ParseParams(napi_value *params, size_t paramsCount) +{ + if (!CheckParamsType(params, paramsCount)) { + NETSTACK_LOGE("CloseContext Parse Failed"); + return; + } + + if (paramsCount == FUNCTION_PARAM_ZERO) { + NETSTACK_LOGI("CloseContext Parse OK1"); + return SetParseOK(true); + } + + if (NapiUtils::GetValueType(GetEnv(), params[0]) == napi_function) { + NETSTACK_LOGI("CloseContext Parse OK2"); + return SetParseOK(SetCallback(params[0]) == napi_ok); + } + + uint32_t closeCode = NapiUtils::GetUint32Property(GetEnv(), params[0], ContextKey::CODE); + if (closeCode >= CLOSE_REASON_NORMAL_CLOSE && closeCode <= CLOSE_REASON_RESERVED12) { + code = closeCode; + } + reason = NapiUtils::GetStringPropertyUtf8(GetEnv(), params[0], ContextKey::REASON); + + if (paramsCount == FUNCTION_PARAM_TWO) { + NETSTACK_LOGI("CloseContext Parse OK3"); + return SetParseOK(SetCallback(params[1]) == napi_ok); + } + NETSTACK_LOGI("CloseContext Parse OK4"); + return SetParseOK(true); +} + +bool CloseContext::CheckParamsType(napi_value *params, size_t paramsCount) +{ + if (paramsCount == FUNCTION_PARAM_ZERO) { + return true; + } + + if (paramsCount == FUNCTION_PARAM_ONE) { + return NapiUtils::GetValueType(GetEnv(), params[0]) == napi_object || + NapiUtils::GetValueType(GetEnv(), params[0]) == napi_function; + } + + if (paramsCount == FUNCTION_PARAM_TWO) { + return NapiUtils::GetValueType(GetEnv(), params[0]) == napi_object && + NapiUtils::GetValueType(GetEnv(), params[1]) == napi_function; + } + + return false; +} +} // namespace OHOS::NetStack \ No newline at end of file diff --git a/frameworks/js/napi/websocket/async_context/src/connect_context.cpp b/frameworks/js/napi/websocket/async_context/src/connect_context.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4f4c758dd32f3cce994419a1f4575af978bf6d70 --- /dev/null +++ b/frameworks/js/napi/websocket/async_context/src/connect_context.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "connect_context.h" + +#include "constant.h" +#include "netstack_common_utils.h" +#include "netstack_log.h" +#include "netstack_napi_utils.h" +#include "securec.h" + +namespace OHOS::NetStack { +ConnectContext::ConnectContext(napi_env env, EventManager *manager) : BaseContext(env, manager) {} + +ConnectContext::~ConnectContext() = default; + +void ConnectContext::ParseParams(napi_value *params, size_t paramsCount) +{ + if (!CheckParamsType(params, paramsCount)) { + NETSTACK_LOGE("ConnectContext Parse Failed"); + return; + } + + url = NapiUtils::GetStringFromValueUtf8(GetEnv(), params[0]); + if (paramsCount == FUNCTION_PARAM_ONE) { + NETSTACK_LOGI("ConnectContext paramsCount == FUNCTION_PARAM_ONE"); + return SetParseOK(true); + } + + if (NapiUtils::GetValueType(GetEnv(), params[1]) == napi_function) { + NETSTACK_LOGI("ConnectContext NapiUtils::GetValueType(GetEnv(), params[1]) == napi_function"); + return SetParseOK(SetCallback(params[1]) == napi_ok); + } + + NETSTACK_LOGI("ConnectContext NapiUtils::GetValueType(GetEnv(), params[1]) == napi_object"); + ParseHeader(params[1]); + + if (paramsCount == FUNCTION_PARAM_THREE) { + NETSTACK_LOGI("ConnectContext paramsCount == FUNCTION_PARAM_THREE"); + return SetParseOK(SetCallback(params[FUNCTION_PARAM_TWO]) == napi_ok); + } + return SetParseOK(true); +} + +void ConnectContext::ParseHeader(napi_value optionsValue) +{ + if (!NapiUtils::HasNamedProperty(GetEnv(), optionsValue, ContextKey::HEADER)) { + return; + } + napi_value jsHeader = NapiUtils::GetNamedProperty(GetEnv(), optionsValue, ContextKey::HEADER); + if (NapiUtils::GetValueType(GetEnv(), jsHeader) != napi_object) { + return; + } + auto names = NapiUtils::GetPropertyNames(GetEnv(), jsHeader); + std::for_each(names.begin(), names.end(), [jsHeader, this](const std::string &name) { + auto value = NapiUtils::GetStringPropertyUtf8(GetEnv(), jsHeader, name); + if (!value.empty()) { + // header key ignores key but value not + header[CommonUtils::ToLower(name)] = value; + } + }); +} + +bool ConnectContext::CheckParamsType(napi_value *params, size_t paramsCount) +{ + if (paramsCount == FUNCTION_PARAM_ONE) { + return NapiUtils::GetValueType(GetEnv(), params[0]) == napi_string; + } + + if (paramsCount == FUNCTION_PARAM_TWO) { + return NapiUtils::GetValueType(GetEnv(), params[0]) == napi_string && + (NapiUtils::GetValueType(GetEnv(), params[1]) == napi_function || + NapiUtils::GetValueType(GetEnv(), params[1]) == napi_object); + } + + if (paramsCount == FUNCTION_PARAM_THREE) { + return NapiUtils::GetValueType(GetEnv(), params[0]) == napi_string && + NapiUtils::GetValueType(GetEnv(), params[1]) == napi_object && + NapiUtils::GetValueType(GetEnv(), params[FUNCTION_PARAM_TWO]) == napi_function; + } + + return false; +} +} // namespace OHOS::NetStack diff --git a/frameworks/js/napi/websocket/async_context/src/send_context.cpp b/frameworks/js/napi/websocket/async_context/src/send_context.cpp new file mode 100644 index 0000000000000000000000000000000000000000..59e04a60412e553fe7c8f976ec244352f178b0d9 --- /dev/null +++ b/frameworks/js/napi/websocket/async_context/src/send_context.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "send_context.h" + +#include "constant.h" +#include "netstack_log.h" +#include "netstack_napi_utils.h" +#include "securec.h" + +namespace OHOS::NetStack { +SendContext::SendContext(napi_env env, EventManager *manager) + : BaseContext(env, manager), data(nullptr), length(0), protocol(LWS_WRITE_TEXT) +{ +} + +void SendContext::ParseParams(napi_value *params, size_t paramsCount) +{ + if (!CheckParamsType(params, paramsCount)) { + NETSTACK_LOGE("SendContext Parse Failed"); + return; + } + + if (NapiUtils::GetValueType(GetEnv(), params[0]) == napi_string) { + NETSTACK_LOGI("SendContext NapiUtils::GetValueType(GetEnv(), params[0]) == napi_string"); + std::string str = NapiUtils::GetStringFromValueUtf8(GetEnv(), params[0]); + // must have PRE and POST + data = malloc(LWS_SEND_BUFFER_PRE_PADDING + str.length() + LWS_SEND_BUFFER_POST_PADDING); + if (data == nullptr) { + NETSTACK_LOGE("no memory"); + return; + } + if (memcpy_s(reinterpret_cast(reinterpret_cast(data) + LWS_SEND_BUFFER_PRE_PADDING), + str.length(), str.c_str(), str.length()) < 0) { + NETSTACK_LOGE("copy failed"); + return; + } + length = str.length(); + protocol = LWS_WRITE_TEXT; + } else { + NETSTACK_LOGI("SendContext data is ArrayBuffer"); + size_t len = 0; + void *mem = NapiUtils::GetInfoFromArrayBufferValue(GetEnv(), params[0], &len); + if (mem == nullptr || len == 0) { + NETSTACK_LOGE("no memory"); + return; + } + // must have PRE and POST + data = malloc(LWS_SEND_BUFFER_PRE_PADDING + len + LWS_SEND_BUFFER_POST_PADDING); + if (data == nullptr) { + NETSTACK_LOGE("no memory"); + return; + } + if (memcpy_s(reinterpret_cast(reinterpret_cast(data) + LWS_SEND_BUFFER_PRE_PADDING), len, + mem, len) < 0) { + NETSTACK_LOGE("copy failed"); + return; + } + length = len; + protocol = LWS_WRITE_BINARY; + } + + if (NapiUtils::GetValueType(GetEnv(), params[1]) == napi_function) { + NETSTACK_LOGI("SendContext NapiUtils::GetValueType(GetEnv(), params[1]) == napi_function"); + return SetParseOK(SetCallback(params[1]) == napi_ok); + } + + NETSTACK_LOGI("SendContext SetParseOK"); + return SetParseOK(true); +} + +bool SendContext::CheckParamsType(napi_value *params, size_t paramsCount) +{ + if (paramsCount == FUNCTION_PARAM_ONE) { + return NapiUtils::GetValueType(GetEnv(), params[0]) == napi_string || + NapiUtils::ValueIsArrayBuffer(GetEnv(), params[0]); + } + + if (paramsCount == FUNCTION_PARAM_TWO) { + return (NapiUtils::GetValueType(GetEnv(), params[0]) == napi_string || + NapiUtils::ValueIsArrayBuffer(GetEnv(), params[0])) && + NapiUtils::GetValueType(GetEnv(), params[1]) == napi_function; + } + + return false; +} +} // namespace OHOS::NetStack \ No newline at end of file diff --git a/frameworks/js/napi/websocket/async_work/include/websocket_async_work.h b/frameworks/js/napi/websocket/async_work/include/websocket_async_work.h new file mode 100644 index 0000000000000000000000000000000000000000..ad86b43902186a1fb223c984b0e648528cfb1f7c --- /dev/null +++ b/frameworks/js/napi/websocket/async_work/include/websocket_async_work.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef COMMUNICATIONNETSTACK_WEBSOCKET_ASYNC_WORK_H +#define COMMUNICATIONNETSTACK_WEBSOCKET_ASYNC_WORK_H + +#include "websocket_exec.h" + +namespace OHOS::NetStack { +class WebSocketAsyncWork final { +public: + ACE_DISALLOW_COPY_AND_MOVE(WebSocketAsyncWork); + + /* executor */ + static void ExecConnect(napi_env env, void *data); + + static void ExecSend(napi_env env, void *data); + + static void ExecClose(napi_env env, void *data); + + /* callback */ + static void ConnectCallback(napi_env env, napi_status status, void *data); + + static void SendCallback(napi_env env, napi_status status, void *data); + + static void CloseCallback(napi_env env, napi_status status, void *data); +}; +} // namespace OHOS::NetStack +#endif /* COMMUNICATIONNETSTACK_WEBSOCKET_ASYNC_WORK_H */ diff --git a/frameworks/js/napi/websocket/async_work/src/websocket_async_work.cpp b/frameworks/js/napi/websocket/async_work/src/websocket_async_work.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7162eb49ee3e69390eef14ab09ddb5cc5c707511 --- /dev/null +++ b/frameworks/js/napi/websocket/async_work/src/websocket_async_work.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "websocket_async_work.h" + +#include "netstack_base_async_work.h" + +namespace OHOS::NetStack { +void WebSocketAsyncWork::ExecConnect(napi_env env, void *data) +{ + BaseAsyncWork::ExecAsyncWork(env, data); +} + +void WebSocketAsyncWork::ExecSend(napi_env env, void *data) +{ + BaseAsyncWork::ExecAsyncWork(env, data); +} + +void WebSocketAsyncWork::ExecClose(napi_env env, void *data) +{ + BaseAsyncWork::ExecAsyncWork(env, data); +} + +void WebSocketAsyncWork::ConnectCallback(napi_env env, napi_status status, void *data) +{ + BaseAsyncWork::AsyncWorkCallback(env, status, data); +} + +void WebSocketAsyncWork::SendCallback(napi_env env, napi_status status, void *data) +{ + BaseAsyncWork::AsyncWorkCallback(env, status, data); +} + +void WebSocketAsyncWork::CloseCallback(napi_env env, napi_status status, void *data) +{ + BaseAsyncWork::AsyncWorkCallback(env, status, data); +} +} // namespace OHOS::NetStack diff --git a/frameworks/js/napi/websocket/constant/include/constant.h b/frameworks/js/napi/websocket/constant/include/constant.h new file mode 100644 index 0000000000000000000000000000000000000000..ddf8cda33dd2ede1f9fbee46550c24ecb9c04617 --- /dev/null +++ b/frameworks/js/napi/websocket/constant/include/constant.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef COMMUNICATIONNETSTACK_CONSTANT_H +#define COMMUNICATIONNETSTACK_CONSTANT_H + +namespace OHOS::NetStack { +enum { + FUNCTION_PARAM_ZERO = 0, + FUNCTION_PARAM_ONE = 1, + FUNCTION_PARAM_TWO = 2, + FUNCTION_PARAM_THREE = 3, +}; + +enum { + CLOSE_REASON_NORMAL_CLOSE [[maybe_unused]] = 1000, + CLOSE_REASON_SERVER_CLOSED [[maybe_unused]] = 1001, + CLOSE_REASON_PROTOCOL_ERROR [[maybe_unused]] = 1002, + CLOSE_REASON_UNSUPPORT_DATA_TYPE [[maybe_unused]] = 1003, + CLOSE_REASON_RESERVED1 [[maybe_unused]], + CLOSE_REASON_RESERVED2 [[maybe_unused]], + CLOSE_REASON_RESERVED3 [[maybe_unused]], + CLOSE_REASON_RESERVED4 [[maybe_unused]], + CLOSE_REASON_RESERVED5 [[maybe_unused]], + CLOSE_REASON_RESERVED6 [[maybe_unused]], + CLOSE_REASON_RESERVED7 [[maybe_unused]], + CLOSE_REASON_RESERVED8 [[maybe_unused]], + CLOSE_REASON_RESERVED9 [[maybe_unused]], + CLOSE_REASON_RESERVED10 [[maybe_unused]], + CLOSE_REASON_RESERVED11 [[maybe_unused]], + CLOSE_REASON_RESERVED12 [[maybe_unused]], +}; + +class ContextKey final { +public: + static const char *HEADER; + + static const char *CODE; + + static const char *REASON; +}; + +class EventName final { +public: + static const char *EVENT_OPEN; + + static const char *EVENT_MESSAGE; + + static const char *EVENT_CLOSE; + + static const char *EVENT_ERROR; +}; +} // namespace OHOS::NetStack +#endif /* COMMUNICATIONNETSTACK_CONSTANT_H */ diff --git a/frameworks/js/napi/websocket/constant/src/constant.cpp b/frameworks/js/napi/websocket/constant/src/constant.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e72a8f1caf7df80653b580acdcc86e47f59e8498 --- /dev/null +++ b/frameworks/js/napi/websocket/constant/src/constant.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "constant.h" + +namespace OHOS::NetStack { +const char *ContextKey::HEADER = "header"; + +const char *ContextKey::CODE = "code"; + +const char *ContextKey::REASON = "reason"; + +const char *EventName::EVENT_OPEN = "open"; + +const char *EventName::EVENT_MESSAGE = "message"; + +const char *EventName::EVENT_CLOSE = "close"; + +const char *EventName::EVENT_ERROR = "error"; +} // namespace OHOS::NetStack \ No newline at end of file diff --git a/frameworks/js/napi/websocket/include/websocket_napi.h b/frameworks/js/napi/websocket/include/websocket_napi.h index 1864d71722f3df7c42b69c0e1866445813ff9cf7..6894c85b9944db2f2eee3e520bfba8fbd3107ebb 100644 --- a/frameworks/js/napi/websocket/include/websocket_napi.h +++ b/frameworks/js/napi/websocket/include/websocket_napi.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Huawei Device Co., Ltd. + * Copyright (c) 2021-2022 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at diff --git a/frameworks/js/napi/websocket/src/websocket_napi.cpp b/frameworks/js/napi/websocket/src/websocket_napi.cpp index b7327a76fc239ef5dfc4ed1cfc7d5c2685116ea6..dfd6a39e4952bb0dd2f9c1dffbe523947057a993 100644 --- a/frameworks/js/napi/websocket/src/websocket_napi.cpp +++ b/frameworks/js/napi/websocket/src/websocket_napi.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Huawei Device Co., Ltd. + * Copyright (c) 2021-2022 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at diff --git a/frameworks/js/napi/websocket/websocket_exec/include/websocket_exec.h b/frameworks/js/napi/websocket/websocket_exec/include/websocket_exec.h new file mode 100644 index 0000000000000000000000000000000000000000..b2b47371c1435b17bae9f2d30bff3416cd04a497 --- /dev/null +++ b/frameworks/js/napi/websocket/websocket_exec/include/websocket_exec.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef COMMUNICATIONNETSTACK_WEBSOCKET_EXEC_H +#define COMMUNICATIONNETSTACK_WEBSOCKET_EXEC_H + +#include "close_context.h" +#include "connect_context.h" +#include "send_context.h" + +namespace OHOS::NetStack { +class WebSocketExec final { +public: + /* async work execute */ + static bool ExecConnect(ConnectContext *context); + + static bool ExecSend(SendContext *context); + + static bool ExecClose(CloseContext *context); + + /* async work callback */ + static napi_value ConnectCallback(ConnectContext *context); + + static napi_value SendCallback(SendContext *context); + + static napi_value CloseCallback(CloseContext *context); + + static int LwsCallback(lws *wsi, lws_callback_reasons reason, void *user, void *in, size_t len); + +private: + static bool ParseUrl(ConnectContext *context, + char *prefix, + size_t prefixLen, + char *address, + size_t addressLen, + char *path, + size_t pathLen, + int *port); + + static void RunService(EventManager *manager); + + static int RaiseError(EventManager *manager); + + static int HttpDummy(lws *wsi, lws_callback_reasons reason, void *user, void *in, size_t len); + + static int + LwsCallbackClientAppendHandshakeHeader(lws *wsi, lws_callback_reasons reason, void *user, void *in, size_t len); + + static int LwsCallbackWsPeerInitiatedClose(lws *wsi, lws_callback_reasons reason, void *user, void *in, size_t len); + + static int LwsCallbackClientWritable(lws *wsi, lws_callback_reasons reason, void *user, void *in, size_t len); + + static int + LwsCallbackClientConnectionError(lws *wsi, lws_callback_reasons reason, void *user, void *in, size_t len); + + static int LwsCallbackClientReceive(lws *wsi, lws_callback_reasons reason, void *user, void *in, size_t len); + + static int + LwsCallbackClientFilterPreEstablish(lws *wsi, lws_callback_reasons reason, void *user, void *in, size_t len); + + static int LwsCallbackClientEstablished(lws *wsi, lws_callback_reasons reason, void *user, void *in, size_t len); + + static int LwsCallbackClientClosed(lws *wsi, lws_callback_reasons reason, void *user, void *in, size_t len); + + static int LwsCallbackWsiDestroy(lws *wsi, lws_callback_reasons reason, void *user, void *in, size_t len); + + static int LwsCallbackProtocolDestroy(lws *wsi, lws_callback_reasons reason, void *user, void *in, size_t len); + + static void OnOpen(EventManager *manager, uint32_t status, const std::string &message); + + static void OnError(EventManager *manager, int32_t code); + + static void OnMessage(EventManager *manager, void *data, size_t length, bool isBinary); + + static void OnClose(EventManager *manager, lws_close_status closeStatus, const std::string &closeReason); +}; +} // namespace OHOS::NetStack +#endif /* COMMUNICATIONNETSTACK_WEBSOCKET_EXEC_H */ diff --git a/frameworks/js/napi/websocket/websocket_exec/src/websocket_exec.cpp b/frameworks/js/napi/websocket/websocket_exec/src/websocket_exec.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e56558248a7ffe8a842ca80b1e01973ab9206722 --- /dev/null +++ b/frameworks/js/napi/websocket/websocket_exec/src/websocket_exec.cpp @@ -0,0 +1,729 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "websocket_exec.h" + +#include +#include +#include + +#include "constant.h" +#include "netstack_common_utils.h" +#include "netstack_log.h" +#include "netstack_napi_utils.h" +#include "securec.h" + +static constexpr const char *PATH_START = "/"; + +static constexpr const char *NAME_END = ":"; + +static constexpr const char *STATUS_LINE_SEP = " "; + +static constexpr const size_t STATUS_LINE_ELEM_NUM = 2; + +static constexpr const char *PREFIX_HTTPS = "https"; + +static constexpr const char *PREFIX_WSS = "wss"; + +static constexpr const int MAX_URI_LENGTH = 1024; + +static constexpr const int MAX_HDR_LENGTH = 1024; + +static constexpr const int FD_LIMIT_PER_THREAD = 1 + 1 + 1; + +static constexpr const int COMMON_ERROR_CODE = 200; + +static constexpr const char *EVENT_KEY_CODE = "code"; + +static constexpr const char *EVENT_KEY_STATUS = "status"; + +static constexpr const char *EVENT_KEY_REASON = "reason"; + +static constexpr const char *EVENT_KEY_MESSAGE = "message"; + +namespace OHOS::NetStack { +static const lws_protocols LWS_PROTOCOLS[] = { + {"lws-minimal-client", WebSocketExec::LwsCallback, 0, 0}, + {nullptr, nullptr, 0, 0}, // this line is needed +}; + +static const lws_retry_bo_t RETRY = { + .secs_since_valid_ping = 0, + .secs_since_valid_hangup = 10, + .jitter_percent = 20, +}; + +struct CallbackDispatcher { + lws_callback_reasons reason; + int (*callback)(lws *wsi, lws_callback_reasons reason, void *user, void *in, size_t len); +}; + +struct OnOpenClosePara { + OnOpenClosePara() : status(0) {} + uint32_t status; + std::string message; +}; + +struct OnMessagePara { + OnMessagePara() : data(nullptr), length(0), isBinary(false) {} + ~OnMessagePara() + { + if (data != nullptr) { + free(data); + } + } + void *data; + size_t length; + bool isBinary; +}; + +class UserData { +public: + struct SendData { + SendData(void *paraData, size_t paraLength, lws_write_protocol paraProtocol) + : data(paraData), length(paraLength), protocol(paraProtocol) + { + } + + SendData() = delete; + + ~SendData() = default; + + void *data; + size_t length; + lws_write_protocol protocol; + }; + + explicit UserData(lws_context *context) + : closeStatus(LWS_CLOSE_STATUS_NOSTATUS), openStatus(0), closed_(false), context_(context) + { + } + + bool IsClosed() + { + std::lock_guard lock(mutex_); + return closed_; + } + + void Close(lws_close_status status, const std::string &reason) + { + std::lock_guard lock(mutex_); + closeStatus = status; + closeReason = reason; + closed_ = true; + } + + void Push(void *data, size_t length, lws_write_protocol protocol) + { + std::lock_guard lock(mutex_); + dataQueue_.push(SendData(data, length, protocol)); + } + + SendData Pop() + { + std::lock_guard lock(mutex_); + if (dataQueue_.empty()) { + return {nullptr, 0, LWS_WRITE_TEXT}; + } + SendData data = dataQueue_.front(); + dataQueue_.pop(); + return data; + } + + void SetContext(lws_context *context) + { + context_ = context; + } + + lws_context *GetContext() + { + return context_; + } + + std::map header; + + lws_close_status closeStatus; + + std::string closeReason; + + uint32_t openStatus; + + std::string openMessage; + +private: + volatile bool closed_; + + std::mutex mutex_; + + lws_context *context_; + + std::queue dataQueue_; +}; + +template static void CallbackTemplate(uv_work_t *work, int status) +{ + (void)status; + + auto workWrapper = static_cast(work->data); + napi_env env = workWrapper->env; + auto closeScope = [env](napi_handle_scope scope) { NapiUtils::CloseScope(env, scope); }; + std::unique_ptr scope(NapiUtils::OpenScope(env), closeScope); + + napi_value obj = MakeJsValue(env, workWrapper->data); + + napi_value callback = NapiUtils::GetReference(env, workWrapper->callbackRef); + napi_value argv[1] = {obj}; + if (NapiUtils::GetValueType(env, callback) == napi_function) { + (void)NapiUtils::CallFunction(env, NapiUtils::GetUndefined(env), callback, 1, argv); + } + + delete workWrapper; + delete work; +} + +bool WebSocketExec::ParseUrl(ConnectContext *context, + char *prefix, + size_t prefixLen, + char *address, + size_t addressLen, + char *path, + size_t pathLen, + int *port) +{ + char uri[MAX_URI_LENGTH] = {0}; + if (strcpy_s(uri, MAX_URI_LENGTH, context->url.c_str()) < 0) { + NETSTACK_LOGE("strcpy_s failed"); + return false; + } + const char *tempPrefix = nullptr; + const char *tempAddress = nullptr; + const char *tempPath = nullptr; + (void)lws_parse_uri(uri, &tempPrefix, &tempAddress, port, &tempPath); + if (strcpy_s(prefix, prefixLen, tempPrefix) < 0) { + NETSTACK_LOGE("strcpy_s failed"); + return false; + } + if (strcpy_s(address, addressLen, tempAddress) < 0) { + NETSTACK_LOGE("strcpy_s failed"); + return false; + } + if (strcpy_s(path, pathLen, tempPath) < 0) { + NETSTACK_LOGE("strcpy_s failed"); + return false; + } + return true; +} + +void WebSocketExec::RunService(EventManager *manager) +{ + NETSTACK_LOGI("start service"); + if (manager == nullptr || manager->GetData() == nullptr) { + NETSTACK_LOGE("RunService para error"); + return; + } + auto userData = reinterpret_cast(manager->GetData()); + lws_context *context = userData->GetContext(); + if (context == nullptr) { + NETSTACK_LOGE("context is null"); + return; + } + while (lws_service(context, 0) >= 0) { + } + lws_context_destroy(context); + userData->SetContext(nullptr); + delete userData; + manager->SetData(nullptr); + NETSTACK_LOGI("websocket end run service"); +} + +int WebSocketExec::RaiseError(EventManager *manager) +{ + OnError(manager, COMMON_ERROR_CODE); + return -1; +} + +int WebSocketExec::HttpDummy(lws *wsi, lws_callback_reasons reason, void *user, void *in, size_t len) +{ + int ret = lws_callback_http_dummy(wsi, reason, user, in, len); + if (ret < 0) { + OnError(reinterpret_cast(user), COMMON_ERROR_CODE); + } + return ret; +} + +int WebSocketExec::LwsCallbackClientAppendHandshakeHeader(lws *wsi, + lws_callback_reasons reason, + void *user, + void *in, + size_t len) +{ + NETSTACK_LOGI("LwsCallbackClientAppendHandshakeHeader"); + auto manager = reinterpret_cast(user); + if (manager->GetData() == nullptr) { + NETSTACK_LOGE("user data is null"); + return RaiseError(manager); + } + auto userData = reinterpret_cast(manager->GetData()); + + auto payload = reinterpret_cast(in); + if (payload == nullptr || (*payload) == nullptr || len == 0) { + NETSTACK_LOGE("header payload is null, do not append header"); + return RaiseError(manager); + } + auto payloadEnd = (*payload) + len; + for (const auto &pair : userData->header) { + std::string name = pair.first + NAME_END; + if (lws_add_http_header_by_name(wsi, reinterpret_cast(name.c_str()), + reinterpret_cast(pair.second.c_str()), + static_cast(strlen(pair.second.c_str())), payload, payloadEnd)) { + NETSTACK_LOGE("add header failed"); + return RaiseError(manager); + } + } + NETSTACK_LOGI("add header OK"); + return HttpDummy(wsi, reason, user, in, len); +} + +int WebSocketExec::LwsCallbackWsPeerInitiatedClose(lws *wsi, + lws_callback_reasons reason, + void *user, + void *in, + size_t len) +{ + NETSTACK_LOGI("LwsCallbackWsPeerInitiatedClose"); + auto manager = reinterpret_cast(user); + if (manager->GetData() == nullptr) { + NETSTACK_LOGE("user data is null"); + return RaiseError(manager); + } + auto userData = reinterpret_cast(manager->GetData()); + + if (in == nullptr || len < sizeof(uint16_t)) { + NETSTACK_LOGI("No close reason"); + userData->Close(LWS_CLOSE_STATUS_NORMAL, ""); + return HttpDummy(wsi, reason, user, in, len); + } + + uint16_t closeStatus = ntohs(*reinterpret_cast(in)); + std::string closeReason; + closeReason.append(reinterpret_cast(in) + sizeof(uint16_t), len - sizeof(uint16_t)); + userData->Close(static_cast(closeStatus), closeReason); + return HttpDummy(wsi, reason, user, in, len); +} + +int WebSocketExec::LwsCallbackClientWritable(lws *wsi, lws_callback_reasons reason, void *user, void *in, size_t len) +{ + auto manager = reinterpret_cast(user); + if (manager->GetData() == nullptr) { + NETSTACK_LOGE("user data is null"); + return RaiseError(manager); + } + auto userData = reinterpret_cast(manager->GetData()); + if (userData->IsClosed()) { + NETSTACK_LOGI("need to close"); + lws_close_reason(wsi, userData->closeStatus, + reinterpret_cast(const_cast(userData->closeReason.c_str())), + strlen(userData->closeReason.c_str())); + // here do not emit error, because we close it + return -1; + } + + auto sendData = userData->Pop(); + if (sendData.data == nullptr || sendData.length == 0) { + return HttpDummy(wsi, reason, user, in, len); + } + int sendLength = lws_write(wsi, reinterpret_cast(sendData.data) + LWS_SEND_BUFFER_PRE_PADDING, + sendData.length, sendData.protocol); + free(sendData.data); + NETSTACK_LOGI("send data length = %{public}d", sendLength); + return HttpDummy(wsi, reason, user, in, len); +} + +int WebSocketExec::LwsCallbackClientConnectionError(lws *wsi, + lws_callback_reasons reason, + void *user, + void *in, + size_t len) +{ + NETSTACK_LOGI("LwsCallbackClientConnectionError %{public}s", + (in == nullptr) ? "null" : reinterpret_cast(in)); + return HttpDummy(wsi, reason, user, in, len); +} + +int WebSocketExec::LwsCallbackClientReceive(lws *wsi, lws_callback_reasons reason, void *user, void *in, size_t len) +{ + NETSTACK_LOGI("LwsCallbackClientReceive"); + OnMessage(reinterpret_cast(user), in, len, lws_frame_is_binary(wsi)); + return HttpDummy(wsi, reason, user, in, len); +} + +int WebSocketExec::LwsCallbackClientFilterPreEstablish(lws *wsi, + lws_callback_reasons reason, + void *user, + void *in, + size_t len) +{ + NETSTACK_LOGI("LwsCallbackClientFilterPreEstablish"); + auto manager = reinterpret_cast(user); + if (manager->GetData() == nullptr) { + NETSTACK_LOGE("user data is null"); + return RaiseError(manager); + } + auto userData = reinterpret_cast(manager->GetData()); + + userData->openStatus = lws_http_client_http_response(wsi); + char statusLine[MAX_HDR_LENGTH] = {0}; + if (lws_hdr_copy(wsi, statusLine, MAX_HDR_LENGTH, WSI_TOKEN_HTTP) || strlen(statusLine) == 0) { + return HttpDummy(wsi, reason, user, in, len); + } + + auto vec = CommonUtils::Split(statusLine, STATUS_LINE_SEP, STATUS_LINE_ELEM_NUM); + if (vec.size() >= FUNCTION_PARAM_TWO) { + userData->openMessage = vec[1]; + } + + return HttpDummy(wsi, reason, user, in, len); +} + +int WebSocketExec::LwsCallbackClientEstablished(lws *wsi, lws_callback_reasons reason, void *user, void *in, size_t len) +{ + NETSTACK_LOGI("LwsCallbackClientEstablished"); + auto manager = reinterpret_cast(user); + if (manager->GetData() == nullptr) { + NETSTACK_LOGE("user data is null"); + return RaiseError(manager); + } + auto userData = reinterpret_cast(manager->GetData()); + + OnOpen(reinterpret_cast(user), userData->openStatus, userData->openMessage); + return HttpDummy(wsi, reason, user, in, len); +} + +int WebSocketExec::LwsCallbackClientClosed(lws *wsi, lws_callback_reasons reason, void *user, void *in, size_t len) +{ + NETSTACK_LOGI("LwsCallbackClientClosed"); + auto manager = reinterpret_cast(user); + if (manager->GetData() == nullptr) { + NETSTACK_LOGE("user data is null"); + return RaiseError(manager); + } + auto userData = reinterpret_cast(manager->GetData()); + + OnClose(reinterpret_cast(user), userData->closeStatus, userData->closeReason); + return HttpDummy(wsi, reason, user, in, len); +} + +int WebSocketExec::LwsCallbackWsiDestroy(lws *wsi, lws_callback_reasons reason, void *user, void *in, size_t len) +{ + NETSTACK_LOGI("LwsCallbackWsiDestroy"); + lws_context_destroy(lws_get_context(wsi)); + return HttpDummy(wsi, reason, user, in, len); +} + +int WebSocketExec::LwsCallbackProtocolDestroy(lws *wsi, lws_callback_reasons reason, void *user, void *in, size_t len) +{ + NETSTACK_LOGI("LwsCallbackProtocolDestroy"); + return HttpDummy(wsi, reason, user, in, len); +} + +int WebSocketExec::LwsCallback(lws *wsi, lws_callback_reasons reason, void *user, void *in, size_t len) +{ + CallbackDispatcher dispatchers[] = { + {LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER, LwsCallbackClientAppendHandshakeHeader}, + {LWS_CALLBACK_WS_PEER_INITIATED_CLOSE, LwsCallbackWsPeerInitiatedClose}, + {LWS_CALLBACK_CLIENT_WRITEABLE, LwsCallbackClientWritable}, + {LWS_CALLBACK_CLIENT_CONNECTION_ERROR, LwsCallbackClientConnectionError}, + {LWS_CALLBACK_CLIENT_RECEIVE, LwsCallbackClientReceive}, + {LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH, LwsCallbackClientFilterPreEstablish}, + {LWS_CALLBACK_CLIENT_ESTABLISHED, LwsCallbackClientEstablished}, + {LWS_CALLBACK_CLIENT_CLOSED, LwsCallbackClientClosed}, + {LWS_CALLBACK_WSI_DESTROY, LwsCallbackWsiDestroy}, + {LWS_CALLBACK_PROTOCOL_DESTROY, LwsCallbackProtocolDestroy}, + }; + + for (const auto dispatcher : dispatchers) { + if (dispatcher.reason == reason) { + return dispatcher.callback(wsi, reason, user, in, len); + } + } + + return HttpDummy(wsi, reason, user, in, len); +} + +static inline void FillContextInfo(lws_context_creation_info &info) +{ + info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; + info.port = CONTEXT_PORT_NO_LISTEN; + info.protocols = LWS_PROTOCOLS; + info.fd_limit_per_thread = FD_LIMIT_PER_THREAD; +} + +bool WebSocketExec::ExecConnect(ConnectContext *context) +{ + NETSTACK_LOGI("begin connect, parse url"); + if (context == nullptr || context->GetManager() == nullptr) { + return false; + } + char prefix[MAX_URI_LENGTH] = {0}; + char address[MAX_URI_LENGTH] = {0}; + char pathWithoutStart[MAX_URI_LENGTH] = {0}; + int port = 0; + if (!ParseUrl(context, prefix, MAX_URI_LENGTH, address, MAX_URI_LENGTH, pathWithoutStart, MAX_URI_LENGTH, &port)) { + NETSTACK_LOGE("ParseUrl failed"); + return false; + } + std::string path = PATH_START + std::string(pathWithoutStart); + + lws_context_creation_info info = {0}; + FillContextInfo(info); + lws_context *lwsContext = lws_create_context(&info); + if (lwsContext == nullptr) { + NETSTACK_LOGE("no memory"); + return false; + } + + lws_client_connect_info connectInfo = {nullptr}; + connectInfo.context = lwsContext; + connectInfo.port = port; + connectInfo.address = address; + connectInfo.path = path.c_str(); + connectInfo.host = address; + connectInfo.origin = address; + if (strcmp(prefix, PREFIX_HTTPS) == 0 || strcmp(prefix, PREFIX_WSS) == 0) { + connectInfo.ssl_connection = + LCCSCF_USE_SSL | LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK | LCCSCF_ALLOW_INSECURE | LCCSCF_ALLOW_SELFSIGNED; + } + lws *wsi = nullptr; + connectInfo.pwsi = &wsi; + connectInfo.retry_and_idle_policy = &RETRY; + auto userData = new UserData(lwsContext); + userData->header = context->header; + auto manager = context->GetManager(); + manager->SetData(userData); + connectInfo.userdata = reinterpret_cast(manager); + if (lws_client_connect_via_info(&connectInfo) == nullptr) { + NETSTACK_LOGI("ExecConnect websocket connect failed"); + context->SetErrorCode(-1); + // here return true, means that connection failed but no error happened during exec + lws_context_destroy(lwsContext); + return true; + } + + std::thread serviceThread(RunService, manager); + serviceThread.detach(); + return true; +} + +napi_value WebSocketExec::ConnectCallback(ConnectContext *context) +{ + if (context->GetErrorCode() < 0) { + NETSTACK_LOGI("ConnectCallback connect failed"); + return NapiUtils::GetBoolean(context->GetEnv(), false); + } + NETSTACK_LOGI("ConnectCallback connect success"); + return NapiUtils::GetBoolean(context->GetEnv(), true); +} + +bool WebSocketExec::ExecSend(SendContext *context) +{ + if (context == nullptr || context->GetManager() == nullptr) { + NETSTACK_LOGE("context is null"); + return false; + } + + auto manager = context->GetManager(); + auto userData = reinterpret_cast(manager->GetData()); + if (userData == nullptr) { + NETSTACK_LOGE("user data is null"); + return false; + } + + userData->Push(context->data, context->length, context->protocol); + NETSTACK_LOGI("ExecSend OK"); + return true; +} + +napi_value WebSocketExec::SendCallback(SendContext *context) +{ + NETSTACK_LOGI("SendCallback success"); + return NapiUtils::GetBoolean(context->GetEnv(), true); +} + +bool WebSocketExec::ExecClose(CloseContext *context) +{ + if (context == nullptr || context->GetManager() == nullptr) { + NETSTACK_LOGE("context is null"); + return false; + } + + auto manager = context->GetManager(); + auto userData = reinterpret_cast(manager->GetData()); + if (userData == nullptr) { + NETSTACK_LOGE("user data is null"); + return false; + } + + userData->Close(static_cast(context->code), context->reason); + NETSTACK_LOGI("ExecClose OK"); + return true; +} + +napi_value WebSocketExec::CloseCallback(CloseContext *context) +{ + NETSTACK_LOGI("CloseCallback success"); + return NapiUtils::GetBoolean(context->GetEnv(), true); +} + +static napi_value CreateError(napi_env env, void *callbackPara) +{ + auto code = reinterpret_cast(callbackPara); + auto deleter = [](const int32_t *p) { delete p; }; + std::unique_ptr handler(code, deleter); + napi_value err = NapiUtils::CreateObject(env); + if (NapiUtils::GetValueType(env, err) != napi_object) { + return NapiUtils::GetUndefined(env); + } + NapiUtils::SetInt32Property(env, err, EVENT_KEY_CODE, *code); + return err; +} + +static napi_value CreateOpenPara(napi_env env, void *callbackPara) +{ + auto para = reinterpret_cast(callbackPara); + auto deleter = [](const OnOpenClosePara *p) { delete p; }; + std::unique_ptr handler(para, deleter); + napi_value obj = NapiUtils::CreateObject(env); + if (NapiUtils::GetValueType(env, obj) != napi_object) { + return NapiUtils::GetUndefined(env); + } + NapiUtils::SetUint32Property(env, obj, EVENT_KEY_STATUS, para->status); + NapiUtils::SetStringPropertyUtf8(env, obj, EVENT_KEY_MESSAGE, para->message); + return obj; +} + +static napi_value CreateClosePara(napi_env env, void *callbackPara) +{ + auto para = reinterpret_cast(callbackPara); + auto deleter = [](const OnOpenClosePara *p) { delete p; }; + std::unique_ptr handler(para, deleter); + napi_value obj = NapiUtils::CreateObject(env); + if (NapiUtils::GetValueType(env, obj) != napi_object) { + return NapiUtils::GetUndefined(env); + } + NapiUtils::SetUint32Property(env, obj, EVENT_KEY_CODE, para->status); + NapiUtils::SetStringPropertyUtf8(env, obj, EVENT_KEY_REASON, para->message); + return obj; +} + +static napi_value CreateMessagePara(napi_env env, void *callbackPara) +{ + auto para = reinterpret_cast(callbackPara); + auto deleter = [](const OnMessagePara *p) { delete p; }; + std::unique_ptr handler(para, deleter); + if (!para->isBinary) { + std::string str; + str.append(reinterpret_cast(para->data), para->length); + return NapiUtils::CreateStringUtf8(env, str); + } + + void *data = nullptr; + napi_value arrayBuffer = NapiUtils::CreateArrayBuffer(env, para->length, &data); + if (data != nullptr && NapiUtils::ValueIsArrayBuffer(env, arrayBuffer) && + memcpy_s(data, para->length, para->data, para->length) >= 0) { + return arrayBuffer; + } + return NapiUtils::GetUndefined(env); +} + +void WebSocketExec::OnError(EventManager *manager, int32_t code) +{ + NETSTACK_LOGI("OnError %{public}d", code); + if (manager == nullptr) { + NETSTACK_LOGE("manager is null"); + return; + } + if (!manager->HasEventListener(EventName::EVENT_ERROR)) { + NETSTACK_LOGI("no event listener: %{public}s", EventName::EVENT_ERROR); + return; + } + manager->EmitByUv(EventName::EVENT_ERROR, new int32_t(code), CallbackTemplate); +} + +void WebSocketExec::OnOpen(EventManager *manager, uint32_t status, const std::string &message) +{ + NETSTACK_LOGI("OnOpen %{public}u %{public}s", status, message.c_str()); + if (manager == nullptr) { + NETSTACK_LOGE("manager is null"); + return; + } + if (!manager->HasEventListener(EventName::EVENT_OPEN)) { + NETSTACK_LOGI("no event listener: %{public}s", EventName::EVENT_OPEN); + return; + } + auto para = new OnOpenClosePara; + para->status = status; + para->message = message; + manager->EmitByUv(EventName::EVENT_OPEN, para, CallbackTemplate); +} + +void WebSocketExec::OnClose(EventManager *manager, lws_close_status closeStatus, const std::string &closeReason) +{ + NETSTACK_LOGI("OnClose %{public}u %{public}s", closeStatus, closeReason.c_str()); + if (manager == nullptr) { + NETSTACK_LOGE("manager is null"); + return; + } + if (!manager->HasEventListener(EventName::EVENT_CLOSE)) { + NETSTACK_LOGI("no event listener: %{public}s", EventName::EVENT_CLOSE); + return; + } + auto para = new OnOpenClosePara; + para->status = closeStatus; + para->message = closeReason; + manager->EmitByUv(EventName::EVENT_CLOSE, para, CallbackTemplate); +} + +void WebSocketExec::OnMessage(EventManager *manager, void *data, size_t length, bool isBinary) +{ + NETSTACK_LOGI("OnMessage %{public}d", isBinary); + if (manager == nullptr) { + NETSTACK_LOGE("manager is null"); + return; + } + if (!manager->HasEventListener(EventName::EVENT_MESSAGE)) { + NETSTACK_LOGI("no event listener: %{public}s", EventName::EVENT_MESSAGE); + return; + } + if (length > INT32_MAX) { + NETSTACK_LOGE("data length too long"); + return; + } + auto para = new OnMessagePara; + para->data = malloc(length); + if (para->data == nullptr) { + delete para; + NETSTACK_LOGE("no memory"); + return; + } + if (memcpy_s(para->data, length, data, length) < 0) { + delete para; + NETSTACK_LOGE("mem copy failed"); + return; + } + para->length = length; + para->isBinary = isBinary; + manager->EmitByUv(EventName::EVENT_MESSAGE, para, CallbackTemplate); +} +} // namespace OHOS::NetStack \ No newline at end of file diff --git a/frameworks/js/napi/websocket/websocket_module/include/websocket_module.h b/frameworks/js/napi/websocket/websocket_module/include/websocket_module.h new file mode 100644 index 0000000000000000000000000000000000000000..63484e596e7a274bf9a9898f6e16d6dd2bd22daf --- /dev/null +++ b/frameworks/js/napi/websocket/websocket_module/include/websocket_module.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef COMMUNICATIONNETSTACK_WEBSOCKET_MODULE_H +#define COMMUNICATIONNETSTACK_WEBSOCKET_MODULE_H + +#include "napi/native_api.h" + +namespace OHOS::NetStack { +class WebSocketModule final { +public: + class WebSocket { + public: + static constexpr const char *FUNCTION_CONNECT = "connect"; + static constexpr const char *FUNCTION_SEND = "send"; + static constexpr const char *FUNCTION_CLOSE = "close"; + static constexpr const char *FUNCTION_ON = "on"; + static constexpr const char *FUNCTION_OFF = "off"; + + static napi_value Connect(napi_env env, napi_callback_info info); + + static napi_value Send(napi_env env, napi_callback_info info); + + static napi_value Close(napi_env env, napi_callback_info info); + + static napi_value On(napi_env env, napi_callback_info info); + + static napi_value Off(napi_env env, napi_callback_info info); + }; + + static constexpr const char *FUNCTION_CREATE_WEB_SOCKET = "createWebSocket"; + static constexpr const char *INTERFACE_WEB_SOCKET = "WebSocket"; + + static napi_value InitWebSocketModule(napi_env env, napi_value exports); + +private: + static napi_value CreateWebSocket(napi_env env, napi_callback_info info); + + static void DefineWebSocketClass(napi_env env, napi_value exports); + + static void FinalizeWebSocketInstance(napi_env env, void *data, void *hint); + + static void InitWebSocketProperties(napi_env env, napi_value exports); +}; +} // namespace OHOS::NetStack +#endif /* COMMUNICATIONNETSTACK_WEBSOCKET_MODULE_H */ diff --git a/frameworks/js/napi/websocket/websocket_module/src/websocket_module.cpp b/frameworks/js/napi/websocket/websocket_module/src/websocket_module.cpp new file mode 100644 index 0000000000000000000000000000000000000000..44b153d98977ddb3e8b179b503c160cbf471f527 --- /dev/null +++ b/frameworks/js/napi/websocket/websocket_module/src/websocket_module.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "websocket_module.h" + +#include "constant.h" +#include "netstack_log.h" +#include "netstack_module_template.h" +#include "websocket_async_work.h" + +namespace OHOS::NetStack { +napi_value WebSocketModule::InitWebSocketModule(napi_env env, napi_value exports) +{ + DefineWebSocketClass(env, exports); + InitWebSocketProperties(env, exports); + + return exports; +} + +napi_value WebSocketModule::CreateWebSocket(napi_env env, napi_callback_info info) +{ + return ModuleTemplate::NewInstance(env, info, INTERFACE_WEB_SOCKET, FinalizeWebSocketInstance); +} + +void WebSocketModule::DefineWebSocketClass(napi_env env, napi_value exports) +{ + std::initializer_list properties = { + DECLARE_NAPI_FUNCTION(WebSocket::FUNCTION_CONNECT, WebSocket::Connect), + DECLARE_NAPI_FUNCTION(WebSocket::FUNCTION_SEND, WebSocket::Send), + DECLARE_NAPI_FUNCTION(WebSocket::FUNCTION_CLOSE, WebSocket::Close), + DECLARE_NAPI_FUNCTION(WebSocket::FUNCTION_ON, WebSocket::On), + DECLARE_NAPI_FUNCTION(WebSocket::FUNCTION_OFF, WebSocket::Off), + }; + ModuleTemplate::DefineClass(env, exports, properties, INTERFACE_WEB_SOCKET); +} + +void WebSocketModule::InitWebSocketProperties(napi_env env, napi_value exports) +{ + std::initializer_list properties = { + DECLARE_NAPI_FUNCTION(FUNCTION_CREATE_WEB_SOCKET, CreateWebSocket), + }; + NapiUtils::DefineProperties(env, exports, properties); +} + +void WebSocketModule::FinalizeWebSocketInstance(napi_env env, void *data, void *hint) +{ + NETSTACK_LOGI("websocket handle is finalized"); +} + +napi_value WebSocketModule::WebSocket::Connect(napi_env env, napi_callback_info info) +{ + return ModuleTemplate::Interface( + env, info, "WebSocketConnect", nullptr, WebSocketAsyncWork::ExecConnect, WebSocketAsyncWork::ConnectCallback); +} + +napi_value WebSocketModule::WebSocket::Send(napi_env env, napi_callback_info info) +{ + return ModuleTemplate::Interface(env, info, "WebSocketSend", nullptr, WebSocketAsyncWork::ExecSend, + WebSocketAsyncWork::SendCallback); +} + +napi_value WebSocketModule::WebSocket::Close(napi_env env, napi_callback_info info) +{ + return ModuleTemplate::Interface(env, info, "WebSocketClose", nullptr, WebSocketAsyncWork::ExecClose, + WebSocketAsyncWork::CloseCallback); +} + +napi_value WebSocketModule::WebSocket::On(napi_env env, napi_callback_info info) +{ + ModuleTemplate::On(env, info, {EventName::EVENT_OPEN, EventName::EVENT_MESSAGE, EventName::EVENT_CLOSE}, true); + return ModuleTemplate::On(env, info, {EventName::EVENT_ERROR}, false); +} + +napi_value WebSocketModule::WebSocket::Off(napi_env env, napi_callback_info info) +{ + return ModuleTemplate::Off( + env, info, {EventName::EVENT_OPEN, EventName::EVENT_MESSAGE, EventName::EVENT_CLOSE, EventName::EVENT_ERROR}); +} + +NAPI_MODULE(webSocket, WebSocketModule::InitWebSocketModule) +} // namespace OHOS::NetStack \ No newline at end of file diff --git a/test/napi/log/test_hilog.cpp b/test/napi/log/test_hilog.cpp index 58953bcdf93f7d30705b280565f0055f3e15dc0c..dcd7faaadcaea8e039ff68b139e9f088ddce45f6 100644 --- a/test/napi/log/test_hilog.cpp +++ b/test/napi/log/test_hilog.cpp @@ -14,6 +14,8 @@ */ #include +#include +#include #include "netstack_log.h" @@ -27,21 +29,21 @@ static void StripFormatString(const std::string &prefix, std::string &str) } } -#define PRINT_LOG(LEVEL) \ - do { \ - std::string newFmt(fmt); \ - StripFormatString("{public}", newFmt); \ - StripFormatString("{private}", newFmt); \ - \ - va_list args; \ - va_start(args, fmt); \ - printf(#LEVEL " "); \ - fflush(stdout); \ - vfprintf(stdout, newFmt.c_str(), args); \ - fflush(stdout); \ - printf("\n"); \ - fflush(stdout); \ - va_end(args); \ +#define PRINT_LOG(LEVEL) \ + do { \ + std::string newFmt(fmt); \ + StripFormatString("{public}", newFmt); \ + StripFormatString("{private}", newFmt); \ + \ + va_list args; \ + va_start(args, fmt); \ + printf(#LEVEL " %d %u ", getpid(), std::this_thread::get_id()); \ + fflush(stdout); \ + vfprintf(stdout, newFmt.c_str(), args); \ + fflush(stdout); \ + printf("\n"); \ + fflush(stdout); \ + va_end(args); \ } while (0) int HiLog::Debug(const HiLogLabel &label, const char *fmt, ...) diff --git a/test/napi/websocket/CMakeLists.txt b/test/napi/websocket/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..f3ff0656a4afc65370901e50fa615729dc0f05c4 --- /dev/null +++ b/test/napi/websocket/CMakeLists.txt @@ -0,0 +1,53 @@ +# Copyright (C) 2022 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +include_directories(../../../frameworks/js/napi/websocket/async_context/include) +include_directories(../../../frameworks/js/napi/websocket/async_work/include) +include_directories(../../../frameworks/js/napi/websocket/constant/include) +include_directories(../../../frameworks/js/napi/websocket/websocket_exec/include) +include_directories(../../../frameworks/js/napi/websocket/websocket_module/include) +include_directories(../../../../../../third_party/libwebsockets/include) +include_directories(../../../../../../third_party/libwebsockets/cmake-build-debug) + +link_directories(../../../../../../third_party/libwebsockets/cmake-build-debug/lib) + +add_executable( + test_napi_websocket_exec + test_napi_exec.cpp + ../log/test_hilog.cpp + ../../../frameworks/js/napi/websocket/async_context/src/connect_context.cpp + ../../../frameworks/js/napi/websocket/async_context/src/send_context.cpp + ../../../frameworks/js/napi/websocket/async_context/src/close_context.cpp + ../../../frameworks/js/napi/websocket/websocket_module/src/websocket_module.cpp + ../../../frameworks/js/napi/websocket/websocket_exec/src/websocket_exec.cpp + ../../../frameworks/js/napi/websocket/async_work/src/websocket_async_work.cpp + ../../../frameworks/js/napi/websocket/constant/src/constant.cpp +) + +target_link_libraries(test_napi_websocket_exec websockets) +target_link_libraries(test_napi_websocket_exec crypto) +target_link_libraries(test_napi_websocket_exec ssl) +target_link_libraries(test_napi_websocket_exec ace_napi_quickjs) +target_link_libraries(test_napi_websocket_exec quickjs) +target_link_libraries(test_napi_websocket_exec uv) +target_link_libraries(test_napi_websocket_exec securec) +target_link_libraries(test_napi_websocket_exec gtestd) +target_link_libraries(test_napi_websocket_exec gmockd) +target_link_libraries(test_napi_websocket_exec gtest_maind) +target_link_libraries(test_napi_websocket_exec gmock_maind) +target_link_libraries(test_napi_websocket_exec pthread) +target_link_libraries(test_napi_websocket_exec dl) +target_link_libraries(test_napi_websocket_exec nghttp2) +target_link_libraries(test_napi_websocket_exec netstack_utils) + +set(CMAKE_CXX_FLAGS -g) diff --git a/test/napi/websocket/test_napi_exec.cpp b/test/napi/websocket/test_napi_exec.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9f2810ba2702e64ff2f7728b5cb32ffd0ddcc4e0 --- /dev/null +++ b/test/napi/websocket/test_napi_exec.cpp @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "test_common.h" + +#include "constant.h" +#include "securec.h" +#include "websocket_module.h" + +namespace OHOS::NetStack { +napi_value WEBSOCKET_HANDLE = nullptr; + +int MESSAGE_NUM = 0; + +napi_value ConnectCallback(napi_env env, napi_callback_info info) +{ + napi_value thisVal = nullptr; + size_t paramsCount = 1; + napi_value params[1] = {nullptr}; + NAPI_CALL(env, napi_get_cb_info(env, info, ¶msCount, params, &thisVal, nullptr)); + + bool connected = false; + NAPI_CALL(env, napi_get_value_bool(env, params[0], &connected)); + NETSTACK_LOGI("connected: ? %{public}d", connected); + return NapiUtils::GetUndefined(env); +} + +napi_value SendCallback(napi_env env, napi_callback_info info) +{ + NETSTACK_LOGI("Send OK"); + return NapiUtils::GetUndefined(env); +} + +napi_value OnOpen(napi_env env, napi_callback_info info) +{ + napi_value thisVal = nullptr; + size_t paramsCount = 1; + napi_value params[1] = {nullptr}; + NAPI_CALL(env, napi_get_cb_info(env, info, ¶msCount, params, &thisVal, nullptr)); + + NETSTACK_LOGI("status: %{public}u,", NapiUtils::GetUint32Property(env, params[0], "status")); + NETSTACK_LOGI("message: %{public}s", NapiUtils::GetStringPropertyUtf8(env, params[0], "message").c_str()); + + napi_value send = NapiUtils::GetNamedProperty(env, WEBSOCKET_HANDLE, WebSocketModule::WebSocket::FUNCTION_SEND); + napi_value param[FUNCTION_PARAM_TWO] = { + NapiUtils::CreateStringUtf8(env, "Hello World"), + NapiUtils::CreateFunction(env, "SendCallback", SendCallback, nullptr), + }; + NapiUtils::CallFunction(env, WEBSOCKET_HANDLE, send, FUNCTION_PARAM_TWO, param); + return NapiUtils::GetUndefined(env); +} + +static constexpr const int MAX_MESSAGE_NUM = 10; + +napi_value OnMessage(napi_env env, napi_callback_info info) +{ + napi_value thisVal = nullptr; + size_t paramsCount = 1; + napi_value params[1] = {nullptr}; + NAPI_CALL(env, napi_get_cb_info(env, info, ¶msCount, params, &thisVal, nullptr)); + + ++MESSAGE_NUM; + NETSTACK_LOGI("message: %{public}s", NapiUtils::GetStringFromValueUtf8(env, params[0]).c_str()); + + if (MESSAGE_NUM <= MAX_MESSAGE_NUM) { + napi_value send = NapiUtils::GetNamedProperty(env, WEBSOCKET_HANDLE, WebSocketModule::WebSocket::FUNCTION_SEND); + napi_value param[FUNCTION_PARAM_TWO] = { + NapiUtils::CreateStringUtf8(env, "Hello World"), + NapiUtils::CreateFunction(env, "SendCallback", SendCallback, nullptr), + }; + NapiUtils::CallFunction(env, WEBSOCKET_HANDLE, send, 1, param); + return NapiUtils::GetUndefined(env); + } + + napi_value close = NapiUtils::GetNamedProperty(env, WEBSOCKET_HANDLE, WebSocketModule::WebSocket::FUNCTION_CLOSE); + napi_value options = NapiUtils::CreateObject(env); + NapiUtils::SetUint32Property(env, options, "code", CLOSE_REASON_NORMAL_CLOSE); + NapiUtils::SetStringPropertyUtf8(env, options, "reason", "CLOSE_NORMAL"); + napi_value param[1] = {options}; + NapiUtils::CallFunction(env, WEBSOCKET_HANDLE, close, 1, param); + return NapiUtils::GetUndefined(env); +} + +[[maybe_unused]] HWTEST_F(NativeEngineTest, name, testing::ext::TestSize.Level0) +{ + auto env = (napi_env)engine_; + napi_value exports = NapiUtils::CreateObject(env); + + WebSocketModule::InitWebSocketModule(env, exports); + napi_value createWebsocket = NapiUtils::GetNamedProperty(env, exports, WebSocketModule::FUNCTION_CREATE_WEB_SOCKET); + + if (WEBSOCKET_HANDLE == nullptr) { + WEBSOCKET_HANDLE = NapiUtils::CallFunction(env, exports, createWebsocket, 0, nullptr); + } + + ASSERT_TRUE(NapiUtils::GetValueType(env, WEBSOCKET_HANDLE) == napi_object); + + napi_value connect = + NapiUtils::GetNamedProperty(env, WEBSOCKET_HANDLE, WebSocketModule::WebSocket::FUNCTION_CONNECT); + napi_value header = NapiUtils::CreateObject(env); + NapiUtils::SetStringPropertyUtf8(env, header, "Name1", "Value1"); + NapiUtils::SetStringPropertyUtf8(env, header, "Name2", "Value2"); + napi_value obj = NapiUtils::CreateObject(env); + NapiUtils::SetNamedProperty(env, obj, "header", header); + napi_value param[FUNCTION_PARAM_THREE] = { + NapiUtils::CreateStringUtf8(env, "wss://x.x.x.x:443/socket"), + obj, + NapiUtils::CreateFunction(env, "ConnectCallback", ConnectCallback, nullptr), + }; + NapiUtils::CallFunction(env, WEBSOCKET_HANDLE, connect, FUNCTION_PARAM_TWO, param); + + napi_value on = NapiUtils::GetNamedProperty(env, WEBSOCKET_HANDLE, WebSocketModule::WebSocket::FUNCTION_ON); + napi_value onOpenParam[FUNCTION_PARAM_TWO] = { + NapiUtils::CreateStringUtf8(env, "open"), + NapiUtils::CreateFunction(env, "OnOpen", OnOpen, nullptr), + }; + NapiUtils::CallFunction(env, WEBSOCKET_HANDLE, on, FUNCTION_PARAM_TWO, onOpenParam); + + napi_value onMessageParam[FUNCTION_PARAM_TWO] = { + NapiUtils::CreateStringUtf8(env, "message"), + NapiUtils::CreateFunction(env, "OnMessage", OnMessage, nullptr), + }; + + // call on("open") more times + NapiUtils::CallFunction(env, WEBSOCKET_HANDLE, on, FUNCTION_PARAM_TWO, onOpenParam); + NapiUtils::CallFunction(env, WEBSOCKET_HANDLE, on, FUNCTION_PARAM_TWO, onMessageParam); +} +} // namespace OHOS::NetStack + +int main(int argc, char **argv) +{ + testing::GTEST_FLAG(output) = "xml:./"; + testing::InitGoogleTest(&argc, argv); + + JSRuntime *rt = JS_NewRuntime(); + + if (rt == nullptr) { + return 0; + } + + JSContext *ctx = JS_NewContext(rt); + if (ctx == nullptr) { + return 0; + } + + js_std_add_helpers(ctx, 0, nullptr); + + g_nativeEngine = + reinterpret_cast(new QuickJSNativeEngine(rt, ctx, nullptr)); // default instance id 0 + + int ret = RUN_ALL_TESTS(); + (void)ret; + + g_nativeEngine->Loop(LOOP_DEFAULT); + + return 0; +} \ No newline at end of file diff --git a/test/napi/websocket/unittest/BUILD.gn b/test/napi/websocket/unittest/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..52669098d6bce480ba4e44ece9a5e4b7fe4167f6 --- /dev/null +++ b/test/napi/websocket/unittest/BUILD.gn @@ -0,0 +1,140 @@ +# Copyright (c) 2022 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//build/ohos.gni") +import("//build/test.gni") + +if ("${product_name}" == "m40") { + SUBSYSTEM_DIR = "//foundation/communication" + NETSTACK_NAPI_ROOT = "$SUBSYSTEM_DIR/netstack/frameworks/js/napi/" + + utils_source = [ + "$SUBSYSTEM_DIR/netstack/utils/base_context/src/netstack_base_context.cpp", + "$SUBSYSTEM_DIR/netstack/utils/common_utils/src/netstack_common_utils.cpp", + "$SUBSYSTEM_DIR/netstack/utils/event_manager/src/netstack_event_listener.cpp", + "$SUBSYSTEM_DIR/netstack/utils/event_manager/src/netstack_event_manager.cpp", + "$SUBSYSTEM_DIR/netstack/utils/module_template/src/netstack_module_template.cpp", + "$SUBSYSTEM_DIR/netstack/utils/napi_utils/src/netstack_napi_utils.cpp", + ] + + utils_include = [ + "$SUBSYSTEM_DIR/netstack/utils/base_async_work/include", + "$SUBSYSTEM_DIR/netstack/utils/base_context/include", + "$SUBSYSTEM_DIR/netstack/utils/common_utils/include", + "$SUBSYSTEM_DIR/netstack/utils/event_manager/include", + "$SUBSYSTEM_DIR/netstack/utils/log/include", + "$SUBSYSTEM_DIR/netstack/utils/module_template/include", + "$SUBSYSTEM_DIR/netstack/utils/napi_utils/include", + ] + + common_include = [ + "//foundation/ace/ace_engine/frameworks/base/utils", + "//foundation/ace/napi/interfaces/kits", + "//foundation/ace/napi", + "//third_party/node/src", + ] + + common_deps = [ + "//foundation/ace/napi/:ace_napi", + "//utils/native/base:utils", + ] + + common_external_deps = [ "hiviewdfx_hilog_native:libhilog" ] + + ohos_static_library("websocket_static") { + include_dirs = [ + "$NETSTACK_NAPI_ROOT/websocket/async_context/include", + "$NETSTACK_NAPI_ROOT/websocket/async_work/include", + "$NETSTACK_NAPI_ROOT/websocket/constant/include", + "$NETSTACK_NAPI_ROOT/websocket/websocket_exec/include", + "$NETSTACK_NAPI_ROOT/websocket/websocket_module/include", + ] + include_dirs += utils_include + include_dirs += common_include + include_dirs += [ + "//third_party/libwebsockets/include", + "//third_party/openssl/include", + ] + + sources = [ + "$NETSTACK_NAPI_ROOT/websocket/async_context/src/close_context.cpp", + "$NETSTACK_NAPI_ROOT/websocket/async_context/src/connect_context.cpp", + "$NETSTACK_NAPI_ROOT/websocket/async_context/src/send_context.cpp", + "$NETSTACK_NAPI_ROOT/websocket/async_work/src/websocket_async_work.cpp", + "$NETSTACK_NAPI_ROOT/websocket/constant/src/constant.cpp", + "$NETSTACK_NAPI_ROOT/websocket/websocket_exec/src/websocket_exec.cpp", + "$NETSTACK_NAPI_ROOT/websocket/websocket_module/src/websocket_module.cpp", + ] + sources += utils_source + + deps = common_deps + deps += [ + "//third_party/libwebsockets:websockets", + "//third_party/openssl:libcrypto_static", + "//third_party/openssl:ssl_source", + "//third_party/zlib:libz", + ] + + external_deps = common_external_deps + } + + ohos_unittest("websocket_test") { + module_out_path = "netstack/websocket_test" + + include_dirs = [ + "$NETSTACK_NAPI_ROOT/websocket/async_context/include", + "$NETSTACK_NAPI_ROOT/websocket/async_work/include", + "$NETSTACK_NAPI_ROOT/websocket/constant/include", + "$NETSTACK_NAPI_ROOT/websocket/websocket_exec/include", + "$NETSTACK_NAPI_ROOT/websocket/websocket_module/include", + ] + include_dirs += [ + "//third_party/libwebsockets/include", + "//third_party/openssl/include", + ] + include_dirs += utils_include + include_dirs += common_include + + sources = [ "websocket_test.cpp" ] + + deps = [ ":websocket_static" ] + deps += [ + "//third_party/libwebsockets:websockets", + "//third_party/openssl:libcrypto_static", + "//third_party/openssl:ssl_source", + "//third_party/zlib:libz", + ] + + part_name = "netstack" + subsystem_name = "communication" + } + + group("unittest") { + testonly = true + deps = [ ":websocket_test" ] + } +} else { + ohos_unittest("websocket_test") { + module_out_path = "netstack/websocket_test" + + sources = [ "websocket_unittest.cpp" ] + + part_name = "netstack" + subsystem_name = "communication" + } + + group("unittest") { + testonly = true + deps = [ ":websocket_test" ] + } +} diff --git a/test/napi/websocket/unittest/CMakeLists.txt b/test/napi/websocket/unittest/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..3114e2e2f71c34b506b6b6576f455188c2167627 --- /dev/null +++ b/test/napi/websocket/unittest/CMakeLists.txt @@ -0,0 +1,20 @@ +# Copyright (C) 2022 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +include_directories(../../../../frameworks/js/napi/websocket/async_context/include) +include_directories(../../../../frameworks/js/napi/websocket/async_work/include) +include_directories(../../../../frameworks/js/napi/websocket/constant/include) +include_directories(../../../../frameworks/js/napi/websocket/websocket_exec/include) +include_directories(../../../../frameworks/js/napi/websocket/websocket_module/include) + +add_executable(websocket_test websocket_test.cpp) \ No newline at end of file diff --git a/test/napi/websocket/unittest/websocket_test.cpp b/test/napi/websocket/unittest/websocket_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8dcddf4ef1c9518988d003ba1f071d9cb6c7bda0 --- /dev/null +++ b/test/napi/websocket/unittest/websocket_test.cpp @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "gtest/gtest.h" + +#include "websocket_async_work.h" +#include "websocket_exec.h" + +namespace OHOS::NetStack { +using namespace testing::ext; +class WebsocketTest : public testing::Test { +public: + static void SetUpTestCase(); + static void TearDownTestCase(); + void SetUp(); + void TearDown(); +}; + +void WebsocketTest::SetUpTestCase() {} + +void WebsocketTest::TearDownTestCase() {} + +void WebsocketTest::SetUp() {} + +void WebsocketTest::TearDown() {} + +HWTEST_F(WebsocketTest, TestExecConnect1, testing::ext::TestSize.Level4) +{ + bool ret = WebSocketExec::ExecConnect(nullptr); + ASSERT_FALSE(ret); +} + +HWTEST_F(WebsocketTest, TestExecConnect2, testing::ext::TestSize.Level4) +{ + auto context = new ConnectContext(nullptr, nullptr); + bool ret = WebSocketExec::ExecConnect(context); + delete context; + ASSERT_FALSE(ret); +} + +HWTEST_F(WebsocketTest, TestExecConnect3, testing::ext::TestSize.Level4) +{ + auto context = new ConnectContext(nullptr, nullptr); + WebSocketAsyncWork::ExecConnect(nullptr, context); + bool ret = context->IsExecOK(); + delete context; + ASSERT_FALSE(ret); +} + +HWTEST_F(WebsocketTest, TestExecConnect4, testing::ext::TestSize.Level4) +{ + auto context = new ConnectContext(nullptr, nullptr); + WebSocketAsyncWork::ExecConnect(nullptr, context); + bool ret = context->IsParseOK(); + delete context; + ASSERT_FALSE(ret); +} + +HWTEST_F(WebsocketTest, TestExecConnect5, testing::ext::TestSize.Level4) +{ + auto context = new ConnectContext(nullptr, nullptr); + WebSocketAsyncWork::ExecConnect(nullptr, context); + int32_t ret = context->GetErrorCode(); + delete context; + ASSERT_EQ(ret, -1); +} + +HWTEST_F(WebsocketTest, TestExecConnect6, testing::ext::TestSize.Level4) +{ + auto context = new ConnectContext(nullptr, nullptr); + ASSERT_FALSE(WebSocketExec::ExecConnect(context)); + int32_t ret = context->GetErrorCode(); + delete context; + ASSERT_EQ(ret, 0); +} + +HWTEST_F(WebsocketTest, TestExecClose1, testing::ext::TestSize.Level4) +{ + bool ret = WebSocketExec::ExecClose(nullptr); + ASSERT_FALSE(ret); +} + +HWTEST_F(WebsocketTest, TestExecClose2, testing::ext::TestSize.Level4) +{ + auto context = new CloseContext(nullptr, nullptr); + bool ret = WebSocketExec::ExecClose(context); + delete context; + ASSERT_FALSE(ret); +} + +HWTEST_F(WebsocketTest, TestExecClose3, testing::ext::TestSize.Level4) +{ + auto context = new CloseContext(nullptr, nullptr); + WebSocketAsyncWork::ExecClose(nullptr, context); + bool ret = context->IsExecOK(); + delete context; + ASSERT_FALSE(ret); +} + +HWTEST_F(WebsocketTest, TestExecClose4, testing::ext::TestSize.Level4) +{ + auto context = new CloseContext(nullptr, nullptr); + WebSocketAsyncWork::ExecClose(nullptr, context); + bool ret = context->IsParseOK(); + delete context; + ASSERT_FALSE(ret); +} + +HWTEST_F(WebsocketTest, TestExecClose5, testing::ext::TestSize.Level4) +{ + auto context = new CloseContext(nullptr, nullptr); + WebSocketAsyncWork::ExecClose(nullptr, context); + int32_t ret = context->GetErrorCode(); + delete context; + ASSERT_EQ(ret, -1); +} + +HWTEST_F(WebsocketTest, TestExecClose6, testing::ext::TestSize.Level4) +{ + auto context = new CloseContext(nullptr, nullptr); + ASSERT_FALSE(WebSocketExec::ExecClose(context)); + int32_t ret = context->GetErrorCode(); + delete context; + ASSERT_EQ(ret, 0); +} + +HWTEST_F(WebsocketTest, TestExecSend1, testing::ext::TestSize.Level4) +{ + bool ret = WebSocketExec::ExecSend(nullptr); + ASSERT_FALSE(ret); +} + +HWTEST_F(WebsocketTest, TestExecSend2, testing::ext::TestSize.Level4) +{ + auto context = new SendContext(nullptr, nullptr); + bool ret = WebSocketExec::ExecSend(context); + delete context; + ASSERT_FALSE(ret); +} + +HWTEST_F(WebsocketTest, TestExecSend3, testing::ext::TestSize.Level4) +{ + auto context = new SendContext(nullptr, nullptr); + WebSocketAsyncWork::ExecSend(nullptr, context); + bool ret = context->IsExecOK(); + delete context; + ASSERT_FALSE(ret); +} + +HWTEST_F(WebsocketTest, TestExecSend4, testing::ext::TestSize.Level4) +{ + auto context = new SendContext(nullptr, nullptr); + WebSocketAsyncWork::ExecSend(nullptr, context); + bool ret = context->IsParseOK(); + delete context; + ASSERT_FALSE(ret); +} + +HWTEST_F(WebsocketTest, TestExecSend5, testing::ext::TestSize.Level4) +{ + auto context = new SendContext(nullptr, nullptr); + WebSocketAsyncWork::ExecSend(nullptr, context); + int32_t ret = context->GetErrorCode(); + delete context; + ASSERT_EQ(ret, -1); +} + +HWTEST_F(WebsocketTest, TestExecSend6, testing::ext::TestSize.Level4) +{ + auto context = new SendContext(nullptr, nullptr); + ASSERT_FALSE(WebSocketExec::ExecSend(context)); + int32_t ret = context->GetErrorCode(); + delete context; + ASSERT_EQ(ret, 0); +} + +} // namespace OHOS::NetStack \ No newline at end of file diff --git a/test/napi/websocket/unittest/websocket_unittest.cpp b/test/napi/websocket/unittest/websocket_unittest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..561c8c287924c8322687b6d12993eec3bc37afd0 --- /dev/null +++ b/test/napi/websocket/unittest/websocket_unittest.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "gtest/gtest.h" + +namespace OHOS::NetStack { +using namespace testing::ext; +class WebsocketTest : public testing::Test { +public: + static void SetUpTestCase(); + static void TearDownTestCase(); + void SetUp(); + void TearDown(); +}; + +void WebsocketTest::SetUpTestCase() {} + +void WebsocketTest::TearDownTestCase() {} + +void WebsocketTest::SetUp() {} + +void WebsocketTest::TearDown() {} + +HWTEST_F(WebsocketTest, TestExecConnect, testing::ext::TestSize.Level4) +{ + ASSERT_TRUE(true); +} +} // namespace OHOS::NetStack \ No newline at end of file diff --git a/utils/base_context/include/netstack_base_context.h b/utils/base_context/include/netstack_base_context.h index 84f11eb87c8b68359b2593857ea20cdbe72077c7..4d05cfc4775638e6d989537aab9439f2cf0b68a1 100644 --- a/utils/base_context/include/netstack_base_context.h +++ b/utils/base_context/include/netstack_base_context.h @@ -74,6 +74,8 @@ public: [[nodiscard]] bool IsNeedPromise() const; + EventManager *GetManager() const; + protected: EventManager *manager_; diff --git a/utils/base_context/src/netstack_base_context.cpp b/utils/base_context/src/netstack_base_context.cpp index b5f999e978e82407d7f3dc1aced2b331a9d1cfa3..20162d60320dc2419e93117e22a23c4082758f6e 100644 --- a/utils/base_context/src/netstack_base_context.cpp +++ b/utils/base_context/src/netstack_base_context.cpp @@ -151,4 +151,9 @@ bool BaseContext::IsNeedPromise() const { return needPromise_; } + +EventManager *BaseContext::GetManager() const +{ + return manager_; +} } // namespace OHOS::NetStack \ No newline at end of file diff --git a/utils/common_utils/include/netstack_common_utils.h b/utils/common_utils/include/netstack_common_utils.h index 353fe60926e55cbeb06e2cddec3626d26084a3e2..7fe078f73d06fba847e83581814509b062f4ee10 100644 --- a/utils/common_utils/include/netstack_common_utils.h +++ b/utils/common_utils/include/netstack_common_utils.h @@ -22,6 +22,8 @@ namespace OHOS::NetStack::CommonUtils { std::vector Split(const std::string &str, const std::string &sep); +std::vector Split(const std::string &str, const std::string &sep, size_t size); + std::string Strip(const std::string &str, char ch = ' '); std::string ToLower(const std::string &s); diff --git a/utils/common_utils/src/netstack_common_utils.cpp b/utils/common_utils/src/netstack_common_utils.cpp index 8ffe3746d373ecc22e497d1f0d83427ad4c774cd..5e589c41d584b479a600dc8da80a3582b6f1e4a1 100644 --- a/utils/common_utils/src/netstack_common_utils.cpp +++ b/utils/common_utils/src/netstack_common_utils.cpp @@ -34,6 +34,27 @@ std::vector Split(const std::string &str, const std::string &sep) return res; } +std::vector Split(const std::string &str, const std::string &sep, size_t size) +{ + std::string s = str; + std::vector res; + while (!s.empty()) { + if (res.size() + 1 == size) { + res.emplace_back(s); + break; + } + + size_t pos = s.find(sep); + if (pos == std::string::npos) { + res.emplace_back(s); + break; + } + res.emplace_back(s.substr(0, pos)); + s = s.substr(pos + sep.size()); + } + return res; +} + std::string Strip(const std::string &str, char ch) { int64_t i = 0; diff --git a/utils/event_manager/include/netstack_event_listener.h b/utils/event_manager/include/netstack_event_listener.h index 2e929f690ed89aaf1e6a945fe6611ace43050884..b15663611bf284e3b9f3721f12bfa86eed6f5c2b 100644 --- a/utils/event_manager/include/netstack_event_listener.h +++ b/utils/event_manager/include/netstack_event_listener.h @@ -20,6 +20,7 @@ #include "napi/native_api.h" #include "noncopyable.h" +#include "uv.h" namespace OHOS::NetStack { class EventListener { @@ -40,8 +41,16 @@ public: [[nodiscard]] bool MatchOnce(const std::string &type) const; + [[nodiscard]] bool MatchType(const std::string &type) const; + [[nodiscard]] bool IsAsyncCallback() const; + void EmitByUv(const std::string &type, void *data, void(Handler)(uv_work_t *, int status)) const; + + [[nodiscard]] napi_env GetEnv() const; + + [[nodiscard]] napi_ref GetCallbackRef() const; + private: napi_env env_; @@ -53,6 +62,16 @@ private: bool asyncCallback_; }; + +struct UvWorkWrapper { + UvWorkWrapper() = delete; + + explicit UvWorkWrapper(void *theData, napi_env theEnv, napi_ref theCallbackRef); + + void *data; + napi_env env; + napi_ref callbackRef; +}; } // namespace OHOS::NetStack #endif /* COMMUNICATIONNETSTACK_EVENT_LISTENER_H */ diff --git a/utils/event_manager/include/netstack_event_manager.h b/utils/event_manager/include/netstack_event_manager.h index 51568acdcb1492cf0f3dde0ac92b3faee8436465..c421f5f4433254b232b96f32cd487b4378c9bbd4 100644 --- a/utils/event_manager/include/netstack_event_manager.h +++ b/utils/event_manager/include/netstack_event_manager.h @@ -36,6 +36,10 @@ public: [[nodiscard]] void *GetData(); + void EmitByUv(const std::string &type, void *data, void(Handler)(uv_work_t *, int status)); + + bool HasEventListener(const std::string &type); + private: std::mutex mutex_; diff --git a/utils/event_manager/src/netstack_event_listener.cpp b/utils/event_manager/src/netstack_event_listener.cpp index 96d738e24dfa4c9fc2cb59ab56dce853f1207adf..29455f52ad1da244c920febd208001712ac43587 100644 --- a/utils/event_manager/src/netstack_event_listener.cpp +++ b/utils/event_manager/src/netstack_event_listener.cpp @@ -101,8 +101,41 @@ bool EventListener::MatchOnce(const std::string &type) const return once_; } +bool EventListener::MatchType(const std::string &type) const +{ + return type_ == type; +} + bool EventListener::IsAsyncCallback() const { return asyncCallback_; } + +void EventListener::EmitByUv(const std::string &type, void *data, void(Handler)(uv_work_t *, int status)) const +{ + if (type_ != type) { + return; + } + + if (callbackRef_ == nullptr) { + return; + } + + NapiUtils::CreateUvQueueWork(env_, data, Handler); +} + +napi_env EventListener::GetEnv() const +{ + return env_; +} + +napi_ref EventListener::GetCallbackRef() const +{ + return callbackRef_; +} + +UvWorkWrapper::UvWorkWrapper(void *theData, napi_env theEnv, napi_ref theCallbackRef) + : data(theData), env(theEnv), callbackRef(theCallbackRef) +{ +} } // namespace OHOS::NetStack diff --git a/utils/event_manager/src/netstack_event_manager.cpp b/utils/event_manager/src/netstack_event_manager.cpp index f531ea66a2749b12387509f264cbc433c2fd6af1..8c05ef26becf9239242d3e6c500cb11674df4d18 100644 --- a/utils/event_manager/src/netstack_event_manager.cpp +++ b/utils/event_manager/src/netstack_event_manager.cpp @@ -15,8 +15,6 @@ #include "netstack_event_manager.h" -#include - namespace OHOS::NetStack { static constexpr const int CALLBACK_PARAM_NUM = 1; @@ -30,6 +28,10 @@ void EventManager::AddListener(napi_env env, bool once, bool asyncCallback) { + auto it = std::remove_if(listeners_.begin(), listeners_.end(), + [type](const EventListener &listener) -> bool { return listener.MatchType(type); }); + listeners_.erase(it, listeners_.end()); + listeners_.emplace_back(EventListener(env, type, callback, once, asyncCallback)); } @@ -75,4 +77,26 @@ void *EventManager::GetData() std::lock_guard lock(mutex_); return data_; } + +void EventManager::EmitByUv(const std::string &type, void *data, void(Handler)(uv_work_t *, int status)) +{ + std::lock_guard lock(mutex_); + + std::for_each(listeners_.begin(), listeners_.end(), [type, data, Handler](const EventListener &listener) { + auto workWrapper = new UvWorkWrapper(data, listener.GetEnv(), listener.GetCallbackRef()); + listener.EmitByUv(type, workWrapper, Handler); + }); + + auto it = std::remove_if(listeners_.begin(), listeners_.end(), + [type](const EventListener &listener) -> bool { return listener.MatchOnce(type); }); + listeners_.erase(it, listeners_.end()); +} + +bool EventManager::HasEventListener(const std::string &type) +{ + std::lock_guard lock(mutex_); + + return std::any_of(listeners_.begin(), listeners_.end(), + [&type](const EventListener &listener) -> bool { return listener.MatchType(type); }); +} } // namespace OHOS::NetStack \ No newline at end of file diff --git a/utils/log/include/netstack_log.h b/utils/log/include/netstack_log.h index 3d20358cb67509d43ccdf7820caa40f190a2508a..4353ff3bcdcdb6c324ae556439f2f074c4f5d71f 100644 --- a/utils/log/include/netstack_log.h +++ b/utils/log/include/netstack_log.h @@ -37,8 +37,13 @@ static constexpr OHOS::HiviewDFX::HiLogLabel NETSTACK_LOG_LABEL = {LOG_CORE, NET #else -#include -#include +#ifdef _WIN32 +#undef MAKE_FILE_NAME +#define MAKE_FILE_NAME (strrchr(__FILE__, '\\') + 1) +#endif /* _WIN32 */ + +#include +#include #include "securec.h" diff --git a/utils/module_template/include/netstack_module_template.h b/utils/module_template/include/netstack_module_template.h index e101508ababdc191443cffd1da1f528e2816f58c..7248dcbef39d076bfa5f0499cc73ae022319c3c5 100644 --- a/utils/module_template/include/netstack_module_template.h +++ b/utils/module_template/include/netstack_module_template.h @@ -20,6 +20,7 @@ #include "netstack_base_async_work.h" #include "netstack_base_context.h" +#include "netstack_log.h" #define MAX_PARAM_NUM 64 @@ -46,7 +47,7 @@ napi_value Interface(napi_env env, auto context = new Context(env, manager); context->ParseParams(params, paramsCount); - NETSTACK_LOGE("js params parse OK ? %{public}d", context->IsParseOK()); + NETSTACK_LOGI("js params parse OK ? %{public}d", context->IsParseOK()); if (Work != nullptr) { if (!Work(env, thisVal, context)) { NETSTACK_LOGE("work failed error code = %{public}d", context->GetErrorCode()); @@ -55,6 +56,7 @@ napi_value Interface(napi_env env, context->CreateAsyncWork(asyncWorkName, executor, callback); if (NapiUtils::GetValueType(env, context->GetCallback()) != napi_function) { + NETSTACK_LOGI("context->CreatePromise()"); return context->CreatePromise(); } return NapiUtils::GetUndefined(env); diff --git a/utils/napi_utils/include/netstack_napi_utils.h b/utils/napi_utils/include/netstack_napi_utils.h index d8a2beb1c2376c3f910288d4b82785b4c03a2ba1..720eae3067e6ef80d2fa035d30971871012b8394 100644 --- a/utils/napi_utils/include/netstack_napi_utils.h +++ b/utils/napi_utils/include/netstack_napi_utils.h @@ -21,6 +21,7 @@ #include "napi/native_api.h" #include "napi/native_common.h" +#include "uv.h" namespace OHOS::NetStack::NapiUtils { napi_valuetype GetValueType(napi_env env, napi_value value); @@ -91,6 +92,8 @@ bool GetBooleanProperty(napi_env env, napi_value object, const std::string &prop void SetBooleanProperty(napi_env env, napi_value object, const std::string &name, bool value); +napi_value GetBoolean(napi_env env, bool value); + /* define properties */ void DefineProperties(napi_env env, napi_value object, @@ -100,6 +103,14 @@ void DefineProperties(napi_env env, napi_value JsonStringify(napi_env env, napi_value object); napi_value JsonParse(napi_env env, napi_value str); + +/* libuv */ +void CreateUvQueueWork(napi_env env, void *data, void(Handler)(uv_work_t *, int status)); + +/* scope */ +napi_handle_scope OpenScope(napi_env env); + +void CloseScope(napi_env env, napi_handle_scope scope); } // namespace OHOS::NetStack::NapiUtils #endif /* COMMUNICATIONNETSTACK_NETSTACK_NAPI_UTILS_H */ diff --git a/utils/napi_utils/src/netstack_napi_utils.cpp b/utils/napi_utils/src/netstack_napi_utils.cpp index cd25ccba44d1bd101ee379106eb12b158d530e4a..171c66c533259ad17a4c5f9b9151bae277dee286 100644 --- a/utils/napi_utils/src/netstack_napi_utils.cpp +++ b/utils/napi_utils/src/netstack_napi_utils.cpp @@ -298,6 +298,13 @@ void SetBooleanProperty(napi_env env, napi_value object, const std::string &name napi_set_named_property(env, object, name.c_str(), jsValue); } +napi_value GetBoolean(napi_env env, bool value) +{ + napi_value jsValue = nullptr; + NAPI_CALL(env, napi_get_boolean(env, value, &jsValue)); + return jsValue; +} + /* define properties */ void DefineProperties(napi_env env, napi_value object, @@ -357,4 +364,30 @@ napi_value JsonParse(napi_env env, napi_value str) NAPI_CALL_BASE(env, napi_call_function(env, json, parse, 1, argv, &res), undefined); return res; } + +/* libuv */ +void CreateUvQueueWork(napi_env env, void *data, void(Handler)(uv_work_t *, int status)) +{ + uv_loop_s *loop = nullptr; + NAPI_CALL_RETURN_VOID(env, napi_get_uv_event_loop(env, &loop)); + + auto work = new uv_work_t; + work->data = data; + + (void)uv_queue_work( + loop, work, [](uv_work_t *) {}, Handler); +} + +/* scope */ +napi_handle_scope OpenScope(napi_env env) +{ + napi_handle_scope scope = nullptr; + NAPI_CALL(env, napi_open_handle_scope(env, &scope)); + return scope; +} + +void CloseScope(napi_env env, napi_handle_scope scope) +{ + (void)napi_close_handle_scope(env, scope); +} } // namespace OHOS::NetStack::NapiUtils \ No newline at end of file