1 /*
2 * Copyright (C) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "js_notify_data_listener.h"
17
18 #include <numeric>
19
20 #include "js_task.h"
21 #include "log.h"
22 #include "napi/native_node_api.h"
23 #include "napi_utils.h"
24 #include "request_event.h"
25 #include "request_manager.h"
26
27 namespace OHOS::Request {
28
AddListener(napi_value cb)29 napi_status JSNotifyDataListener::AddListener(napi_value cb)
30 {
31 napi_status ret = this->AddListenerInner(cb);
32 if (ret != napi_ok) {
33 return ret;
34 }
35 /* remove listener must be subscribed to free task */
36 if (this->validCbNum == 1 && this->type_ != SubscribeType::REMOVE) {
37 RequestManager::GetInstance()->AddListener(this->taskId_, this->type_, shared_from_this());
38 }
39 return napi_ok;
40 }
41
RemoveListener(napi_value cb)42 napi_status JSNotifyDataListener::RemoveListener(napi_value cb)
43 {
44 napi_status ret = this->RemoveListenerInner(cb);
45 if (ret != napi_ok) {
46 return ret;
47 }
48 if (this->validCbNum == 0 && this->type_ != SubscribeType::REMOVE) {
49 RequestManager::GetInstance()->RemoveListener(this->taskId_, this->type_, shared_from_this());
50 }
51 return napi_ok;
52 }
53
IsHeaderReceive(const std::shared_ptr<NotifyData> & notifyData)54 bool JSNotifyDataListener::IsHeaderReceive(const std::shared_ptr<NotifyData> ¬ifyData)
55 {
56 if (notifyData->version == Version::API9 && notifyData->action == Action::UPLOAD
57 && notifyData->type == SubscribeType::HEADER_RECEIVE) {
58 return true;
59 } else if (notifyData->version == Version::API10 && notifyData->action == Action::UPLOAD
60 && notifyData->progress.state == State::COMPLETED
61 && (notifyData->type == SubscribeType::PROGRESS || notifyData->type == SubscribeType::COMPLETED)) {
62 return true;
63 }
64 return false;
65 }
66
ProcessHeaderReceive(const std::shared_ptr<NotifyData> & notifyData)67 void JSNotifyDataListener::ProcessHeaderReceive(const std::shared_ptr<NotifyData> ¬ifyData)
68 {
69 uint32_t index = notifyData->progress.index;
70 size_t len = 0;
71 std::string filePath;
72 {
73 std::lock_guard<std::mutex> lockGuard(JsTask::taskMutex_);
74 auto item = JsTask::taskMap_.find(std::to_string(notifyData->taskId));
75 if (item == JsTask::taskMap_.end()) {
76 REQUEST_HILOGE("Task ID not found");
77 return;
78 }
79 JsTask *task = item->second;
80 len = task->config_.bodyFileNames.size();
81 if (index >= len) {
82 return;
83 }
84 filePath = task->config_.bodyFileNames[index];
85 }
86
87 NapiUtils::ReadBytesFromFile(filePath, notifyData->progress.bodyBytes);
88 // Waiting for "complete" to read and delete.
89 if (!(notifyData->version == Version::API10 && index + 1 == len && notifyData->type == SubscribeType::PROGRESS)) {
90 NapiUtils::RemoveFile(filePath);
91 }
92 }
93
NotifyDataProcess(const std::shared_ptr<NotifyData> & notifyData,napi_value * value,uint32_t & paramNumber)94 void JSNotifyDataListener::NotifyDataProcess(
95 const std::shared_ptr<NotifyData> ¬ifyData, napi_value *value, uint32_t ¶mNumber)
96 {
97 if (IsHeaderReceive(notifyData)) {
98 ProcessHeaderReceive(notifyData);
99 }
100
101 if (notifyData->version == Version::API10) {
102 REQUEST_HILOGD("Receive API10 callback");
103 value[0] = NapiUtils::Convert2JSValue(this->env_, notifyData->progress);
104 return;
105 }
106
107 if (notifyData->action == Action::DOWNLOAD) {
108 if (notifyData->type == SubscribeType::PROGRESS) {
109 value[0] = NapiUtils::Convert2JSValue(this->env_, notifyData->progress.processed);
110 if (!notifyData->progress.sizes.empty()) {
111 value[1] = NapiUtils::Convert2JSValue(this->env_, notifyData->progress.sizes[0]);
112 paramNumber = NapiUtils::TWO_ARG;
113 }
114 } else if (notifyData->type == SubscribeType::FAILED) {
115 if (notifyData->taskStates.empty()) {
116 paramNumber = 0;
117 return;
118 }
119 int64_t failedReason;
120 auto it = RequestEvent::failMap_.find(static_cast<Reason>(notifyData->taskStates[0].responseCode));
121 if (it != RequestEvent::failMap_.end()) {
122 failedReason = it->second;
123 } else {
124 failedReason = static_cast<int64_t>(ERROR_UNKNOWN);
125 }
126 value[0] = NapiUtils::Convert2JSValue(this->env_, failedReason);
127 }
128 } else if (notifyData->action == Action::UPLOAD) {
129 if (notifyData->type == SubscribeType::COMPLETED || notifyData->type == SubscribeType::FAILED) {
130 value[0] = NapiUtils::Convert2JSValue(env_, notifyData->taskStates);
131 } else if (notifyData->type == SubscribeType::PROGRESS) {
132 int64_t totalSize =
133 std::accumulate(notifyData->progress.sizes.begin(), notifyData->progress.sizes.end(), 0);
134 value[0] = NapiUtils::Convert2JSValue(this->env_, notifyData->progress.totalProcessed);
135 value[1] = NapiUtils::Convert2JSValue(this->env_, totalSize);
136 paramNumber = NapiUtils::TWO_ARG;
137 } else if (notifyData->type == SubscribeType::HEADER_RECEIVE) {
138 value[0] = NapiUtils::Convert2JSHeadersAndBody(
139 env_, notifyData->progress.extras, notifyData->progress.bodyBytes, true);
140 }
141 }
142 }
143
SubscribeTypeToString(SubscribeType type)144 static std::string SubscribeTypeToString(SubscribeType type)
145 {
146 switch (type) {
147 case SubscribeType::COMPLETED:
148 return "completed";
149 case SubscribeType::FAILED:
150 return "failed";
151 case SubscribeType::HEADER_RECEIVE:
152 return "header_receive";
153 case SubscribeType::PAUSE:
154 return "pause";
155 case SubscribeType::PROGRESS:
156 return "progress";
157 case SubscribeType::REMOVE:
158 return "remove";
159 case SubscribeType::RESUME:
160 return "resume";
161 case SubscribeType::RESPONSE:
162 return "response";
163 case SubscribeType::BUTT:
164 return "butt";
165 }
166 }
167
CheckRemoveJSTask(const std::shared_ptr<NotifyData> & notifyData,const std::string & tid)168 static RemoveTaskChecker CheckRemoveJSTask(const std::shared_ptr<NotifyData> ¬ifyData, const std::string &tid)
169 {
170 if (notifyData->version == Version::API9
171 && (notifyData->type == SubscribeType::COMPLETED || notifyData->type == SubscribeType::FAILED
172 || notifyData->type == SubscribeType::REMOVE)) {
173 return RemoveTaskChecker::ClearFileAndRemoveTask;
174 } else if (notifyData->version == Version::API10) {
175 if (notifyData->type == SubscribeType::REMOVE) {
176 return RemoveTaskChecker::ClearFileAndRemoveTask;
177 } else if (notifyData->type == SubscribeType::COMPLETED || notifyData->type == SubscribeType::FAILED) {
178 return RemoveTaskChecker::ClearFile;
179 }
180 }
181 return RemoveTaskChecker::DoNothing;
182 }
183
DoJSTask(const std::shared_ptr<NotifyData> & notifyData)184 void JSNotifyDataListener::DoJSTask(const std::shared_ptr<NotifyData> ¬ifyData)
185 {
186 std::string tid = std::to_string(notifyData->taskId);
187 uint32_t paramNumber = NapiUtils::ONE_ARG;
188 napi_value values[NapiUtils::TWO_ARG] = { nullptr };
189 // Data from file to memory.
190 this->NotifyDataProcess(notifyData, values, paramNumber);
191 RemoveTaskChecker checkDo = CheckRemoveJSTask(notifyData, tid);
192 if (checkDo == RemoveTaskChecker::DoNothing) {
193 this->OnMessageReceive(values, paramNumber);
194 } else if (checkDo == RemoveTaskChecker::ClearFile) {
195 JsTask::ClearTaskTemp(tid, true, false, false);
196 REQUEST_HILOGD("jstask %{public}s clear file", tid.c_str());
197 this->OnMessageReceive(values, paramNumber);
198 } else if (checkDo == RemoveTaskChecker::ClearFileAndRemoveTask) {
199 JsTask::ClearTaskTemp(tid, true, true, true);
200 REQUEST_HILOGD("jstask %{public}s clear file", tid.c_str());
201 this->OnMessageReceive(values, paramNumber);
202 JsTask::RemoveTaskContext(tid);
203 JsTask::ClearTaskMap(tid);
204 REQUEST_HILOGD("jstask %{public}s removed", tid.c_str());
205 }
206 }
207
OnNotifyDataReceive(const std::shared_ptr<NotifyData> & notifyData)208 void JSNotifyDataListener::OnNotifyDataReceive(const std::shared_ptr<NotifyData> ¬ifyData)
209 {
210 REQUEST_HILOGI(
211 "Notify %{public}s tid %{public}d", SubscribeTypeToString(notifyData->type).c_str(), notifyData->taskId);
212 NotifyDataPtr *ptr = new (std::nothrow) NotifyDataPtr;
213 if (ptr == nullptr) {
214 REQUEST_HILOGE("NotifyDataPtr new failed");
215 return;
216 }
217 ptr->listener = shared_from_this();
218 ptr->notifyData = notifyData;
219
220 int32_t ret = napi_send_event(
221 this->env_,
222 [ptr]() {
223 uint32_t paramNumber = NapiUtils::ONE_ARG;
224 napi_handle_scope scope = nullptr;
225 napi_open_handle_scope(ptr->listener->env_, &scope);
226 if (scope == nullptr) {
227 delete ptr;
228 return;
229 }
230 ptr->listener->DoJSTask(ptr->notifyData);
231 napi_close_handle_scope(ptr->listener->env_, scope);
232 delete ptr;
233 },
234 napi_eprio_high);
235 if (ret != napi_ok) {
236 REQUEST_HILOGE("napi_send_event failed: %{public}d", ret);
237 delete ptr;
238 }
239 }
240
241 } // namespace OHOS::Request