diff --git a/CMakeLists.txt b/CMakeLists.txt index df12a6e98cfa445bcfa569e47f8ca27566bafabe..3fece9a9fde7e77636485f3ad413a04fadeea618 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,6 +38,8 @@ include_directories(../../../base/hiviewdfx/hilog/interfaces/native/innerkits/in include_directories(test/napi/common) include_directories(../../../foundation/ace/ace_engine/frameworks/base/utils) +include_directories(../../../foundation/ace/napi/interfaces/innerkits) +include_directories(../../../for_linux) include_directories(../../../third_party/quickjs) @@ -60,4 +62,5 @@ link_directories(cmake-build-debug/utils) add_subdirectory(utils) add_subdirectory(test/napi/http) add_subdirectory(test/napi/socket) +add_subdirectory(test/napi/fetch) add_subdirectory(test/utils/napi_utils/unittest) \ No newline at end of file diff --git a/frameworks/js/napi/BUILD.gn b/frameworks/js/napi/BUILD.gn index ac92ef30d1477aa4c4a3317d32d4d70d9f0744a6..2836e3a6659f1ab48938ac53d0e2cc6629b092b6 100644 --- a/frameworks/js/napi/BUILD.gn +++ b/frameworks/js/napi/BUILD.gn @@ -153,12 +153,27 @@ ohos_shared_library("websocket") { } ohos_shared_library("fetch") { - include_dirs = [ "$NETSTACK_NAPI_ROOT/fetch/fetch_module/include" ] + include_dirs = [ + "$NETSTACK_NAPI_ROOT/fetch/async_context/include", + "$NETSTACK_NAPI_ROOT/fetch/async_work/include", + "$NETSTACK_NAPI_ROOT/fetch/constant/include", + "$NETSTACK_NAPI_ROOT/fetch/fetch_exec/include", + "$NETSTACK_NAPI_ROOT/fetch/fetch_module/include", + "$NETSTACK_NAPI_ROOT/fetch/options/include", + ] include_dirs += utils_include include_dirs += common_include include_dirs += [ "//third_party/curl/include" ] - sources = [ "fetch/fetch_module/src/fetch_module.cpp" ] + sources = [ + "fetch/async_context/src/fetch_context.cpp", + "fetch/async_work/src/fetch_async_work.cpp", + "fetch/constant/src/constant.cpp", + "fetch/fetch_exec/src/fetch_exec.cpp", + "fetch/fetch_module/src/fetch_module.cpp", + "fetch/options/src/fetch_request.cpp", + "fetch/options/src/fetch_response.cpp", + ] sources += utils_source deps = common_deps diff --git a/frameworks/js/napi/fetch/async_context/include/fetch_context.h b/frameworks/js/napi/fetch/async_context/include/fetch_context.h new file mode 100644 index 0000000000000000000000000000000000000000..e8bf9d066fae4f54e4c90e6c5c96c4d1c6491d69 --- /dev/null +++ b/frameworks/js/napi/fetch/async_context/include/fetch_context.h @@ -0,0 +1,74 @@ +/* + * 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_FETCH_CONTEXT_H +#define COMMUNICATIONNETSTACK_FETCH_CONTEXT_H + +#include "fetch_request.h" +#include "fetch_response.h" +#include "netstack_base_context.h" +#include "noncopyable.h" + +namespace OHOS::NetStack { +class FetchContext final : public BaseContext { +public: + ACE_DISALLOW_COPY_AND_MOVE(FetchContext); + + FetchContext() = delete; + + explicit FetchContext(napi_env env, EventManager *manager); + + void ParseParams(napi_value *params, size_t paramsCount); + + [[nodiscard]] napi_value GetSuccessCallback() const; + + [[nodiscard]] napi_value GetFailCallback() const; + + [[nodiscard]] napi_value GetCompleteCallback() const; + + [[nodiscard]] std::string GetResponseType() const; + + FetchRequest request; + + FetchResponse response; + +private: + napi_status SetSuccessCallback(napi_value callback); + + napi_status SetFailCallback(napi_value callback); + + napi_status SetCompleteCallback(napi_value callback); + + bool CheckParamsType(napi_value *params, size_t paramsCount); + + void ParseHeader(napi_value optionsValue); + + bool ParseData(napi_value optionsValue); + + bool GetRequestBody(napi_value data); + + bool GetUrlParam(napi_value data); + + std::string responseType_; + + napi_ref successCallback_; + + napi_ref failCallback_; + + napi_ref completeCallback_; +}; +} // namespace OHOS::NetStack + +#endif /* COMMUNICATIONNETSTACK_FETCH_CONTEXT_H */ diff --git a/frameworks/js/napi/fetch/async_context/src/fetch_context.cpp b/frameworks/js/napi/fetch/async_context/src/fetch_context.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d863465fa1d3b3f5b70485fd06bbdf28f8c3ff4a --- /dev/null +++ b/frameworks/js/napi/fetch/async_context/src/fetch_context.cpp @@ -0,0 +1,255 @@ +/* + * 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 "fetch_context.h" + +#include "constant.h" +#include "fetch_exec.h" +#include "netstack_common_utils.h" +#include "netstack_log.h" +#include "netstack_napi_utils.h" + +namespace OHOS::NetStack { +FetchContext::FetchContext(napi_env env, EventManager *manager) + : BaseContext(env, manager), successCallback_(nullptr), failCallback_(nullptr), completeCallback_(nullptr) +{ +} + +void FetchContext::ParseParams(napi_value *params, size_t paramsCount) +{ + SetNeedPromise(false); + + bool valid = CheckParamsType(params, paramsCount); + if (!valid) { + NETSTACK_LOGE("fetch CheckParamsType failed"); + return; + } + + request.SetUrl(NapiUtils::GetStringPropertyUtf8(GetEnv(), params[0], FetchConstant::PARAM_KEY_URL)); + request.SetMethod(NapiUtils::GetStringPropertyUtf8(GetEnv(), params[0], FetchConstant::PARAM_KEY_METHOD)); + if (request.GetMethod().empty()) { + request.SetMethod(FetchConstant::HTTP_METHOD_GET); + } + if (NapiUtils::HasNamedProperty(GetEnv(), params[0], FetchConstant::PARAM_KEY_RESPONSE_TYPE)) { + responseType_ = NapiUtils::GetStringPropertyUtf8(GetEnv(), params[0], FetchConstant::PARAM_KEY_RESPONSE_TYPE); + } + ParseHeader(params[0]); + ParseData(params[0]); + + napi_value success = NapiUtils::GetNamedProperty(GetEnv(), params[0], FetchConstant::PARAM_KEY_SUCCESS); + if (NapiUtils::GetValueType(GetEnv(), success) == napi_function) { + if (SetSuccessCallback(success) != napi_ok) { + NETSTACK_LOGE("SetSuccessCallback failed"); + return; + } + } + napi_value fail = NapiUtils::GetNamedProperty(GetEnv(), params[0], FetchConstant::PARAM_KEY_FAIL); + if (NapiUtils::GetValueType(GetEnv(), fail) == napi_function) { + if (SetFailCallback(fail) != napi_ok) { + NETSTACK_LOGE("SetFailCallback failed"); + return; + } + } + napi_value complete = NapiUtils::GetNamedProperty(GetEnv(), params[0], FetchConstant::PARAM_KEY_COMPLETE); + if (NapiUtils::GetValueType(GetEnv(), complete) == napi_function) { + if (SetCompleteCallback(complete) != napi_ok) { + NETSTACK_LOGE("SetCompleteCallback failed"); + return; + } + } + NETSTACK_LOGI("fetch parse OK"); + SetParseOK(true); +} + +bool FetchContext::CheckParamsType(napi_value *params, size_t paramsCount) +{ + if (paramsCount == 1) { + return NapiUtils::GetValueType(GetEnv(), params[0]) == napi_object; + } + return false; +} + +void FetchContext::ParseHeader(napi_value optionsValue) +{ + if (!NapiUtils::HasNamedProperty(GetEnv(), optionsValue, FetchConstant::PARAM_KEY_HEADER)) { + return; + } + napi_value header = NapiUtils::GetNamedProperty(GetEnv(), optionsValue, FetchConstant::PARAM_KEY_HEADER); + if (NapiUtils::GetValueType(GetEnv(), header) != napi_object) { + return; + } + auto names = NapiUtils::GetPropertyNames(GetEnv(), header); + std::for_each(names.begin(), names.end(), [header, this](const std::string &name) { + auto value = NapiUtils::GetStringPropertyUtf8(GetEnv(), header, name); + if (!value.empty()) { + request.SetHeader(CommonUtils::ToLower(name), value); + } + }); +} + +bool FetchContext::ParseData(napi_value optionsValue) +{ + if (!NapiUtils::HasNamedProperty(GetEnv(), optionsValue, FetchConstant::PARAM_KEY_DATA)) { + return FetchExec::MethodForGet(request.GetMethod()); + } + napi_value data = NapiUtils::GetNamedProperty(GetEnv(), optionsValue, FetchConstant::PARAM_KEY_DATA); + + if (FetchExec::MethodForGet(request.GetMethod())) { + return GetUrlParam(data); + } + if (FetchExec::MethodForPost(request.GetMethod())) { + return GetRequestBody(data); + } + + NETSTACK_LOGE("ParseData failed, method is not supported %{public}s", request.GetMethod().c_str()); + return false; +} + +bool FetchContext::GetRequestBody(napi_value data) +{ + /* if body is empty, return false, or curl will wait for body */ + + napi_valuetype type = NapiUtils::GetValueType(GetEnv(), data); + if (type == napi_string) { + auto body = NapiUtils::GetStringFromValueUtf8(GetEnv(), data); + if (body.empty()) { + NETSTACK_LOGE("body napi_string is empty"); + return false; + } + request.SetBody(body.c_str(), body.size()); + return true; + } + if (type == napi_object) { + std::string body = NapiUtils::GetStringFromValueUtf8(GetEnv(), NapiUtils::JsonStringify(GetEnv(), data)); + if (body.empty()) { + NETSTACK_LOGE("body napi_object is empty"); + return false; + } + request.SetBody(body.c_str(), body.size()); + return true; + } + + NETSTACK_LOGE("Body just support string and object"); + return false; +} + +bool FetchContext::GetUrlParam(napi_value data) +{ + std::string url = request.GetUrl(); + if (url.empty()) { + NETSTACK_LOGE("url is empty!"); + return false; + } + + std::string param; + std::size_t index = url.find(FetchConstant::HTTP_URL_PARAM_START); + if (index != std::string::npos) { + param = url.substr(index + 1); + url = url.substr(0, index); + } + + napi_valuetype type = NapiUtils::GetValueType(GetEnv(), data); + if (type == napi_string) { + std::string extraParam = NapiUtils::GetStringFromValueUtf8(GetEnv(), data); + + request.SetUrl(FetchExec::MakeUrl(url, param, extraParam)); + return true; + } + if (type != napi_object) { + return true; + } + + std::string extraParam; + auto names = NapiUtils::GetPropertyNames(GetEnv(), data); + std::for_each(names.begin(), names.end(), [this, data, &extraParam](std::string name) { + auto value = NapiUtils::GetStringPropertyUtf8(GetEnv(), data, name); + NETSTACK_LOGI("url param name = %{public}s, value = %{public}s", name.c_str(), value.c_str()); + if (!name.empty() && !value.empty()) { + bool encodeName = FetchExec::EncodeUrlParam(name); + bool encodeValue = FetchExec::EncodeUrlParam(value); + if (encodeName || encodeValue) { + request.SetHeader(FetchConstant::HTTP_CONTENT_TYPE, FetchConstant::HTTP_CONTENT_TYPE_URL_ENCODE); + } + extraParam += + name + FetchConstant::HTTP_URL_NAME_VALUE_SEPARATOR + value + FetchConstant::HTTP_URL_PARAM_SEPARATOR; + } + }); + if (!extraParam.empty()) { + extraParam = extraParam.substr(0, extraParam.size() - 1); // remove the last & + } + + request.SetUrl(FetchExec::MakeUrl(url, param, extraParam)); + return true; +} + +napi_status FetchContext::SetSuccessCallback(napi_value callback) +{ + if (successCallback_ != nullptr) { + (void)napi_delete_reference(GetEnv(), successCallback_); + } + return napi_create_reference(GetEnv(), callback, 1, &successCallback_); +} + +napi_status FetchContext::SetFailCallback(napi_value callback) +{ + if (failCallback_ != nullptr) { + (void)napi_delete_reference(GetEnv(), failCallback_); + } + return napi_create_reference(GetEnv(), callback, 1, &failCallback_); +} + +napi_status FetchContext::SetCompleteCallback(napi_value callback) +{ + if (completeCallback_ != nullptr) { + (void)napi_delete_reference(GetEnv(), completeCallback_); + } + return napi_create_reference(GetEnv(), callback, 1, &completeCallback_); +} + +napi_value FetchContext::GetSuccessCallback() const +{ + if (successCallback_ == nullptr) { + return nullptr; + } + napi_value callback = nullptr; + NAPI_CALL(GetEnv(), napi_get_reference_value(GetEnv(), successCallback_, &callback)); + return callback; +} + +napi_value FetchContext::GetFailCallback() const +{ + if (failCallback_ == nullptr) { + return nullptr; + } + napi_value callback = nullptr; + NAPI_CALL(GetEnv(), napi_get_reference_value(GetEnv(), failCallback_, &callback)); + return callback; +} + +napi_value FetchContext::GetCompleteCallback() const +{ + if (completeCallback_ == nullptr) { + return nullptr; + } + napi_value callback = nullptr; + NAPI_CALL(GetEnv(), napi_get_reference_value(GetEnv(), completeCallback_, &callback)); + return callback; +} + +std::string FetchContext::GetResponseType() const +{ + return responseType_; +} +} // namespace OHOS::NetStack \ No newline at end of file diff --git a/frameworks/js/napi/fetch/async_work/include/fetch_async_work.h b/frameworks/js/napi/fetch/async_work/include/fetch_async_work.h new file mode 100644 index 0000000000000000000000000000000000000000..5fb3e11c4d4b08a549bcef1ad1672e6d82df37f2 --- /dev/null +++ b/frameworks/js/napi/fetch/async_work/include/fetch_async_work.h @@ -0,0 +1,35 @@ +/* + * 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_FETCH_ASYNC_WORK_H +#define COMMUNICATIONNETSTACK_FETCH_ASYNC_WORK_H + +#include "napi/native_api.h" +#include "napi/native_common.h" +#include "noncopyable.h" +#include "fetch_context.h" + +namespace OHOS::NetStack { +class FetchAsyncWork final { +public: + ACE_DISALLOW_COPY_AND_MOVE(FetchAsyncWork); + + static void ExecFetch(napi_env env, void *data); + + static void FetchCallback(napi_env env, napi_status status, void *data); +}; +} // namespace OHOS::NetStack + +#endif /* COMMUNICATIONNETSTACK_FETCH_ASYNC_WORK_H */ diff --git a/frameworks/js/napi/fetch/async_work/src/fetch_async_work.cpp b/frameworks/js/napi/fetch/async_work/src/fetch_async_work.cpp new file mode 100644 index 0000000000000000000000000000000000000000..93792f5461d8cc75cd3df291a447099546013263 --- /dev/null +++ b/frameworks/js/napi/fetch/async_work/src/fetch_async_work.cpp @@ -0,0 +1,31 @@ +/* + * 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 + * + * fetch://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 "fetch_async_work.h" + +#include "fetch_exec.h" +#include "netstack_base_async_work.h" + +namespace OHOS::NetStack { +void FetchAsyncWork::ExecFetch(napi_env env, void *data) +{ + BaseAsyncWork::ExecAsyncWork(env, data); +} + +void FetchAsyncWork::FetchCallback(napi_env env, napi_status status, void *data) +{ + BaseAsyncWork::AsyncWorkCallbackForSystem(env, status, data); +} +} // namespace OHOS::NetStack \ No newline at end of file diff --git a/frameworks/js/napi/fetch/constant/include/constant.h b/frameworks/js/napi/fetch/constant/include/constant.h new file mode 100644 index 0000000000000000000000000000000000000000..fbfbdd36faf2b8388d0c76a4034500cc1e45d26d --- /dev/null +++ b/frameworks/js/napi/fetch/constant/include/constant.h @@ -0,0 +1,70 @@ +/* + * 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 + * + * 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 + +#include + +#include "noncopyable.h" + +namespace OHOS::NetStack { +class FetchConstant final { +public: + ACE_DISALLOW_COPY_AND_MOVE(FetchConstant); + + /* Http Method */ + static const char *const HTTP_METHOD_GET; + static const char *const HTTP_METHOD_HEAD; + static const char *const HTTP_METHOD_OPTIONS; + static const char *const HTTP_METHOD_TRACE; + static const char *const HTTP_METHOD_DELETE; + static const char *const HTTP_METHOD_POST; + static const char *const HTTP_METHOD_PUT; + static const char *const HTTP_METHOD_CONNECT; + + /* options key */ + static const char *const PARAM_KEY_URL; + static const char *const PARAM_KEY_METHOD; + static const char *const PARAM_KEY_DATA; + static const char *const PARAM_KEY_HEADER; + static const char *const PARAM_KEY_SUCCESS; + static const char *const PARAM_KEY_FAIL; + static const char *const PARAM_KEY_COMPLETE; + static const char *const PARAM_KEY_RESPONSE_TYPE; + + static const char *const RESPONSE_KEY_DATA; + static const char *const RESPONSE_KEY_CODE; + static const char *const RESPONSE_KEY_HEADERS; + + static const char *const HTTP_URL_PARAM_START; + static const char *const HTTP_URL_PARAM_SEPARATOR; + static const char *const HTTP_URL_NAME_VALUE_SEPARATOR; + static const char *const HTTP_HEADER_SEPARATOR; + static const char *const HTTP_LINE_SEPARATOR; + + static const char *const HTTP_DEFAULT_USER_AGENT; + static const char *const HTTP_DEFAULT_CA_PATH; + + static const char *const HTTP_CONTENT_TYPE; + static const char *const HTTP_CONTENT_TYPE_URL_ENCODE; + static const char *const HTTP_CONTENT_TYPE_JSON; + + static const char *const HTTP_RESPONSE_TYPE_TEXT; + static const char *const HTTP_RESPONSE_TYPE_JSON; +}; +} // namespace OHOS::NetStack + +#endif /* COMMUNICATIONNETSTACK_CONSTANT_H */ diff --git a/frameworks/js/napi/fetch/constant/src/constant.cpp b/frameworks/js/napi/fetch/constant/src/constant.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b15b1cbbe8162ef4e56a7527a8c1159230d74b30 --- /dev/null +++ b/frameworks/js/napi/fetch/constant/src/constant.cpp @@ -0,0 +1,56 @@ +/* + * 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 + * + * 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 *const FetchConstant::HTTP_METHOD_GET = "GET"; +const char *const FetchConstant::HTTP_METHOD_HEAD = "HEAD"; +const char *const FetchConstant::HTTP_METHOD_OPTIONS = "OPTIONS"; +const char *const FetchConstant::HTTP_METHOD_TRACE = "TRACE"; +const char *const FetchConstant::HTTP_METHOD_DELETE = "DELETE"; +const char *const FetchConstant::HTTP_METHOD_POST = "POST"; +const char *const FetchConstant::HTTP_METHOD_PUT = "PUT"; +const char *const FetchConstant::HTTP_METHOD_CONNECT = "CONNECT"; + +const char *const FetchConstant::PARAM_KEY_URL = "url"; +const char *const FetchConstant::PARAM_KEY_METHOD = "method"; +const char *const FetchConstant::PARAM_KEY_DATA = "data"; +const char *const FetchConstant::PARAM_KEY_HEADER = "header"; +const char *const FetchConstant::PARAM_KEY_SUCCESS = "success"; +const char *const FetchConstant::PARAM_KEY_FAIL = "fail"; +const char *const FetchConstant::PARAM_KEY_COMPLETE = "complete"; +const char *const FetchConstant::PARAM_KEY_RESPONSE_TYPE = "responseType"; + +const char *const FetchConstant::RESPONSE_KEY_DATA = "data"; +const char *const FetchConstant::RESPONSE_KEY_CODE = "code"; +const char *const FetchConstant::RESPONSE_KEY_HEADERS = "headers"; + +const char *const FetchConstant::HTTP_URL_PARAM_START = "?"; +const char *const FetchConstant::HTTP_URL_PARAM_SEPARATOR = "&"; +const char *const FetchConstant::HTTP_URL_NAME_VALUE_SEPARATOR = "="; +const char *const FetchConstant::HTTP_HEADER_SEPARATOR = ":"; +const char *const FetchConstant::HTTP_LINE_SEPARATOR = "\r\n"; + +const char *const FetchConstant::HTTP_DEFAULT_USER_AGENT = "libcurl-agent/1.0"; +const char *const FetchConstant::HTTP_DEFAULT_CA_PATH = "/etc/ssl/certs/cacert.pem"; + +const char *const FetchConstant::HTTP_CONTENT_TYPE = "content-type"; +const char *const FetchConstant::HTTP_CONTENT_TYPE_URL_ENCODE = "application/x-www-form-urlencoded"; +const char *const FetchConstant::HTTP_CONTENT_TYPE_JSON = "application/json"; + +const char *const FetchConstant::HTTP_RESPONSE_TYPE_TEXT = "text"; +const char *const FetchConstant::HTTP_RESPONSE_TYPE_JSON = "json"; +} // namespace OHOS::NetStack diff --git a/frameworks/js/napi/fetch/fetch_exec/include/fetch_exec.h b/frameworks/js/napi/fetch/fetch_exec/include/fetch_exec.h new file mode 100644 index 0000000000000000000000000000000000000000..7014539cc0780bd39ee187a8dd661d88c6a281c2 --- /dev/null +++ b/frameworks/js/napi/fetch/fetch_exec/include/fetch_exec.h @@ -0,0 +1,72 @@ +/* + * 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_FETCH_EXEC_H +#define COMMUNICATIONNETSTACK_FETCH_EXEC_H + +#include +#include + +#include "curl/curl.h" +#include "fetch_context.h" +#include "napi/native_api.h" +#include "noncopyable.h" + +namespace OHOS::NetStack { +class FetchExec final { +public: + ACE_DISALLOW_COPY_AND_MOVE(FetchExec); + + FetchExec() = default; + + ~FetchExec() = default; + + static bool ExecFetch(FetchContext *context); + + static napi_value FetchCallback(FetchContext *context); + + static std::string MakeUrl(const std::string &url, std::string param, const std::string &extraParam); + + static bool MethodForGet(const std::string &method); + + static bool MethodForPost(const std::string &method); + + static bool EncodeUrlParam(std::string &str); + + static bool Initialize(); + +private: + static bool SetOption(CURL *curl, FetchContext *context, struct curl_slist *requestHeader); + + static size_t OnWritingMemoryBody(const void *data, size_t size, size_t memBytes, void *userData); + + static size_t OnWritingMemoryHeader(const void *data, size_t size, size_t memBytes, void *userData); + + static struct curl_slist *MakeHeaders(const std::vector &vec); + + static napi_value MakeResponseHeader(FetchContext *context); + + static bool IsUnReserved(unsigned char in); + + static napi_value MakeResponse(FetchContext *context); + +private: + static std::mutex mutex_; + + static bool initialized_; +}; +} // namespace OHOS::NetStack + +#endif /* COMMUNICATIONNETSTACK_FETCH_EXEC_H */ diff --git a/frameworks/js/napi/fetch/fetch_exec/src/fetch_exec.cpp b/frameworks/js/napi/fetch/fetch_exec/src/fetch_exec.cpp new file mode 100644 index 0000000000000000000000000000000000000000..dffc245a7f5ade47369e66a5cf9fb555f00f1fc4 --- /dev/null +++ b/frameworks/js/napi/fetch/fetch_exec/src/fetch_exec.cpp @@ -0,0 +1,347 @@ +/* + * 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 "fetch_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" + +#define NETSTACK_CURL_EASY_SET_OPTION(handle, opt, data, asyncContext) \ + do { \ + CURLcode result = curl_easy_setopt(handle, opt, data); \ + if (result != CURLE_OK) { \ + const char *err = curl_easy_strerror(result); \ + NETSTACK_LOGE("Failed to set option: %{public}s, %{public}s %{public}d", #opt, err, result); \ + (asyncContext)->SetErrorCode(result); \ + return false; \ + } \ + } while (0) + +#define NETSTACK_CURL_EASY_PERFORM(handle, asyncContext) \ + do { \ + CURLcode result = curl_easy_perform(handle); \ + if (result != CURLE_OK) { \ + NETSTACK_LOGE("request fail, url:%{public}s, %{public}s %{public}d", \ + (asyncContext)->request.GetUrl().c_str(), curl_easy_strerror(result), result); \ + (asyncContext)->SetErrorCode(result); \ + return false; \ + } \ + } while (0) + +#define NETSTACK_CURL_EASY_GET_INFO(handle, opt, data, asyncContext) \ + do { \ + CURLcode result = curl_easy_getinfo(handle, opt, data); \ + if (result != CURLE_OK) { \ + const char *err = curl_easy_strerror(result); \ + NETSTACK_LOGE("Failed to get info: %{public}s, %{public}s %{public}d", #opt, err, result); \ + (asyncContext)->SetErrorCode(result); \ + return false; \ + } \ + } while (0) + +static constexpr const int FAIL_CALLBACK_PARAM = 2; + +namespace OHOS::NetStack { +std::mutex FetchExec::mutex_; + +bool FetchExec::initialized_ = false; + +bool FetchExec::ExecFetch(FetchContext *context) +{ + if (!initialized_) { + NETSTACK_LOGE("curl not init"); + return false; + } + + std::unique_ptr handle(curl_easy_init(), curl_easy_cleanup); + + if (!handle) { + NETSTACK_LOGE("Failed to create fetch task"); + return false; + } + + NETSTACK_LOGI("final url: %{public}s", context->request.GetUrl().c_str()); + + std::vector vec; + std::for_each(context->request.GetHeader().begin(), context->request.GetHeader().end(), + [&vec](const std::pair &p) { + vec.emplace_back(p.first + FetchConstant::HTTP_HEADER_SEPARATOR + p.second); + }); + std::unique_ptr header(MakeHeaders(vec), curl_slist_free_all); + + if (!SetOption(handle.get(), context, header.get())) { + NETSTACK_LOGE("set option failed"); + return false; + } + + NETSTACK_CURL_EASY_PERFORM(handle.get(), context); + + int32_t responseCode; + NETSTACK_CURL_EASY_GET_INFO(handle.get(), CURLINFO_RESPONSE_CODE, &responseCode, context); + + context->response.SetResponseCode(responseCode); + context->response.ParseHeaders(); + + return true; +} + +napi_value FetchExec::FetchCallback(FetchContext *context) +{ + if (context->IsExecOK()) { + napi_value success = context->GetSuccessCallback(); + if (NapiUtils::GetValueType(context->GetEnv(), success) == napi_function) { + napi_value response = MakeResponse(context); + napi_value undefined = NapiUtils::GetUndefined(context->GetEnv()); + napi_value argv[1] = {response}; + (void)NapiUtils::CallFunction(context->GetEnv(), undefined, success, 1, argv); + } + } else { + napi_value fail = context->GetFailCallback(); + if (NapiUtils::GetValueType(context->GetEnv(), fail) == napi_function) { + napi_value errData = NapiUtils::GetUndefined(context->GetEnv()); + napi_value errCode = NapiUtils::CreateUint32(context->GetEnv(), context->GetErrorCode()); + napi_value undefined = NapiUtils::GetUndefined(context->GetEnv()); + napi_value argv[FAIL_CALLBACK_PARAM] = {errData, errCode}; + (void)NapiUtils::CallFunction(context->GetEnv(), undefined, fail, FAIL_CALLBACK_PARAM, argv); + } + } + + napi_value complete = context->GetCompleteCallback(); + if (NapiUtils::GetValueType(context->GetEnv(), complete) == napi_function) { + napi_value undefined = NapiUtils::GetUndefined(context->GetEnv()); + (void)NapiUtils::CallFunction(context->GetEnv(), undefined, complete, 0, nullptr); + } + + return NapiUtils::GetUndefined(context->GetEnv()); +} + +napi_value FetchExec::MakeResponse(FetchContext *context) +{ + napi_value object = NapiUtils::CreateObject(context->GetEnv()); + if (NapiUtils::GetValueType(context->GetEnv(), object) != napi_object) { + return nullptr; + } + + NapiUtils::SetUint32Property(context->GetEnv(), object, FetchConstant::RESPONSE_KEY_CODE, + context->response.GetResponseCode()); + + napi_value header = MakeResponseHeader(context); + if (NapiUtils::GetValueType(context->GetEnv(), header) == napi_object) { + NapiUtils::SetNamedProperty(context->GetEnv(), object, FetchConstant::RESPONSE_KEY_HEADERS, header); + } + + if (CommonUtils::ToLower(context->GetResponseType()) == FetchConstant::HTTP_RESPONSE_TYPE_JSON) { + napi_value data = NapiUtils::JsonParse( + context->GetEnv(), NapiUtils::CreateStringUtf8(context->GetEnv(), context->response.GetData())); + NapiUtils::SetNamedProperty(context->GetEnv(), object, FetchConstant::RESPONSE_KEY_DATA, data); + return object; + } + + /* now just support utf8 */ + NapiUtils::SetStringPropertyUtf8(context->GetEnv(), object, FetchConstant::RESPONSE_KEY_DATA, + context->response.GetData()); + return object; +} + +std::string FetchExec::MakeUrl(const std::string &url, std::string param, const std::string &extraParam) +{ + if (param.empty()) { + param += extraParam; + } else { + param += FetchConstant::HTTP_URL_PARAM_SEPARATOR; + param += extraParam; + } + + if (param.empty()) { + return url; + } + + return url + FetchConstant::HTTP_URL_PARAM_START + param; +} + +bool FetchExec::MethodForGet(const std::string &method) +{ + return (method == FetchConstant::HTTP_METHOD_HEAD || method == FetchConstant::HTTP_METHOD_OPTIONS || + method == FetchConstant::HTTP_METHOD_DELETE || method == FetchConstant::HTTP_METHOD_TRACE || + method == FetchConstant::HTTP_METHOD_GET || method == FetchConstant::HTTP_METHOD_CONNECT); +} + +bool FetchExec::MethodForPost(const std::string &method) +{ + return (method == FetchConstant::HTTP_METHOD_POST || method == FetchConstant::HTTP_METHOD_PUT); +} + +bool FetchExec::EncodeUrlParam(std::string &str) +{ + char encoded[4]; + std::string encodeOut; + for (size_t i = 0; i < strlen(str.c_str()); ++i) { + auto c = static_cast(str.c_str()[i]); + if (IsUnReserved(c)) { + encodeOut += static_cast(c); + } else { + if (sprintf_s(encoded, sizeof(encoded), "%%%02X", c) < 0) { + return false; + } + encodeOut += encoded; + } + } + + if (str == encodeOut) { + return false; + } + str = encodeOut; + return true; +} + +bool FetchExec::Initialize() +{ + std::lock_guard lock(mutex_); + if (initialized_) { + return true; + } + NETSTACK_LOGI("call curl_global_init"); + if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) { + NETSTACK_LOGE("Failed to initialize 'curl'"); + return false; + } + initialized_ = true; + return initialized_; +} + +bool FetchExec::SetOption(CURL *curl, FetchContext *context, struct curl_slist *requestHeader) +{ + const std::string &method = context->request.GetMethod(); + if (!MethodForGet(method) && !MethodForPost(method)) { + NETSTACK_LOGE("method %{public}s not supported", method.c_str()); + return false; + } + + NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_URL, context->request.GetUrl().c_str(), context); + NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_CUSTOMREQUEST, method.c_str(), context); + + NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_WRITEFUNCTION, OnWritingMemoryBody, context); + NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_WRITEDATA, context, context); + + NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_HEADERFUNCTION, OnWritingMemoryHeader, context); + NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_HEADERDATA, context, context); + + NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_HTTPHEADER, requestHeader, context); + + // Some servers don't like requests that are made without a user-agent field, so we provide one + NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_USERAGENT, FetchConstant::HTTP_DEFAULT_USER_AGENT, context); + + NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_FOLLOWLOCATION, 1L, context); + + /* first #undef CURL_DISABLE_COOKIES in curl config */ + NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_COOKIEFILE, "", context); + +#if NETSTACK_USE_PROXY + NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_PROXY, NETSTACK_PROXY_URL_PORT, context); + NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_PROXYTYPE, NETSTACK_PROXY_TYPE, context); + NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_HTTPPROXYTUNNEL, 1L, context); +#ifdef NETSTACK_PROXY_PASS + NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_PROXYUSERPWD, NETSTACK_PROXY_PASS, context); +#endif // NETSTACK_PROXY_PASS +#endif // NETSTACK_USE_PROXY + +#if NO_SSL_CERTIFICATION + // in real life, you should buy a ssl certification and rename it to /etc/ssl/cert.pem + NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_VERIFYHOST, 0L, context); + NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_SSL_VERIFYPEER, 0L, context); +#else + NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_CAINFO, FetchConstant::HTTP_DEFAULT_CA_PATH, context); +#endif // NO_SSL_CERTIFICATION + + NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_NOPROGRESS, 1L, context); + NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_NOSIGNAL, 1L, context); +#if HTTP_CURL_PRINT_VERBOSE + NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_VERBOSE, 1L, context); +#endif + + if (MethodForPost(method)) { + NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_POST, 1L, context); + if (!context->request.GetBody().empty()) { + NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_POSTFIELDS, context->request.GetBody().c_str(), context); + NETSTACK_CURL_EASY_SET_OPTION(curl, CURLOPT_POSTFIELDSIZE, context->request.GetBody().size(), context); + } + } + + return true; +} + +size_t FetchExec::OnWritingMemoryBody(const void *data, size_t size, size_t memBytes, void *userData) +{ + auto context = static_cast(userData); + context->response.AppendData(data, size * memBytes); + return size * memBytes; +} + +size_t FetchExec::OnWritingMemoryHeader(const void *data, size_t size, size_t memBytes, void *userData) +{ + auto context = static_cast(userData); + context->response.AppendRawHeader(data, size * memBytes); + return size * memBytes; +} + +struct curl_slist *FetchExec::MakeHeaders(const std::vector &vec) +{ + struct curl_slist *header = nullptr; + std::for_each(vec.begin(), vec.end(), [&header](const std::string &s) { + if (!s.empty()) { + header = curl_slist_append(header, s.c_str()); + } + }); + return header; +} + +napi_value FetchExec::MakeResponseHeader(FetchContext *context) +{ + napi_value header = NapiUtils::CreateObject(context->GetEnv()); + if (NapiUtils::GetValueType(context->GetEnv(), header) == napi_object) { + std::for_each(context->response.GetHeader().begin(), context->response.GetHeader().end(), + [context, header](const std::pair &p) { + if (!p.first.empty() && !p.second.empty()) { + NapiUtils::SetStringPropertyUtf8(context->GetEnv(), header, p.first, p.second); + } + }); + } + return header; +} + +bool FetchExec::IsUnReserved(unsigned char in) +{ + if ((in >= '0' && in <= '9') || (in >= 'a' && in <= 'z') || (in >= 'A' && in <= 'Z')) { + return true; + } + switch (in) { + case '-': + case '.': + case '_': + case '~': + return true; + default: + break; + } + return false; +} +} // namespace OHOS::NetStack \ No newline at end of file diff --git a/frameworks/js/napi/fetch/fetch_module/src/fetch_module.cpp b/frameworks/js/napi/fetch/fetch_module/src/fetch_module.cpp index ee482c8ab44b229066d545bbdccf45ff1eac13fe..14691dbc26140a47aab1d93252ea23a364d497db 100644 --- a/frameworks/js/napi/fetch/fetch_module/src/fetch_module.cpp +++ b/frameworks/js/napi/fetch/fetch_module/src/fetch_module.cpp @@ -15,9 +15,10 @@ #include "fetch_module.h" +#include "fetch_async_work.h" +#include "fetch_exec.h" #include "netstack_log.h" - -static constexpr const char *FETCH_MODULE_NAME = "fetch"; +#include "netstack_module_template.h" namespace OHOS::NetStack { napi_value FetchModule::InitFetchModule(napi_env env, napi_value exports) @@ -33,23 +34,10 @@ napi_value FetchModule::InitFetchModule(napi_env env, napi_value exports) napi_value FetchModule::Fetch(napi_env env, napi_callback_info info) { NETSTACK_LOGI("FetchModule::Fetch is called"); - (void)info; - - return NapiUtils::GetUndefined(env); + return ModuleTemplate::Interface( + env, info, FUNCTION_FETCH, [](napi_env, napi_value, FetchContext *) -> bool { return FetchExec::Initialize(); }, + FetchAsyncWork::ExecFetch, FetchAsyncWork::FetchCallback); } -static napi_module g_fetchModule = { - .nm_version = 1, - .nm_flags = 0, - .nm_filename = nullptr, - .nm_register_func = FetchModule::InitFetchModule, - .nm_modname = FETCH_MODULE_NAME, - .nm_priv = nullptr, - .reserved = {nullptr}, -}; - -extern "C" __attribute__((constructor)) void RegisterFetchModule(void) -{ - napi_module_register(&g_fetchModule); -} +NAPI_MODULE(fetch, FetchModule::InitFetchModule) } // namespace OHOS::NetStack diff --git a/frameworks/js/napi/fetch/options/include/fetch_request.h b/frameworks/js/napi/fetch/options/include/fetch_request.h new file mode 100644 index 0000000000000000000000000000000000000000..b4f3b1185b80f471e5ab0e98a51e279ac32b2570 --- /dev/null +++ b/frameworks/js/napi/fetch/options/include/fetch_request.h @@ -0,0 +1,54 @@ +/* + * 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_FETCH_REQUEST_H +#define COMMUNICATIONNETSTACK_FETCH_REQUEST_H + +#include +#include + +namespace OHOS::NetStack { +class FetchRequest final { +public: + FetchRequest(); + + void SetUrl(const std::string &url); + + void SetMethod(const std::string &method); + + void SetBody(const void *data, size_t length); + + void SetHeader(const std::string &key, const std::string &val); + + [[nodiscard]] const std::string &GetUrl() const; + + [[nodiscard]] const std::string &GetMethod() const; + + [[nodiscard]] const std::string &GetBody() const; + + [[nodiscard]] const std::map &GetHeader() const; + +private: + std::string url_; + + std::string body_; + + std::string method_; + + std::map header_; +}; +} // namespace OHOS::NetStack + +#endif /* COMMUNICATIONNETSTACK_FETCH_REQUEST_H */ diff --git a/frameworks/js/napi/fetch/options/include/fetch_response.h b/frameworks/js/napi/fetch/options/include/fetch_response.h new file mode 100644 index 0000000000000000000000000000000000000000..f0647b89e83efdbed11e3ef53b3756212264429a --- /dev/null +++ b/frameworks/js/napi/fetch/options/include/fetch_response.h @@ -0,0 +1,54 @@ +/* + * 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_FETCH_RESPONSE_H +#define COMMUNICATIONNETSTACK_FETCH_RESPONSE_H + +#include +#include + +#include "napi/native_api.h" + +namespace OHOS::NetStack { +class FetchResponse final { +public: + FetchResponse(); + + void AppendData(const void *data, size_t length); + + void AppendRawHeader(const void *data, size_t length); + + void SetResponseCode(uint32_t responseCode); + + void ParseHeaders(); + + [[nodiscard]] const std::string &GetData() const; + + [[nodiscard]] uint32_t GetResponseCode() const; + + [[nodiscard]] const std::map &GetHeader() const; + +private: + std::string data_; + + std::string rawHeader_; + + uint32_t responseCode_; + + std::map header_; +}; +} // namespace OHOS::NetStack + +#endif /* COMMUNICATIONNETSTACK_FETCH_RESPONSE_H */ diff --git a/frameworks/js/napi/fetch/options/src/fetch_request.cpp b/frameworks/js/napi/fetch/options/src/fetch_request.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cdc04556c7935e745b53f0334d35476556dd3705 --- /dev/null +++ b/frameworks/js/napi/fetch/options/src/fetch_request.cpp @@ -0,0 +1,65 @@ +/* + * 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 "fetch_request.h" + +#include "constant.h" + +namespace OHOS::NetStack { +FetchRequest::FetchRequest() : method_(FetchConstant::HTTP_METHOD_GET) +{ + header_[FetchConstant::HTTP_CONTENT_TYPE] = FetchConstant::HTTP_CONTENT_TYPE_JSON; // default +} + +void FetchRequest::SetUrl(const std::string &url) +{ + url_ = url; +} + +void FetchRequest::SetMethod(const std::string &method) +{ + method_ = method; +} + +void FetchRequest::SetBody(const void *data, size_t length) +{ + body_.append(static_cast(data), length); +} + +void FetchRequest::SetHeader(const std::string &key, const std::string &val) +{ + header_[key] = val; +} + +const std::string &FetchRequest::GetUrl() const +{ + return url_; +} + +const std::string &FetchRequest::GetMethod() const +{ + return method_; +} + +const std::string &FetchRequest::GetBody() const +{ + return body_; +} + +const std::map &FetchRequest::GetHeader() const +{ + return header_; +} +} // namespace OHOS::NetStack \ No newline at end of file diff --git a/frameworks/js/napi/fetch/options/src/fetch_response.cpp b/frameworks/js/napi/fetch/options/src/fetch_response.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2fbd017d2699feb96d625aa1beb1e9204611f4c2 --- /dev/null +++ b/frameworks/js/napi/fetch/options/src/fetch_response.cpp @@ -0,0 +1,72 @@ +/* + * 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 "fetch_response.h" + +#include "constant.h" +#include "netstack_common_utils.h" +#include "netstack_log.h" + +namespace OHOS::NetStack { +FetchResponse::FetchResponse() : responseCode_(0) {} + +void FetchResponse::AppendData(const void *data, size_t length) +{ + data_.append(static_cast(data), length); +} + +void FetchResponse::AppendRawHeader(const void *data, size_t length) +{ + rawHeader_.append(static_cast(data), length); +} + +void FetchResponse::SetResponseCode(uint32_t responseCode) +{ + responseCode_ = responseCode; +} + +void FetchResponse::ParseHeaders() +{ + std::vector vec = CommonUtils::Split(rawHeader_, FetchConstant::HTTP_LINE_SEPARATOR); + for (const auto &header : vec) { + if (CommonUtils::Strip(header).empty()) { + continue; + } + size_t index = header.find(FetchConstant::HTTP_HEADER_SEPARATOR); + if (index == std::string::npos) { + header_[CommonUtils::Strip(header)] = ""; + NETSTACK_LOGI("HEAD: %{public}s", CommonUtils::Strip(header).c_str()); + continue; + } + header_[CommonUtils::ToLower(CommonUtils::Strip(header.substr(0, index)))] = + CommonUtils::Strip(header.substr(index + 1)); + } +} + +const std::string &FetchResponse::GetData() const +{ + return data_; +} + +uint32_t FetchResponse::GetResponseCode() const +{ + return responseCode_; +} + +const std::map &FetchResponse::GetHeader() const +{ + return header_; +} +} // namespace OHOS::NetStack \ No newline at end of file diff --git a/frameworks/js/napi/http/http_exec/include/http_exec.h b/frameworks/js/napi/http/http_exec/include/http_exec.h index caa43078479e4e9006581fb05b5044854a9c0c00..d3288892ddba994cbc5ffff547a01bb9913266e4 100644 --- a/frameworks/js/napi/http/http_exec/include/http_exec.h +++ b/frameworks/js/napi/http/http_exec/include/http_exec.h @@ -45,9 +45,9 @@ public: static bool EncodeUrlParam(std::string &str); -private: static bool Initialize(); +private: static bool SetOption(CURL *curl, RequestContext *context, struct curl_slist *requestHeader); static size_t OnWritingMemoryBody(const void *data, size_t size, size_t memBytes, void *userData); diff --git a/frameworks/js/napi/http/http_exec/src/http_exec.cpp b/frameworks/js/napi/http/http_exec/src/http_exec.cpp index 0f155f2632c7b7f743b96a98abf0d7de7ef7ff14..4129e85f858d2f17c689a65051b3b53b86bdcd33 100644 --- a/frameworks/js/napi/http/http_exec/src/http_exec.cpp +++ b/frameworks/js/napi/http/http_exec/src/http_exec.cpp @@ -66,7 +66,8 @@ bool HttpExec::initialized_ = false; bool HttpExec::ExecRequest(RequestContext *context) { - if (!Initialize()) { + if (!initialized_) { + NETSTACK_LOGE("curl not init"); return false; } diff --git a/frameworks/js/napi/http/http_module/src/http_module.cpp b/frameworks/js/napi/http/http_module/src/http_module.cpp index 1b9edd144b7e87fa25719f08e5f9f6ba2de948dd..5999ada08c8422b63f85b58a9356f1b70edbb8b0 100644 --- a/frameworks/js/napi/http/http_module/src/http_module.cpp +++ b/frameworks/js/napi/http/http_module/src/http_module.cpp @@ -18,6 +18,7 @@ #include "constant.h" #include "event_list.h" #include "http_async_work.h" +#include "http_exec.h" #include "netstack_log.h" #include "netstack_module_template.h" @@ -135,8 +136,10 @@ void HttpModuleExports::InitResponseCode(napi_env env, napi_value exports) napi_value HttpModuleExports::HttpRequest::Request(napi_env env, napi_callback_info info) { - return ModuleTemplate::Interface(env, info, REQUEST_ASYNC_WORK_NAME, nullptr, - HttpAsyncWork::ExecRequest, HttpAsyncWork::RequestCallback); + return ModuleTemplate::Interface( + env, info, REQUEST_ASYNC_WORK_NAME, + [](napi_env, napi_value, RequestContext *) -> bool { return HttpExec::Initialize(); }, + HttpAsyncWork::ExecRequest, HttpAsyncWork::RequestCallback); } napi_value HttpModuleExports::HttpRequest::Destroy(napi_env env, napi_callback_info info) diff --git a/frameworks/js/napi/http/options/src/http_response.cpp b/frameworks/js/napi/http/options/src/http_response.cpp index 74596bd321a91f78a6addba04dd690ea02ba6a7e..4a2fb17285b72a26f75f2618964683901bfae13a 100644 --- a/frameworks/js/napi/http/options/src/http_response.cpp +++ b/frameworks/js/napi/http/options/src/http_response.cpp @@ -17,6 +17,7 @@ #include "constant.h" #include "netstack_common_utils.h" +#include "netstack_log.h" namespace OHOS::NetStack { HttpResponse::HttpResponse() : responseCode_(0) {} @@ -46,6 +47,7 @@ void HttpResponse::ParseHeaders() size_t index = header.find(HttpConstant::HTTP_HEADER_SEPARATOR); if (index == std::string::npos) { header_[CommonUtils::Strip(header)] = ""; + NETSTACK_LOGI("HEAD: %{public}s", CommonUtils::Strip(header).c_str()); continue; } header_[CommonUtils::ToLower(CommonUtils::Strip(header.substr(0, index)))] = diff --git a/test/napi/fetch/CMakeLists.txt b/test/napi/fetch/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..2220deeff680a32d5a7e2570f293291a8a5d5b50 --- /dev/null +++ b/test/napi/fetch/CMakeLists.txt @@ -0,0 +1,60 @@ +# 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 +# +# 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/fetch/fetch_module/include) +include_directories(../../../frameworks/js/napi/fetch/async_work/include) +include_directories(../../../frameworks/js/napi/fetch/fetch_exec/include) +include_directories(../../../frameworks/js/napi/fetch/constant/include) +include_directories(../../../frameworks/js/napi/fetch/async_context/include) +include_directories(../../../frameworks/js/napi/fetch/options/include) + +include_directories(../../../../../../third_party/curl/include) + +link_directories(../../../../../../third_party/curl/cmake-build-debug/lib) + +add_executable( + test_napi_fetch_exec + test_napi_exec.cpp + ../log/test_hilog.cpp + + ../../../frameworks/js/napi/fetch/fetch_module/src/fetch_module.cpp + ../../../frameworks/js/napi/fetch/constant/src/constant.cpp + ../../../frameworks/js/napi/fetch/async_context/src/fetch_context.cpp + ../../../frameworks/js/napi/fetch/async_work/src/fetch_async_work.cpp + ../../../frameworks/js/napi/fetch/options/src/fetch_response.cpp + ../../../frameworks/js/napi/fetch/options/src/fetch_request.cpp + ../../../frameworks/js/napi/fetch/fetch_exec/src/fetch_exec.cpp +) + +target_link_libraries(test_napi_fetch_exec curl-d) +target_link_libraries(test_napi_fetch_exec ace_napi_quickjs) +target_link_libraries(test_napi_fetch_exec quickjs) +target_link_libraries(test_napi_fetch_exec uv) +target_link_libraries(test_napi_fetch_exec securec) +target_link_libraries(test_napi_fetch_exec gtestd) +target_link_libraries(test_napi_fetch_exec gmockd) +target_link_libraries(test_napi_fetch_exec gtest_maind) +target_link_libraries(test_napi_fetch_exec gmock_maind) +target_link_libraries(test_napi_fetch_exec pthread) +target_link_libraries(test_napi_fetch_exec dl) +target_link_libraries(test_napi_fetch_exec nghttp2) +target_link_libraries(test_napi_fetch_exec netstack_utils) + +add_compile_definitions(NO_SSL_CERTIFICATION=0) +add_compile_definitions(HTTP_CURL_PRINT_VERBOSE=0) +add_compile_definitions(NETSTACK_USE_PROXY=0) +add_compile_definitions(NETSTACK_PROXY_URL_PORT="xxxx:xxxx") +add_compile_definitions(NETSTACK_PROXY_TYPE=CURLPROXY_HTTP) +add_compile_definitions(NETSTACK_PROXY_PASS="xxxx:xxxx") +add_compile_definitions(SERVER_IP="x.x.x.x") +set(CMAKE_CXX_FLAGS -g) diff --git a/test/napi/fetch/test_napi_exec.cpp b/test/napi/fetch/test_napi_exec.cpp new file mode 100644 index 0000000000000000000000000000000000000000..aa10ccd10307f24cca007d154d86795191ae0fdc --- /dev/null +++ b/test/napi/fetch/test_napi_exec.cpp @@ -0,0 +1,18 @@ +/* + * 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" + +int main() {} \ No newline at end of file diff --git a/utils/base_async_work/include/netstack_base_async_work.h b/utils/base_async_work/include/netstack_base_async_work.h index c68f20cffdbbc86aed197e1c5b888a2ed9125e0d..6a63f702f6e1be7f6bd123c57378f6fcc7d14921 100644 --- a/utils/base_async_work/include/netstack_base_async_work.h +++ b/utils/base_async_work/include/netstack_base_async_work.h @@ -96,8 +96,25 @@ public: } napi_value func = context->GetCallback(); - napi_value undefined = NapiUtils::GetUndefined(env); - (void)NapiUtils::CallFunction(env, undefined, func, argc, argv); + if (NapiUtils::GetValueType(env, func) == napi_function) { + napi_value undefined = NapiUtils::GetUndefined(env); + (void)NapiUtils::CallFunction(env, undefined, func, argc, argv); + } + } + + template + static void AsyncWorkCallbackForSystem(napi_env env, napi_status status, void *data) + { + static_assert(std::is_base_of::value); + + if (status != napi_ok) { + return; + } + auto deleter = [](Context *context) { delete context; }; + std::unique_ptr context(static_cast(data), deleter); + if (Callback != nullptr) { + (void)Callback(context.get()); + } } }; } // namespace OHOS::NetStack diff --git a/utils/base_context/include/netstack_base_context.h b/utils/base_context/include/netstack_base_context.h index 433116f72e0e5837a2505d89bdb240fe577869e5..84f11eb87c8b68359b2593857ea20cdbe72077c7 100644 --- a/utils/base_context/include/netstack_base_context.h +++ b/utils/base_context/include/netstack_base_context.h @@ -70,6 +70,10 @@ public: void Emit(const std::string &type, const std::pair &argv); + void SetNeedPromise(bool needPromise); + + [[nodiscard]] bool IsNeedPromise() const; + protected: EventManager *manager_; @@ -89,6 +93,8 @@ private: napi_deferred deferred_; std::string asyncWorkName_; + + bool needPromise_; }; } // namespace OHOS::NetStack diff --git a/utils/base_context/src/netstack_base_context.cpp b/utils/base_context/src/netstack_base_context.cpp index bb1089e8cb69ce1e4c3513144057571e2a280203..b5f999e978e82407d7f3dc1aced2b331a9d1cfa3 100644 --- a/utils/base_context/src/netstack_base_context.cpp +++ b/utils/base_context/src/netstack_base_context.cpp @@ -26,7 +26,8 @@ BaseContext::BaseContext(napi_env env, EventManager *manager) errorCode_(0), callback_(nullptr), asyncWork_(nullptr), - deferred_(nullptr) + deferred_(nullptr), + needPromise_(true) { } @@ -140,4 +141,14 @@ void BaseContext::Emit(const std::string &type, const std::pairEmit(type, argv); } } + +void BaseContext::SetNeedPromise(bool needPromise) +{ + needPromise_ = needPromise; +} + +bool BaseContext::IsNeedPromise() const +{ + return needPromise_; +} } // namespace OHOS::NetStack \ No newline at end of file diff --git a/utils/module_template/include/netstack_module_template.h b/utils/module_template/include/netstack_module_template.h index 2a335c62cf55883bbb0afb043e6decf2fc0e0711..e101508ababdc191443cffd1da1f528e2816f58c 100644 --- a/utils/module_template/include/netstack_module_template.h +++ b/utils/module_template/include/netstack_module_template.h @@ -82,7 +82,7 @@ napi_value } } - if (NapiUtils::GetValueType(env, context->GetCallback()) != napi_function) { + if (NapiUtils::GetValueType(env, context->GetCallback()) != napi_function && context->IsNeedPromise()) { 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 ac671102d2ce329cb5667f723c092934d68a0f2b..d8a2beb1c2376c3f910288d4b82785b4c03a2ba1 100644 --- a/utils/napi_utils/include/netstack_napi_utils.h +++ b/utils/napi_utils/include/netstack_napi_utils.h @@ -95,6 +95,11 @@ void SetBooleanProperty(napi_env env, napi_value object, const std::string &name void DefineProperties(napi_env env, napi_value object, const std::initializer_list &properties); + +/* JSON */ +napi_value JsonStringify(napi_env env, napi_value object); + +napi_value JsonParse(napi_env env, napi_value str); } // 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 0642618f89a541571e79c1fbb6ebe1cea09147bc..cd25ccba44d1bd101ee379106eb12b158d530e4a 100644 --- a/utils/napi_utils/src/netstack_napi_utils.cpp +++ b/utils/napi_utils/src/netstack_napi_utils.cpp @@ -24,6 +24,12 @@ namespace OHOS::NetStack::NapiUtils { static constexpr const int MAX_STRING_LENGTH = 65536; +static constexpr const char *GLOBAL_JSON = "JSON"; + +static constexpr const char *GLOBAL_JSON_STRINGIFY = "stringify"; + +static constexpr const char *GLOBAL_JSON_PARSE = "parse"; + napi_valuetype GetValueType(napi_env env, napi_value value) { if (value == nullptr) { @@ -302,4 +308,53 @@ void DefineProperties(napi_env env, (void)napi_define_properties(env, object, properties.size(), descriptors); } + +/* JSON */ +napi_value JsonStringify(napi_env env, napi_value object) +{ + napi_value undefined = GetUndefined(env); + + if (GetValueType(env, object) != napi_object) { + return undefined; + } + + napi_value global = nullptr; + NAPI_CALL_BASE(env, napi_get_global(env, &global), undefined); + napi_value json = nullptr; + NAPI_CALL_BASE(env, napi_get_named_property(env, global, GLOBAL_JSON, &json), undefined); + napi_value stringify = nullptr; + NAPI_CALL_BASE(env, napi_get_named_property(env, json, GLOBAL_JSON_STRINGIFY, &stringify), undefined); + if (GetValueType(env, stringify) != napi_function) { + return undefined; + } + + napi_value res = nullptr; + napi_value argv[1] = {object}; + NAPI_CALL_BASE(env, napi_call_function(env, json, stringify, 1, argv, &res), undefined); + return res; +} + +napi_value JsonParse(napi_env env, napi_value str) +{ + napi_value undefined = GetUndefined(env); + + if (GetValueType(env, str) != napi_string) { + return undefined; + } + + napi_value global = nullptr; + NAPI_CALL_BASE(env, napi_get_global(env, &global), undefined); + napi_value json = nullptr; + NAPI_CALL_BASE(env, napi_get_named_property(env, global, GLOBAL_JSON, &json), undefined); + napi_value parse = nullptr; + NAPI_CALL_BASE(env, napi_get_named_property(env, json, GLOBAL_JSON_PARSE, &parse), undefined); + if (GetValueType(env, parse) != napi_function) { + return undefined; + } + + napi_value res = nullptr; + napi_value argv[1] = {str}; + NAPI_CALL_BASE(env, napi_call_function(env, json, parse, 1, argv, &res), undefined); + return res; +} } // namespace OHOS::NetStack::NapiUtils \ No newline at end of file