* Copyright (C) 2024 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 "preload_module.h"
#include <dlfcn.h>
#include <unistd.h>
#include <cstdint>
#include <cstring>
#include <limits>
#include <memory>
#include <optional>
#include "access_token.h"
#include "accesstoken_kit.h"
#include "base/request/request/common/include/constant.h"
#include "base/request/request/common/include/log.h"
#include "ipc_skeleton.h"
#include "js_native_api.h"
#include "js_native_api_types.h"
#include "napi/native_common.h"
#include "napi/native_node_api.h"
#include "napi_utils.h"
#include "preload_common.h"
#include "preload_napi.h"
#include "request_preload.h"
namespace OHOS::Request {
using namespace Security::AccessToken;
constexpr const size_t MAX_UTL_LENGTH = 8192;
constexpr int64_t MAX_MEM_SIZE = 1073741824;
constexpr int64_t MAX_FILE_SIZE = 4294967296;
constexpr int64_t MAX_INFO_LIST_SIZE = 8192;
const std::string INTERNET_PERMISSION = "ohos.permission.INTERNET";
const std::string GET_NETWORK_INFO_PERMISSION = "ohos.permission.GET_NETWORK_INFO";
constexpr int64_t DEFAULT_MAX_RETRY_COUNT = 1;
constexpr int64_t MIN_RETRY_COUNT = 0;
constexpr int64_t MAX_RETRY_COUNT = 10;
constexpr int64_t DEFAULT_NETWORK_CHECK_TIMEOUT = 20;
constexpr int64_t MIN_NETWORK_CHECK_TIMEOUT = 0;
constexpr int64_t MAX_NETWORK_CHECK_TIMEOUT = 20;
constexpr int64_t DEFAULT_HTTP_TOTAL_TIMEOUT = 60;
constexpr int64_t MIN_HTTP_TOTAL_TIMEOUT = 1;
constexpr int64_t MAX_HTTP_TOTAL_TIMEOUT = static_cast<int64_t>(std::numeric_limits<uint32_t>::max() / 1000);
struct Range {
int64_t minVal;
int64_t maxVal;
};
bool ParseNumberField(napi_env env, napi_value obj, const char *fieldName, Range range, int32_t &outValue)
{
napi_value field = GetNamedProperty(env, obj, fieldName);
if (IsValueMissingOrSkipped(env, field)) {
return true;
}
if (GetValueType(env, field) != napi_number) {
return false;
}
int64_t value = GetValueNum(env, field);
if (value < range.minVal || value > range.maxVal) {
return false;
}
outValue = static_cast<int32_t>(value);
return true;
}
napi_status CallbackInfo::RegisterCallback(napi_value cb)
{
std::lock_guard<std::recursive_mutex> lock(allCbMutex_);
for (auto &[isActive, ref] : allCb_) {
napi_value existingCallback = nullptr;
napi_get_reference_value(env_, ref, &existingCallback);
bool areCallbacksEqual = false;
napi_strict_equals(env_, cb, existingCallback, &areCallbacksEqual);
if (!areCallbacksEqual) {
continue;
}
if (!isActive) {
isActive = true;
--toDeleteCount_;
++validCbNum;
}
return napi_ok;
}
napi_ref ref;
napi_status status = napi_create_reference(env_, cb, 1, &ref);
if (status != napi_ok) {
REQUEST_HILOGE("RegisterCallback status not ok, reason: %{public}d", status);
return status;
}
allCb_.push_back(std::make_pair(true, ref));
++validCbNum;
return napi_ok;
}
napi_status CallbackInfo::RemoveCallback(napi_value cb)
{
std::lock_guard<std::recursive_mutex> lock(allCbMutex_);
if (validCbNum == 0) {
return napi_ok;
}
if (cb == nullptr) {
if (!isInvoked_) {
for (const auto &callback : allCb_) {
napi_delete_reference(env_, callback.second);
}
allCb_.clear();
} else {
toDeleteCount_ = allCb_.size();
for (auto &callback : allCb_) {
callback.first = false;
}
validCbNum = 0;
}
return napi_ok;
}
auto it = std::find_if(allCb_.begin(), allCb_.end(), [this, cb](const auto &callback) {
napi_value referenceValue = nullptr;
napi_get_reference_value(env_, callback.second, &referenceValue);
bool isEqual = false;
napi_strict_equals(env_, cb, referenceValue, &isEqual);
return isEqual;
});
if (it != allCb_.end()) {
if (!isInvoked_) {
napi_delete_reference(env_, it->second);
allCb_.erase(it);
} else if (it->first) {
it->first = false;
++toDeleteCount_;
--validCbNum;
}
}
return napi_ok;
}
void CallbackInfo::CleanupCallbacks()
{
if (toDeleteCount_ == 0) {
return;
}
auto newEnd = std::remove_if(allCb_.begin(), allCb_.end(), [this](const std::pair<bool, napi_ref> &cbPair) -> bool {
if (!cbPair.first) {
napi_delete_reference(env_, cbPair.second);
return true;
}
return false;
});
size_t deletedCount = static_cast<size_t>(std::distance(newEnd, allCb_.end()));
allCb_.erase(newEnd, allCb_.end());
toDeleteCount_ = (toDeleteCount_ > deletedCount) ? (toDeleteCount_ - deletedCount) : 0;
}
void CallbackInfo::InvokeSuccessCallbacks(napi_value values[])
{
std::lock_guard<std::recursive_mutex> lock(allCbMutex_);
isInvoked_ = true;
napi_handle_scope scope = nullptr;
napi_status status = napi_open_handle_scope(env_, &scope);
if (status != napi_ok || scope == nullptr) {
REQUEST_HILOGE("InvokeSuccessCallbacks napi_scope failed, reason: %{public}d", status);
return;
}
uint32_t paramNumber = 1;
for (auto it = allCb_.begin(); it != allCb_.end();) {
if (it->first == false) {
napi_delete_reference(env_, it->second);
it = allCb_.erase(it);
--toDeleteCount_;
continue;
}
napi_value callbackFunc = nullptr;
napi_get_reference_value(env_, it->second, &callbackFunc);
napi_value callbackResult = nullptr;
napi_call_function(env_, nullptr, callbackFunc, paramNumber, values, &callbackResult);
it++;
}
CleanupCallbacks();
isInvoked_ = false;
REQUEST_HILOGD("Successfully invoked success callback");
napi_close_handle_scope(env_, scope);
}
void CallbackInfo::InvokeErrorCallbacks(napi_value values[])
{
std::lock_guard<std::recursive_mutex> lock(allCbMutex_);
isInvoked_ = true;
napi_handle_scope scope = nullptr;
napi_status status = napi_open_handle_scope(env_, &scope);
if (status != napi_ok || scope == nullptr) {
REQUEST_HILOGE("InvokeErrorCallbacks napi_scope failed, reason: %{public}d", status);
return;
}
uint32_t paramNumber = 1;
for (auto it = allCb_.begin(); it != allCb_.end();) {
if (it->first == false) {
napi_delete_reference(env_, it->second);
it = allCb_.erase(it);
--toDeleteCount_;
continue;
}
napi_value callbackFunc = nullptr;
napi_get_reference_value(env_, it->second, &callbackFunc);
napi_value callbackResult = nullptr;
napi_call_function(env_, nullptr, callbackFunc, paramNumber, values, &callbackResult);
it++;
}
CleanupCallbacks();
isInvoked_ = false;
REQUEST_HILOGD("Successfully invoked error callbacks");
napi_close_handle_scope(env_, scope);
}
CallbackManager &CallbackManager::GetInstance()
{
static CallbackManager instance;
return instance;
}
napi_status CallbackManager::RegisterCallback(const std::string &url, CallbackType type, napi_env env, napi_value cb)
{
REQUEST_HILOGD("RegisterCallback start. Type: %s", (type == CallbackType::SUCCESS) ? "success" : "error");
if (cb == nullptr) {
REQUEST_HILOGE("RegisterCallback no cb");
return napi_ok;
}
mgrMutex_.lock();
auto &targetMap = (type == CallbackType::SUCCESS) ? successCallbacks_ : errorCallbacks_;
auto it = targetMap.find(url);
if (it == targetMap.end()) {
REQUEST_HILOGD("RegisterCallback create new CallbackInfo");
targetMap[url] = std::make_shared<CallbackInfo>(env, url);
}
std::shared_ptr<CallbackInfo> info = targetMap[url];
mgrMutex_.unlock();
return info->RegisterCallback(cb);
}
napi_status CallbackManager::RemoveCallback(const std::string &url, CallbackType type, napi_env env, napi_value cb)
{
mgrMutex_.lock();
auto &targetMap = (type == CallbackType::SUCCESS) ? successCallbacks_ : errorCallbacks_;
REQUEST_HILOGD("RemoveCallback start. Type: %s", (type == CallbackType::SUCCESS) ? "success" : "error");
auto it = targetMap.find(url);
if (it == targetMap.end()) {
targetMap[url] = std::make_shared<CallbackInfo>(env, url);
}
std::shared_ptr<CallbackInfo> info = targetMap[url];
mgrMutex_.unlock();
return info->RemoveCallback(cb);
}
void CallbackManager::InvokeSuccessCallbacks(
const std::string &url, std::shared_ptr<Data> data, napi_env env, const std::string &taskId)
{
REQUEST_HILOGD("Invoking success callbacks for URL");
mgrMutex_.lock();
auto it = successCallbacks_.find(url);
if (it == successCallbacks_.end()) {
successCallbacks_[url] = std::make_shared<CallbackInfo>(env, url);
}
std::shared_ptr<CallbackInfo> info = successCallbacks_[url];
mgrMutex_.unlock();
int32_t ret = napi_send_event(
info->env_,
[url, info]() {
napi_value values[2] = { nullptr };
info->InvokeSuccessCallbacks(values);
},
napi_eprio_high, "request:cachedownload.download");
if (ret != napi_ok) {
REQUEST_HILOGE("napi_send_event failed: %{public}d", ret);
}
}
void CallbackManager::InvokeErrorCallbacks(
const std::string &url, std::shared_ptr<DownloadError> error, napi_env env, const std::string &taskId)
{
REQUEST_HILOGD("Invoking error callbacks for URL");
mgrMutex_.lock();
auto it = errorCallbacks_.find(url);
if (it == errorCallbacks_.end()) {
errorCallbacks_[url] = std::make_shared<CallbackInfo>(env, url);
}
std::shared_ptr<CallbackInfo> info = errorCallbacks_[url];
mgrMutex_.unlock();
int32_t ret = napi_send_event(
info->env_,
[url, info, error]() {
napi_value values[2] = { nullptr };
napi_value value = nullptr;
napi_create_object(info->env_, &value);
napi_set_named_property(
info->env_, value, "errorCode", Convert2JSValue(info->env_, static_cast<uint32_t>(error->errorCode)));
napi_set_named_property(info->env_, value, "message", Convert2JSValue(info->env_, error->message));
values[0] = value;
info->InvokeErrorCallbacks(values);
},
napi_eprio_high, "request:cachedownload.download");
if (ret != napi_ok) {
REQUEST_HILOGE("napi_send_event failed: %{public}d", ret);
}
}
bool CheckInternetPermission()
{
static bool hasPermission = []() {
uint64_t tokenId = IPCSkeleton::GetCallingFullTokenID();
TypeATokenTypeEnum tokenType = AccessTokenKit::GetTokenTypeFlag(static_cast<AccessTokenID>(tokenId));
if (tokenType == TOKEN_INVALID) {
return false;
}
int result = AccessTokenKit::VerifyAccessToken(tokenId, INTERNET_PERMISSION);
return result == PERMISSION_GRANTED;
}();
return hasPermission;
}
bool CheckNetworkInfoPermission()
{
static bool hasPermission = []() {
uint64_t tokenId = IPCSkeleton::GetCallingFullTokenID();
TypeATokenTypeEnum tokenType = AccessTokenKit::GetTokenTypeFlag(static_cast<AccessTokenID>(tokenId));
if (tokenType == TOKEN_INVALID) {
return false;
}
int result = AccessTokenKit::VerifyAccessToken(tokenId, GET_NETWORK_INFO_PERMISSION);
return result == PERMISSION_GRANTED;
}();
return hasPermission;
}
PreloadCallback CreatePreloadCallback(napi_env env, const std::string url)
{
auto &cbManager = CallbackManager::GetInstance();
return PreloadCallback{
.OnSuccess =
[env, url, &cbManager](const std::shared_ptr<Data> &&data, const std::string &taskId) {
REQUEST_HILOGD("OnSuccess called with url");
cbManager.InvokeSuccessCallbacks(url, data, env, taskId);
},
.OnFail =
[env, url, &cbManager](const PreloadError &error, const std::string &taskId) {
REQUEST_HILOGD("OnFail called with url");
ErrorCode errorCode = ErrorCode::OTHERS;
ErrorKind kind = error.GetErrorKind();
switch (kind) {
case ErrorKind::DNS:
errorCode = ErrorCode::DNS;
break;
case ErrorKind::TCP:
errorCode = ErrorCode::TCP;
break;
case ErrorKind::SSL:
errorCode = ErrorCode::SSL;
break;
case ErrorKind::HTTP:
errorCode = ErrorCode::HTTP;
break;
case ErrorKind::IO:
case ErrorKind::OTHERS:
default:
errorCode = ErrorCode::OTHERS;
break;
}
auto downloadError = std::make_shared<DownloadError>(errorCode, error.GetMessage());
cbManager.InvokeErrorCallbacks(url, downloadError, env, taskId);
},
};
}
napi_value download(napi_env env, napi_callback_info info)
{
if (!CheckInternetPermission()) {
ThrowError(env, E_PERMISSION, "internet permission denied");
REQUEST_HILOGI("internet permission denied");
return nullptr;
}
size_t argc = 2;
napi_value args[2] = { nullptr };
PRELOAD_NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr), "napi_get_cb_info failed");
if (GetValueType(env, args[0]) != napi_string || GetValueType(env, args[1]) != napi_object) {
ThrowError(env, E_PARAMETER_CHECK, "parameter error");
return nullptr;
}
size_t urlLength = GetStringLength(env, args[0]);
if (urlLength > MAX_UTL_LENGTH) {
ThrowError(env, E_PARAMETER_CHECK, "url exceeds the maximum length");
return nullptr;
}
std::string url = GetValueString(env, args[0], urlLength);
std::unique_ptr<PreloadOptions> options = std::make_unique<PreloadOptions>();
SetOptionsHeaders(env, args[1], options);
SetOptionsSslType(env, args[1], options);
napi_value napiCaPath = GetNamedProperty(env, args[1], "caPath");
if (napiCaPath != nullptr) {
std::string caPath = GetStringValueWithDefault(env, napiCaPath);
options->caPath = caPath;
}
bool isUpdate = true;
GetCacheStrategy(env, args[1], isUpdate);
if (!SetOptionsRetry(env, args[1], options)) {
ThrowError(env, E_PARAMETER_CHECK, "maxRetryCount out of range");
return nullptr;
}
if (!SetOptionsTimeout(env, args[1], options)) {
ThrowError(env, E_PARAMETER_CHECK, "timeout parameter out of range");
return nullptr;
}
auto jsCallback = CreatePreloadCallback(env, url);
Preload::GetInstance()->load(url, std::make_unique<PreloadCallback>(jsCallback), std::move(options), isUpdate);
return nullptr;
}
napi_value cancel(napi_env env, napi_callback_info info)
{
size_t argc = 1;
napi_value args[1] = { nullptr };
PRELOAD_NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr), "napi_get_cb_info failed");
if (GetValueType(env, args[0]) != napi_string) {
ThrowError(env, E_PARAMETER_CHECK, "parameter error");
return nullptr;
}
size_t urlLength = GetStringLength(env, args[0]);
if (urlLength > MAX_UTL_LENGTH) {
ThrowError(env, E_PARAMETER_CHECK, "url exceeds the maximum length");
return nullptr;
}
std::string url = GetValueString(env, args[0], urlLength);
Preload::GetInstance()->Cancel(url);
return nullptr;
}
napi_value setMemoryCacheSize(napi_env env, napi_callback_info info)
{
size_t argc = 1;
napi_value args[1] = { nullptr };
PRELOAD_NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr), "napi_get_cb_info failed");
if (GetValueType(env, args[0]) != napi_number) {
ThrowError(env, E_PARAMETER_CHECK, "parameter error");
return nullptr;
}
int64_t size = GetValueNum(env, args[0]);
if (size > MAX_MEM_SIZE) {
ThrowError(env, E_PARAMETER_CHECK, "memory cache size exceeds the maximum value");
return nullptr;
}
Preload::GetInstance()->SetRamCacheSize(size);
return nullptr;
}
napi_value setFileCacheSize(napi_env env, napi_callback_info info)
{
size_t argc = 1;
napi_value args[1] = { nullptr };
PRELOAD_NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr), "napi_get_cb_info failed");
if (GetValueType(env, args[0]) != napi_number) {
ThrowError(env, E_PARAMETER_CHECK, "parameter error");
return nullptr;
}
int64_t size = GetValueNum(env, args[0]);
if (size > MAX_FILE_SIZE) {
ThrowError(env, E_PARAMETER_CHECK, "file cache size exceeds the maximum value");
return nullptr;
}
Preload::GetInstance()->SetFileCacheSize(size);
return nullptr;
}
napi_value setDownloadInfoListSize(napi_env env, napi_callback_info info)
{
size_t argc = 1;
napi_value args[1] = { nullptr };
PRELOAD_NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr), "napi_get_cb_info failed");
if (GetValueType(env, args[0]) != napi_number) {
ThrowError(env, E_PARAMETER_CHECK, "parameter error");
return nullptr;
}
int64_t size = GetValueNum(env, args[0]);
if (size > MAX_INFO_LIST_SIZE) {
ThrowError(env, E_PARAMETER_CHECK, "info list size exceeds the maximum value");
return nullptr;
}
if (size < 0) {
ThrowError(env, E_PARAMETER_CHECK, "info list size is negative");
return nullptr;
}
Preload::GetInstance()->SetDownloadInfoListSize(size);
return nullptr;
}
napi_value getDownloadInfo(napi_env env, napi_callback_info info)
{
if (!CheckNetworkInfoPermission()) {
ThrowError(env, E_PERMISSION, "GET_NETWORK_INFO permission denied");
REQUEST_HILOGI("GET_NETWORK_INFO permission denied");
return nullptr;
}
size_t argc = 1;
napi_value args[1] = { nullptr };
PRELOAD_NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr), "napi_get_cb_info failed");
if (GetValueType(env, args[0]) != napi_string) {
ThrowError(env, E_PARAMETER_CHECK, "parameter error");
return nullptr;
}
size_t urlLength = GetStringLength(env, args[0]);
if (urlLength > MAX_UTL_LENGTH) {
ThrowError(env, E_PARAMETER_CHECK, "url exceeds the maximum length");
return nullptr;
}
std::string url = GetValueString(env, args[0], urlLength);
std::optional<CppDownloadInfo> result = Preload::GetInstance()->GetDownloadInfo(url);
if (!result) {
napi_value undefined;
napi_get_undefined(env, &undefined);
return undefined;
}
return BuildDownloadInfo(env, result.value());
}
napi_value clearMemoryCache(napi_env env, napi_callback_info info)
{
Preload::GetInstance()->ClearMemoryCache();
return nullptr;
}
napi_value clearFileCache(napi_env env, napi_callback_info info)
{
Preload::GetInstance()->ClearFileCache();
return nullptr;
}
napi_value onDownloadSuccess(napi_env env, napi_callback_info info)
{
size_t argc = TWO_ARG;
napi_value args[TWO_ARG] = { nullptr };
napi_status status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
if (status != napi_ok) {
ThrowError(env, E_PARAMETER_CHECK, "parameter obtain error");
return nullptr;
}
if (argc < TWO_ARG) {
ThrowError(env, E_PARAMETER_CHECK, "missing mandatory parameters, wrong number of arguments");
return nullptr;
}
if (GetValueType(env, args[0]) != napi_string) {
ThrowError(env, E_PARAMETER_CHECK, "parameter url type error");
return nullptr;
}
size_t urlLength = GetStringLength(env, args[0]);
if (urlLength == 0 || urlLength > MAX_UTL_LENGTH) {
ThrowError(env, E_PARAMETER_CHECK, urlLength == 0 ? "url is empty" : "url exceeds the maximum length");
return nullptr;
}
std::string url = GetValueString(env, args[0], urlLength);
if (GetValueType(env, args[1]) != napi_function) {
ThrowError(env, E_PARAMETER_CHECK, "parameter callback type error");
return nullptr;
}
CallbackManager::GetInstance().RegisterCallback(url, CallbackType::SUCCESS, env, args[1]);
REQUEST_HILOGD("Success callback registered for URL");
return nullptr;
}
napi_value onDownloadError(napi_env env, napi_callback_info info)
{
size_t argc = TWO_ARG;
napi_value args[TWO_ARG] = { nullptr };
napi_status status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
if (status != napi_ok) {
ThrowError(env, E_PARAMETER_CHECK, "parameter obtain error");
return nullptr;
}
if (argc < TWO_ARG) {
ThrowError(env, E_PARAMETER_CHECK, "missing mandatory parameters, wrong number of arguments");
return nullptr;
}
if (GetValueType(env, args[0]) != napi_string) {
ThrowError(env, E_PARAMETER_CHECK, "parameter url type error");
return nullptr;
}
size_t urlLength = GetStringLength(env, args[0]);
if (urlLength == 0 || urlLength > MAX_UTL_LENGTH) {
ThrowError(env, E_PARAMETER_CHECK, urlLength == 0 ? "url is empty" : "url exceeds the maximum length");
return nullptr;
}
std::string url = GetValueString(env, args[0], urlLength);
if (GetValueType(env, args[1]) != napi_function) {
ThrowError(env, E_PARAMETER_CHECK, "parameter callback type error");
return nullptr;
}
CallbackManager::GetInstance().RegisterCallback(url, CallbackType::ERROR, env, args[1]);
REQUEST_HILOGD("Error callback registered for URL");
return nullptr;
}
napi_value offDownloadSuccess(napi_env env, napi_callback_info info)
{
size_t argc = TWO_ARG;
napi_value args[TWO_ARG] = { nullptr };
napi_status status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
if (status != napi_ok) {
ThrowError(env, E_PARAMETER_CHECK, "parameter obtain error");
return nullptr;
}
if (GetValueType(env, args[0]) != napi_string) {
ThrowError(env, E_PARAMETER_CHECK, "parameter url type error");
return nullptr;
}
size_t urlLength = GetStringLength(env, args[0]);
if (urlLength == 0 || urlLength > MAX_UTL_LENGTH) {
ThrowError(env, E_PARAMETER_CHECK, urlLength == 0 ? "url is empty" : "url exceeds the maximum length");
return nullptr;
}
std::string url = GetValueString(env, args[0], urlLength);
if (argc == ONE_ARG) {
CallbackManager::GetInstance().RemoveCallback(url, CallbackType::SUCCESS, env, nullptr);
REQUEST_HILOGD("Success callback all removed for URL");
return nullptr;
}
if (GetValueType(env, args[1]) != napi_function) {
ThrowError(env, E_PARAMETER_CHECK, "parameter callback type error");
return nullptr;
}
CallbackManager::GetInstance().RemoveCallback(url, CallbackType::SUCCESS, env, args[1]);
REQUEST_HILOGD("Success callback removed for URL");
return nullptr;
}
napi_value offDownloadError(napi_env env, napi_callback_info info)
{
size_t argc = TWO_ARG;
napi_value args[TWO_ARG] = { nullptr };
napi_status status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
if (status != napi_ok) {
ThrowError(env, E_PARAMETER_CHECK, "parameter obtain error");
return nullptr;
}
if (GetValueType(env, args[0]) != napi_string) {
ThrowError(env, E_PARAMETER_CHECK, "parameter url type error");
return nullptr;
}
size_t urlLength = GetStringLength(env, args[0]);
if (urlLength == 0 || urlLength > MAX_UTL_LENGTH) {
ThrowError(env, E_PARAMETER_CHECK, urlLength == 0 ? "url is empty" : "url exceeds the maximum length");
return nullptr;
}
std::string url = GetValueString(env, args[0], urlLength);
if (argc == ONE_ARG) {
CallbackManager::GetInstance().RemoveCallback(url, CallbackType::ERROR, env, nullptr);
REQUEST_HILOGD("Error callback all removed for URL");
return nullptr;
}
if (GetValueType(env, args[1]) != napi_function) {
ThrowError(env, E_PARAMETER_CHECK, "parameter callback type error");
return nullptr;
}
CallbackManager::GetInstance().RemoveCallback(url, CallbackType::ERROR, env, args[1]);
REQUEST_HILOGD("Error callback removed for URL");
return nullptr;
}
napi_value setGlobalRetryOptions(napi_env env, napi_callback_info info)
{
size_t argc = 1;
napi_value args[1] = { nullptr };
PRELOAD_NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr), "napi_get_cb_info failed");
RetryOptions retryOptions = { .maxRetryCount = static_cast<int32_t>(DEFAULT_MAX_RETRY_COUNT) };
if (argc == 0 || IsValueMissingOrSkipped(env, args[0])) {
Preload::GetInstance()->SetGlobalRetryOptions(retryOptions);
return nullptr;
}
if (GetValueType(env, args[0]) != napi_object) {
ThrowError(env, E_PARAMETER_CHECK, "parameter type error");
return nullptr;
}
if (!ParseNumberField(env, args[0], "maxRetryCount", {MIN_RETRY_COUNT, MAX_RETRY_COUNT},
retryOptions.maxRetryCount)) {
ThrowError(env, E_PARAMETER_CHECK, "maxRetryCount type error or out of range");
return nullptr;
}
Preload::GetInstance()->SetGlobalRetryOptions(retryOptions);
return nullptr;
}
napi_value setGlobalTimeoutOptions(napi_env env, napi_callback_info info)
{
size_t argc = 1;
napi_value args[1] = { nullptr };
PRELOAD_NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr), "napi_get_cb_info failed");
TimeoutOptions timeoutOptions = {
.networkCheckTimeout = static_cast<int32_t>(DEFAULT_NETWORK_CHECK_TIMEOUT),
.httpTotalTimeout = static_cast<int32_t>(DEFAULT_HTTP_TOTAL_TIMEOUT)
};
if (argc == 0 || IsValueMissingOrSkipped(env, args[0])) {
Preload::GetInstance()->SetGlobalTimeoutOptions(timeoutOptions);
return nullptr;
}
if (GetValueType(env, args[0]) != napi_object) {
ThrowError(env, E_PARAMETER_CHECK, "parameter type error");
return nullptr;
}
if (!ParseNumberField(env, args[0], "networkCheckTimeout", {MIN_NETWORK_CHECK_TIMEOUT, MAX_NETWORK_CHECK_TIMEOUT},
timeoutOptions.networkCheckTimeout)) {
ThrowError(env, E_PARAMETER_CHECK, "networkCheckTimeout type error or out of range");
return nullptr;
}
if (!ParseNumberField(env, args[0], "httpTotalTimeout", {MIN_HTTP_TOTAL_TIMEOUT, MAX_HTTP_TOTAL_TIMEOUT},
timeoutOptions.httpTotalTimeout)) {
ThrowError(env, E_PARAMETER_CHECK, "httpTotalTimeout type error or out of range");
return nullptr;
}
Preload::GetInstance()->SetGlobalTimeoutOptions(timeoutOptions);
return nullptr;
}
static void NapiCreateEnumSslType(napi_env env, napi_value &sslType)
{
napi_create_object(env, &sslType);
SetStringPropertyUtf8(env, sslType, "TLS", "TLS");
SetStringPropertyUtf8(env, sslType, "TLCP", "TLCP");
}
static void NapiCreateEnumCacheStrategy(napi_env env, napi_value &cacheStrategy)
{
napi_create_object(env, &cacheStrategy);
SetUint32Property(env, cacheStrategy, "FORCE", static_cast<uint32_t>(CacheStrategy::FORCE));
SetUint32Property(env, cacheStrategy, "LAZY", static_cast<uint32_t>(CacheStrategy::LAZY));
}
static void NapiCreateEnumErrorCode(napi_env env, napi_value &errorCode)
{
napi_create_object(env, &errorCode);
SetUint32Property(env, errorCode, "OTHERS", static_cast<uint32_t>(ErrorCode::OTHERS));
SetUint32Property(env, errorCode, "DNS", static_cast<uint32_t>(ErrorCode::DNS));
SetUint32Property(env, errorCode, "TCP", static_cast<uint32_t>(ErrorCode::TCP));
SetUint32Property(env, errorCode, "SSL", static_cast<uint32_t>(ErrorCode::SSL));
SetUint32Property(env, errorCode, "HTTP", static_cast<uint32_t>(ErrorCode::HTTP));
}
static napi_value registerFunc(napi_env env, napi_value exports)
{
napi_value sslType = nullptr;
napi_value cacheStrategy = nullptr;
napi_value errorCode = nullptr;
NapiCreateEnumSslType(env, sslType);
NapiCreateEnumCacheStrategy(env, cacheStrategy);
NapiCreateEnumErrorCode(env, errorCode);
napi_property_descriptor desc[]{
DECLARE_NAPI_PROPERTY("SslType", sslType),
DECLARE_NAPI_PROPERTY("CacheStrategy", cacheStrategy),
DECLARE_NAPI_PROPERTY("ErrorCode", errorCode),
DECLARE_NAPI_FUNCTION("download", download),
DECLARE_NAPI_FUNCTION("cancel", cancel),
DECLARE_NAPI_FUNCTION("setMemoryCacheSize", setMemoryCacheSize),
DECLARE_NAPI_FUNCTION("setFileCacheSize", setFileCacheSize),
DECLARE_NAPI_FUNCTION("setDownloadInfoListSize", setDownloadInfoListSize),
DECLARE_NAPI_FUNCTION("getDownloadInfo", getDownloadInfo),
DECLARE_NAPI_FUNCTION("clearMemoryCache", clearMemoryCache),
DECLARE_NAPI_FUNCTION("clearFileCache", clearFileCache),
DECLARE_NAPI_FUNCTION("onDownloadSuccess", onDownloadSuccess),
DECLARE_NAPI_FUNCTION("onDownloadError", onDownloadError),
DECLARE_NAPI_FUNCTION("offDownloadSuccess", offDownloadSuccess),
DECLARE_NAPI_FUNCTION("offDownloadError", offDownloadError),
DECLARE_NAPI_FUNCTION("setGlobalRetryOptions", setGlobalRetryOptions),
DECLARE_NAPI_FUNCTION("setGlobalTimeoutOptions", setGlobalTimeoutOptions),
};
PRELOAD_NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(napi_property_descriptor), desc),
"napi_define_properties failed");
return exports;
}
}
static __attribute__((constructor)) void RegisterModule()
{
static napi_module module = { .nm_version = 1,
.nm_flags = 0,
.nm_filename = nullptr,
.nm_register_func = OHOS::Request::registerFunc,
.nm_modname = "request.cacheDownload",
.nm_priv = ((void *)0),
.reserved = { 0 } };
napi_module_register(&module);
}