1 /*
2 * Copyright (C) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "request_event.h"
17
18 #include "log.h"
19 #include "request_manager.h"
20
21 namespace OHOS::Request {
22 constexpr const std::int32_t DECIMALISM = 10;
23 static constexpr const char *EVENT_COMPLETED = "completed";
24 static constexpr const char *EVENT_FAILED = "failed";
25 static constexpr const char *EVENT_PAUSE = "pause";
26 static constexpr const char *EVENT_RESUME = "resume";
27 static constexpr const char *EVENT_REMOVE = "remove";
28 static constexpr const char *EVENT_PROGRESS = "progress";
29 static constexpr const char *EVENT_HEADERRECEIVE = "headerReceive";
30 static constexpr const char *EVENT_FAIL = "fail";
31 static constexpr const char *EVENT_COMPLETE = "complete";
32
33 std::unordered_set<std::string> RequestEvent::supportEventsV9_ = {
34 EVENT_COMPLETE,
35 EVENT_PAUSE,
36 EVENT_REMOVE,
37 EVENT_PROGRESS,
38 EVENT_HEADERRECEIVE,
39 EVENT_FAIL,
40 };
41
42 std::unordered_set<std::string> RequestEvent::supportEventsV10_ = {
43 EVENT_PROGRESS,
44 EVENT_COMPLETED,
45 EVENT_FAILED,
46 EVENT_PAUSE,
47 EVENT_RESUME,
48 EVENT_REMOVE,
49 };
50
51 std::map<std::string, RequestEvent::Event> RequestEvent::requestEvent_ = {
52 { FUNCTION_PAUSE, RequestEvent::PauseExec },
53 { FUNCTION_QUERY, RequestEvent::QueryExec },
54 { FUNCTION_QUERY_MIME_TYPE, RequestEvent::QueryMimeTypeExec },
55 { FUNCTION_REMOVE, RequestEvent::RemoveExec },
56 { FUNCTION_RESUME, RequestEvent::ResumeExec },
57 { FUNCTION_START, RequestEvent::StartExec },
58 { FUNCTION_STOP, RequestEvent::StopExec },
59 };
60
61 std::map<std::string, uint32_t> RequestEvent::resMap_ = {
62 { FUNCTION_PAUSE, BOOL_RES },
63 { FUNCTION_QUERY, INFO_RES },
64 { FUNCTION_QUERY_MIME_TYPE, STR_RES },
65 { FUNCTION_REMOVE, BOOL_RES },
66 { FUNCTION_RESUME, BOOL_RES },
67 { FUNCTION_START, BOOL_RES },
68 };
69
70 std::map<State, DownloadStatus> RequestEvent::stateMap_ = {
71 { State::INITIALIZED, SESSION_PENDING },
72 { State::WAITING, SESSION_PAUSED },
73 { State::RUNNING, SESSION_RUNNING },
74 { State::RETRYING, SESSION_RUNNING },
75 { State::PAUSED, SESSION_PAUSED },
76 { State::COMPLETED, SESSION_SUCCESS },
77 { State::STOPPED, SESSION_FAILED },
78 { State::FAILED, SESSION_FAILED },
79 };
80
81 std::map<Reason, DownloadErrorCode> RequestEvent::failMap_ = {
82 { REASON_OK, ERROR_FILE_ALREADY_EXISTS },
83 { IO_ERROR, ERROR_FILE_ERROR },
84 { REDIRECT_ERROR, ERROR_TOO_MANY_REDIRECTS },
85 { OTHERS_ERROR, ERROR_UNKNOWN },
86 { NETWORK_OFFLINE, ERROR_OFFLINE },
87 { UNSUPPORTED_NETWORK_TYPE, ERROR_UNSUPPORTED_NETWORK_TYPE },
88 { UNSUPPORT_RANGE_REQUEST, ERROR_UNKNOWN },
89 };
90
Pause(napi_env env,napi_callback_info info)91 napi_value RequestEvent::Pause(napi_env env, napi_callback_info info)
92 {
93 REQUEST_HILOGD("Pause in");
94 return Exec(env, info, FUNCTION_PAUSE);
95 }
96
Query(napi_env env,napi_callback_info info)97 napi_value RequestEvent::Query(napi_env env, napi_callback_info info)
98 {
99 REQUEST_HILOGD("QueryV8 in");
100 return Exec(env, info, FUNCTION_QUERY);
101 }
102
QueryMimeType(napi_env env,napi_callback_info info)103 napi_value RequestEvent::QueryMimeType(napi_env env, napi_callback_info info)
104 {
105 REQUEST_HILOGD("QueryMimeType in");
106 return Exec(env, info, FUNCTION_QUERY_MIME_TYPE);
107 }
108
Remove(napi_env env,napi_callback_info info)109 napi_value RequestEvent::Remove(napi_env env, napi_callback_info info)
110 {
111 REQUEST_HILOGD("RemoveV8 in");
112 return Exec(env, info, FUNCTION_REMOVE);
113 }
114
Resume(napi_env env,napi_callback_info info)115 napi_value RequestEvent::Resume(napi_env env, napi_callback_info info)
116 {
117 REQUEST_HILOGD("Resume in");
118 return Exec(env, info, FUNCTION_RESUME);
119 }
120
Start(napi_env env,napi_callback_info info)121 napi_value RequestEvent::Start(napi_env env, napi_callback_info info)
122 {
123 REQUEST_HILOGD("Start in");
124 return Exec(env, info, FUNCTION_START);
125 }
126
Stop(napi_env env,napi_callback_info info)127 napi_value RequestEvent::Stop(napi_env env, napi_callback_info info)
128 {
129 REQUEST_HILOGD("Stop in");
130 return Exec(env, info, FUNCTION_STOP);
131 }
132
On(napi_env env,napi_callback_info info)133 napi_value RequestEvent::On(napi_env env, napi_callback_info info)
134 {
135 JsParam jsParam;
136 ExceptionError err = ParseOnOffParameters(env, info, true, jsParam);
137 if (err.code != E_OK) {
138 bool withErrCode = jsParam.task->config_.version == Version::API10;
139 NapiUtils::ThrowError(env, err.code, err.errInfo, withErrCode);
140 return nullptr;
141 }
142
143 sptr<RequestNotify> listener = new (std::nothrow) RequestNotify(env, jsParam.callback);
144 if (listener == nullptr) {
145 REQUEST_HILOGE("Create callback object fail");
146 return nullptr;
147 }
148 REQUEST_HILOGD("On event %{public}s + %{public}s", jsParam.type.c_str(), jsParam.task->GetTid().c_str());
149 std::string key = jsParam.type + jsParam.task->GetTid();
150 jsParam.task->AddListener(key, listener);
151 if (jsParam.task->GetListenerSize(key) == 1) {
152 RequestManager::GetInstance()->On(
153 jsParam.type, jsParam.task->GetTid(), listener, jsParam.task->config_.version);
154 }
155 return nullptr;
156 }
157
Off(napi_env env,napi_callback_info info)158 napi_value RequestEvent::Off(napi_env env, napi_callback_info info)
159 {
160 JsParam jsParam;
161 ExceptionError err = ParseOnOffParameters(env, info, false, jsParam);
162 if (err.code != E_OK) {
163 bool withErrCode = jsParam.task->config_.version == Version::API10;
164 NapiUtils::ThrowError(env, err.code, err.errInfo, withErrCode);
165 return nullptr;
166 }
167
168 if (jsParam.callback == nullptr) {
169 jsParam.task->RemoveListener(jsParam.type, jsParam.task->GetTid(), jsParam.task->config_.version);
170 } else {
171 jsParam.task->RemoveListener(
172 jsParam.type, jsParam.task->GetTid(), jsParam.callback, jsParam.task->config_.version);
173 }
174 return nullptr;
175 }
176
IsSupportType(const std::string & type,Version version)177 bool RequestEvent::IsSupportType(const std::string &type, Version version)
178 {
179 if (version == Version::API10) {
180 return supportEventsV10_.find(type) != supportEventsV10_.end();
181 } else {
182 return supportEventsV9_.find(type) != supportEventsV9_.end();
183 }
184 }
185
BuildNotifyData(const std::shared_ptr<TaskInfo> & taskInfo)186 NotifyData RequestEvent::BuildNotifyData(const std::shared_ptr<TaskInfo> &taskInfo)
187 {
188 NotifyData notifyData;
189 notifyData.progress = taskInfo->progress;
190 notifyData.action = taskInfo->action;
191 notifyData.version = taskInfo->version;
192 notifyData.mode = taskInfo->mode;
193 notifyData.taskStates = taskInfo->taskStates;
194 return notifyData;
195 }
196
ParseOnOffParameters(napi_env env,napi_callback_info info,bool IsRequiredParam,JsParam & jsParam)197 ExceptionError RequestEvent::ParseOnOffParameters(
198 napi_env env, napi_callback_info info, bool IsRequiredParam, JsParam &jsParam)
199 {
200 ExceptionError err = { .code = E_OK };
201 size_t argc = NapiUtils::MAX_ARGC;
202 napi_value argv[NapiUtils::MAX_ARGC] = { nullptr };
203 napi_status status = napi_get_cb_info(env, info, &argc, argv, &jsParam.self, nullptr);
204 if (status != napi_ok) {
205 return { .code = E_PARAMETER_CHECK, .errInfo = "Failed to obtain parameters" };
206 }
207 napi_unwrap(env, jsParam.self, reinterpret_cast<void **>(&jsParam.task));
208 if (jsParam.task == nullptr) {
209 return { .code = E_PARAMETER_CHECK, .errInfo = "Failed to obtain the current object" };
210 }
211
212 if ((IsRequiredParam && argc < NapiUtils::TWO_ARG) || (!IsRequiredParam && argc < NapiUtils::ONE_ARG)) {
213 return { .code = E_PARAMETER_CHECK, .errInfo = "Wrong number of arguments" };
214 }
215 napi_valuetype valuetype;
216 napi_typeof(env, argv[NapiUtils::FIRST_ARGV], &valuetype);
217 if (valuetype != napi_string) {
218 return { .code = E_PARAMETER_CHECK, .errInfo = "The first parameter is not of string type" };
219 }
220 jsParam.type = NapiUtils::Convert2String(env, argv[NapiUtils::FIRST_ARGV]);
221 if (!IsSupportType(jsParam.type, jsParam.task->config_.version)) {
222 return { .code = E_PARAMETER_CHECK, .errInfo = "First parameter error" };
223 }
224 ConvertType(jsParam.type);
225 if (argc == NapiUtils::ONE_ARG) {
226 return err;
227 }
228 valuetype = napi_undefined;
229 napi_typeof(env, argv[NapiUtils::SECOND_ARGV], &valuetype);
230 if (valuetype != napi_function) {
231 return { .code = E_PARAMETER_CHECK, .errInfo = "The second parameter is not of function type" };
232 }
233 jsParam.callback = argv[NapiUtils::SECOND_ARGV];
234 return err;
235 }
236
ConvertType(std::string & type)237 void RequestEvent::ConvertType(std::string &type)
238 {
239 if (type == EVENT_COMPLETED) {
240 type = EVENT_COMPLETE;
241 }
242 if (type == EVENT_FAILED) {
243 type = EVENT_FAIL;
244 }
245 }
246
Exec(napi_env env,napi_callback_info info,const std::string & execType)247 napi_value RequestEvent::Exec(napi_env env, napi_callback_info info, const std::string &execType)
248 {
249 auto context = std::make_shared<ExecContext>();
250 auto input = [context](size_t argc, napi_value *argv, napi_value self) -> napi_status {
251 return ParseInputParameters(context->env_, argc, self, context);
252 };
253 auto output = [context, execType](napi_value *result) -> napi_status {
254 if (context->innerCode_ != E_OK) {
255 return napi_generic_failure;
256 }
257 return GetResult(context->env_, context, execType, *result);
258 };
259 auto exec = [context, execType]() {
260 auto handle = requestEvent_.find(execType);
261 if (handle != requestEvent_.end()) {
262 context->innerCode_ = handle->second(context);
263 }
264 };
265
266 context->SetInput(input).SetOutput(output).SetExec(exec);
267 AsyncCall asyncCall(env, info, context);
268 return asyncCall.Call(context, execType);
269 }
270
ParseInputParameters(napi_env env,size_t argc,napi_value self,const std::shared_ptr<ExecContext> & context)271 napi_status RequestEvent::ParseInputParameters(
272 napi_env env, size_t argc, napi_value self, const std::shared_ptr<ExecContext> &context)
273 {
274 NAPI_ASSERT_BASE(env, self != nullptr, "self is nullptr", napi_invalid_arg);
275 NAPI_CALL_BASE(env, napi_unwrap(env, self, reinterpret_cast<void **>(&context->task)), napi_invalid_arg);
276 NAPI_ASSERT_BASE(env, context->task != nullptr, "there is no native task", napi_invalid_arg);
277 context->version_ = context->task->config_.version;
278 context->withErrCode_ = context->version_ != Version::API8;
279 return napi_ok;
280 }
281
GetResult(napi_env env,const std::shared_ptr<ExecContext> & context,const std::string & execType,napi_value & result)282 napi_status RequestEvent::GetResult(
283 napi_env env, const std::shared_ptr<ExecContext> &context, const std::string &execType, napi_value &result)
284 {
285 if (resMap_[execType] == BOOL_RES) {
286 return NapiUtils::Convert2JSValue(env, context->boolRes, result);
287 }
288 if (resMap_[execType] == STR_RES) {
289 return NapiUtils::Convert2JSValue(env, context->strRes, result);
290 }
291 if (resMap_[execType] == INFO_RES) {
292 return NapiUtils::Convert2JSValue(env, context->infoRes, result);
293 }
294 return napi_generic_failure;
295 }
296
StartExec(const std::shared_ptr<ExecContext> & context)297 int32_t RequestEvent::StartExec(const std::shared_ptr<ExecContext> &context)
298 {
299 int32_t ret = RequestManager::GetInstance()->Start(context->task->GetTid());
300 if (ret == E_OK) {
301 context->boolRes = true;
302 }
303 return ret;
304 }
305
StopExec(const std::shared_ptr<ExecContext> & context)306 int32_t RequestEvent::StopExec(const std::shared_ptr<ExecContext> &context)
307 {
308 int32_t ret = RequestManager::GetInstance()->Stop(context->task->GetTid());
309 if (ret == E_OK) {
310 context->boolRes = true;
311 }
312 return ret;
313 }
314
PauseExec(const std::shared_ptr<ExecContext> & context)315 int32_t RequestEvent::PauseExec(const std::shared_ptr<ExecContext> &context)
316 {
317 int32_t ret = RequestManager::GetInstance()->Pause(context->task->GetTid(), context->version_);
318 if (ret == E_OK) {
319 context->boolRes = true;
320 }
321 if (context->version_ != Version::API10 && ret != E_PERMISSION) {
322 return E_OK;
323 }
324 return ret;
325 }
326
QueryExec(const std::shared_ptr<ExecContext> & context)327 int32_t RequestEvent::QueryExec(const std::shared_ptr<ExecContext> &context)
328 {
329 TaskInfo infoRes;
330 int32_t ret = E_OK;
331 if (!RequestManager::GetInstance()->LoadRequestServer()) {
332 ret = E_SERVICE_ERROR;
333 return ret;
334 }
335 ret = RequestManager::GetInstance()->Show(context->task->GetTid(), infoRes);
336 if (context->version_ != Version::API10 && ret != E_PERMISSION) {
337 ret = E_OK;
338 }
339 GetDownloadInfo(infoRes, context->infoRes);
340 return ret;
341 }
342
QueryMimeTypeExec(const std::shared_ptr<ExecContext> & context)343 int32_t RequestEvent::QueryMimeTypeExec(const std::shared_ptr<ExecContext> &context)
344 {
345 int32_t ret = E_OK;
346 if (!RequestManager::GetInstance()->LoadRequestServer()) {
347 ret = E_SERVICE_ERROR;
348 return ret;
349 }
350 ret = RequestManager::GetInstance()->QueryMimeType(context->task->GetTid(), context->strRes);
351 if (context->version_ != Version::API10 && ret != E_PERMISSION) {
352 ret = E_OK;
353 }
354 return ret;
355 }
356
GetDownloadInfo(const TaskInfo & infoRes,DownloadInfo & info)357 void RequestEvent::GetDownloadInfo(const TaskInfo &infoRes, DownloadInfo &info)
358 {
359 info.downloadId = strtoul(infoRes.tid.c_str(), NULL, DECIMALISM);
360 if (infoRes.progress.state == State::FAILED) {
361 auto it = failMap_.find(infoRes.code);
362 if (it != failMap_.end()) {
363 info.failedReason = it->second;
364 } else {
365 info.failedReason = ERROR_UNKNOWN;
366 }
367 }
368 if (infoRes.progress.state == State::WAITING
369 && (infoRes.code == NETWORK_OFFLINE || infoRes.code == UNSUPPORTED_NETWORK_TYPE)) {
370 info.pausedReason = PAUSED_WAITING_FOR_NETWORK;
371 }
372 if (infoRes.progress.state == State::PAUSED) {
373 if (infoRes.code == USER_OPERATION) {
374 info.pausedReason = PAUSED_BY_USER;
375 }
376 }
377 if (!infoRes.files.empty()) {
378 info.fileName = infoRes.files[0].filename;
379 info.filePath = infoRes.files[0].uri;
380 }
381 auto it = stateMap_.find(infoRes.progress.state);
382 if (it != stateMap_.end()) {
383 info.status = it->second;
384 }
385 info.url = infoRes.url;
386 info.downloadTitle = infoRes.title;
387 if (!infoRes.progress.sizes.empty()) {
388 info.downloadTotalBytes = infoRes.progress.sizes[0];
389 }
390 info.description = infoRes.description;
391 info.downloadedBytes = infoRes.progress.processed;
392 }
393
RemoveExec(const std::shared_ptr<ExecContext> & context)394 int32_t RequestEvent::RemoveExec(const std::shared_ptr<ExecContext> &context)
395 {
396 int32_t ret = RequestManager::GetInstance()->Remove(context->task->GetTid(), context->version_);
397 if (context->version_ != Version::API10 && ret != E_PERMISSION) {
398 ret = E_OK;
399 }
400 if (ret == E_OK) {
401 context->boolRes = true;
402 }
403 return ret;
404 }
405
ResumeExec(const std::shared_ptr<ExecContext> & context)406 int32_t RequestEvent::ResumeExec(const std::shared_ptr<ExecContext> &context)
407 {
408 int32_t ret = E_OK;
409 if (!RequestManager::GetInstance()->LoadRequestServer()) {
410 ret = E_SERVICE_ERROR;
411 return ret;
412 }
413 ret = RequestManager::GetInstance()->Resume(context->task->GetTid());
414 if (context->version_ != Version::API10 && ret != E_PERMISSION) {
415 ret = E_OK;
416 }
417 if (ret == E_OK) {
418 context->boolRes = true;
419 }
420 return ret;
421 }
422 } // namespace OHOS::Request
423