1 // Copyright (C) 2023 Huawei Device Co., Ltd.
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13
14 use std::io::{self, SeekFrom};
15 use std::sync::atomic::{AtomicBool, AtomicI64, AtomicU32, AtomicU64, AtomicU8, Ordering};
16 use std::sync::{Arc, Mutex};
17 use std::time::Duration;
18
19 use ylong_http_client::async_impl::{Body, Client, Request, RequestBuilder, Response};
20 use ylong_http_client::{ErrorKind, HttpClientError};
21 use ylong_runtime::io::{AsyncSeekExt, AsyncWriteExt};
22
23 cfg_oh! {
24 use crate::manage::SystemConfig;
25 }
26
27 use super::config::Version;
28 use super::info::{CommonTaskInfo, State, TaskInfo, UpdateInfo};
29 use super::notify::{EachFileStatus, NotifyData, Progress};
30 use super::reason::Reason;
31 use crate::error::ErrorCode;
32 use crate::manage::database::RequestDb;
33 use crate::manage::network_manager::NetworkManager;
34 use crate::manage::notifier::Notifier;
35 use crate::service::client::ClientManagerEntry;
36 use crate::service::notification_bar::NotificationDispatcher;
37 use crate::task::client::build_client;
38 use crate::task::config::{Action, TaskConfig};
39 use crate::task::files::{AttachedFiles, Files};
40 use crate::utils::form_item::FileSpec;
41 use crate::utils::get_current_timestamp;
42
43 const RETRY_TIMES: u32 = 4;
44 const RETRY_INTERVAL: u64 = 400;
45
46 pub(crate) struct RequestTask {
47 pub(crate) conf: TaskConfig,
48 pub(crate) client: Client,
49 pub(crate) files: Files,
50 pub(crate) body_files: Files,
51 pub(crate) ctime: u64,
52 pub(crate) mime_type: Mutex<String>,
53 pub(crate) progress: Mutex<Progress>,
54 pub(crate) status: Mutex<TaskStatus>,
55 pub(crate) code: Mutex<Vec<Reason>>,
56 pub(crate) tries: AtomicU32,
57 pub(crate) background_notify_time: AtomicU64,
58 pub(crate) background_notify: Arc<AtomicBool>,
59 pub(crate) file_total_size: AtomicI64,
60 pub(crate) rate_limiting: AtomicU64,
61 pub(crate) max_speed: AtomicI64,
62 pub(crate) last_notify: AtomicU64,
63 pub(crate) client_manager: ClientManagerEntry,
64 pub(crate) running_result: Mutex<Option<Result<(), Reason>>>,
65 pub(crate) timeout_tries: AtomicU32,
66 pub(crate) upload_resume: AtomicBool,
67 pub(crate) mode: AtomicU8,
68 }
69
70 impl RequestTask {
task_id(&self) -> u3271 pub(crate) fn task_id(&self) -> u32 {
72 self.conf.common_data.task_id
73 }
74
uid(&self) -> u6475 pub(crate) fn uid(&self) -> u64 {
76 self.conf.common_data.uid
77 }
78
config(&self) -> &TaskConfig79 pub(crate) fn config(&self) -> &TaskConfig {
80 &self.conf
81 }
82
83 // only use for download task
mime_type(&self) -> String84 pub(crate) fn mime_type(&self) -> String {
85 self.mime_type.lock().unwrap().clone()
86 }
87
action(&self) -> Action88 pub(crate) fn action(&self) -> Action {
89 self.conf.common_data.action
90 }
91
speed_limit(&self, limit: u64)92 pub(crate) fn speed_limit(&self, limit: u64) {
93 let old = self.rate_limiting.swap(limit, Ordering::SeqCst);
94 if old != limit {
95 info!("task {} speed_limit {}", self.task_id(), limit);
96 }
97 }
98
network_retry(&self) -> Result<(), TaskError>99 pub(crate) async fn network_retry(&self) -> Result<(), TaskError> {
100 if self.tries.load(Ordering::SeqCst) < RETRY_TIMES {
101 self.tries.fetch_add(1, Ordering::SeqCst);
102 if !NetworkManager::is_online() {
103 return Err(TaskError::Waiting(TaskPhase::NetworkOffline));
104 } else {
105 ylong_runtime::time::sleep(Duration::from_millis(RETRY_INTERVAL)).await;
106 return Err(TaskError::Waiting(TaskPhase::NeedRetry));
107 }
108 }
109 Ok(())
110 }
111 }
112
change_upload_size(begins: u64, mut ends: i64, size: i64) -> i64113 pub(crate) fn change_upload_size(begins: u64, mut ends: i64, size: i64) -> i64 {
114 if ends < 0 || ends >= size {
115 ends = size - 1;
116 }
117 if begins as i64 > ends {
118 return size;
119 }
120 ends - begins as i64 + 1
121 }
122
123 impl RequestTask {
new( config: TaskConfig, files: AttachedFiles, client: Client, client_manager: ClientManagerEntry, upload_resume: bool, ) -> RequestTask124 pub(crate) fn new(
125 config: TaskConfig,
126 files: AttachedFiles,
127 client: Client,
128 client_manager: ClientManagerEntry,
129 upload_resume: bool,
130 ) -> RequestTask {
131 let file_len = files.files.len();
132 let action = config.common_data.action;
133
134 let file_total_size = match action {
135 Action::Upload => {
136 let mut file_total_size = 0i64;
137 // If the total size overflows, ignore it.
138 for size in files.sizes.iter() {
139 file_total_size += *size;
140 }
141 file_total_size
142 }
143 Action::Download => -1,
144 _ => unreachable!("Action::Any in RequestTask::new never reach"),
145 };
146
147 let mut sizes = files.sizes.clone();
148
149 if action == Action::Upload && config.common_data.index < sizes.len() as u32 {
150 sizes[config.common_data.index as usize] = change_upload_size(
151 config.common_data.begins,
152 config.common_data.ends,
153 sizes[config.common_data.index as usize],
154 );
155 }
156
157 let time = get_current_timestamp();
158 let status = TaskStatus::new(time);
159 let progress = Progress::new(sizes);
160 let mode = AtomicU8::new(config.common_data.mode.repr);
161
162 RequestTask {
163 conf: config,
164 client,
165 files: files.files,
166 body_files: files.body_files,
167 ctime: time,
168 mime_type: Mutex::new(String::new()),
169 progress: Mutex::new(progress),
170 tries: AtomicU32::new(0),
171 status: Mutex::new(status),
172 code: Mutex::new(vec![Reason::Default; file_len]),
173 background_notify_time: AtomicU64::new(time),
174 background_notify: Arc::new(AtomicBool::new(false)),
175 file_total_size: AtomicI64::new(file_total_size),
176 rate_limiting: AtomicU64::new(0),
177 max_speed: AtomicI64::new(0),
178 last_notify: AtomicU64::new(time),
179 client_manager,
180 running_result: Mutex::new(None),
181 timeout_tries: AtomicU32::new(0),
182 upload_resume: AtomicBool::new(upload_resume),
183 mode,
184 }
185 }
186
new_by_info( config: TaskConfig, #[cfg(feature = "oh")] system: SystemConfig, info: TaskInfo, client_manager: ClientManagerEntry, upload_resume: bool, ) -> Result<RequestTask, ErrorCode>187 pub(crate) fn new_by_info(
188 config: TaskConfig,
189 #[cfg(feature = "oh")] system: SystemConfig,
190 info: TaskInfo,
191 client_manager: ClientManagerEntry,
192 upload_resume: bool,
193 ) -> Result<RequestTask, ErrorCode> {
194 #[cfg(feature = "oh")]
195 let (files, client) = check_config(&config, system)?;
196 #[cfg(not(feature = "oh"))]
197 let (files, client) = check_config(&config)?;
198
199 let file_len = files.files.len();
200 let action = config.common_data.action;
201 let time = get_current_timestamp();
202
203 let file_total_size = match action {
204 Action::Upload => {
205 let mut file_total_size = 0i64;
206 // If the total size overflows, ignore it.
207 for size in files.sizes.iter() {
208 file_total_size += *size;
209 }
210 file_total_size
211 }
212 Action::Download => *info.progress.sizes.first().unwrap_or(&-1),
213 _ => unreachable!("Action::Any in RequestTask::new never reach"),
214 };
215
216 // If `TaskInfo` is provided, use data of it.
217 let ctime = info.common_data.ctime;
218 let mime_type = info.mime_type.clone();
219 let tries = info.common_data.tries;
220 let status = TaskStatus {
221 mtime: time,
222 state: State::from(info.progress.common_data.state),
223 reason: Reason::from(info.common_data.reason),
224 };
225 let progress = info.progress;
226 let mode = AtomicU8::new(config.common_data.mode.repr);
227
228 let mut task = RequestTask {
229 conf: config,
230 client,
231 files: files.files,
232 body_files: files.body_files,
233 ctime,
234 mime_type: Mutex::new(mime_type),
235 progress: Mutex::new(progress),
236 tries: AtomicU32::new(tries),
237 status: Mutex::new(status),
238 code: Mutex::new(vec![Reason::Default; file_len]),
239 background_notify_time: AtomicU64::new(time),
240 background_notify: Arc::new(AtomicBool::new(false)),
241 file_total_size: AtomicI64::new(file_total_size),
242 rate_limiting: AtomicU64::new(0),
243 max_speed: AtomicI64::new(info.max_speed),
244 last_notify: AtomicU64::new(time),
245 client_manager,
246 running_result: Mutex::new(None),
247 timeout_tries: AtomicU32::new(0),
248 upload_resume: AtomicBool::new(upload_resume),
249 mode,
250 };
251 let background_notify = NotificationDispatcher::get_instance().register_task(&task);
252 task.background_notify = background_notify;
253 Ok(task)
254 }
255
build_notify_data(&self) -> NotifyData256 pub(crate) fn build_notify_data(&self) -> NotifyData {
257 let vec = self.get_each_file_status();
258 NotifyData {
259 bundle: self.conf.bundle.clone(),
260 // `unwrap` for propagating panics among threads.
261 progress: self.progress.lock().unwrap().clone(),
262 action: self.conf.common_data.action,
263 version: self.conf.version,
264 each_file_status: vec,
265 task_id: self.conf.common_data.task_id,
266 uid: self.conf.common_data.uid,
267 }
268 }
269
update_progress_in_database(&self)270 pub(crate) fn update_progress_in_database(&self) {
271 let mtime = self.status.lock().unwrap().mtime;
272 let reason = self.status.lock().unwrap().reason;
273 let progress = self.progress.lock().unwrap().clone();
274 let update_info = UpdateInfo {
275 mtime,
276 reason: reason.repr,
277 progress,
278 tries: self.tries.load(Ordering::SeqCst),
279 mime_type: self.mime_type(),
280 };
281 RequestDb::get_instance().update_task(self.task_id(), update_info);
282 }
283
build_request_builder(&self) -> Result<RequestBuilder, HttpClientError>284 pub(crate) fn build_request_builder(&self) -> Result<RequestBuilder, HttpClientError> {
285 use ylong_http_client::async_impl::PercentEncoder;
286
287 let url = self.conf.url.clone();
288 let url = match PercentEncoder::encode(url.as_str()) {
289 Ok(value) => value,
290 Err(e) => {
291 error!("url percent encoding error is {:?}", e);
292 sys_event!(
293 ExecFault,
294 DfxCode::TASK_FAULT_03,
295 &format!("url percent encoding error is {:?}", e)
296 );
297 return Err(e);
298 }
299 };
300
301 let method = match self.conf.method.to_uppercase().as_str() {
302 "PUT" => "PUT",
303 "POST" => "POST",
304 "GET" => "GET",
305 _ => match self.conf.common_data.action {
306 Action::Upload => {
307 if self.conf.version == Version::API10 {
308 "PUT"
309 } else {
310 "POST"
311 }
312 }
313 Action::Download => "GET",
314 _ => "",
315 },
316 };
317 let mut request = RequestBuilder::new().method(method).url(url.as_str());
318 for (key, value) in self.conf.headers.iter() {
319 request = request.header(key.as_str(), value.as_str());
320 }
321 Ok(request)
322 }
323
clear_downloaded_file(&self) -> Result<(), std::io::Error>324 pub(crate) async fn clear_downloaded_file(&self) -> Result<(), std::io::Error> {
325 info!("task {} clear downloaded file", self.task_id());
326 let file = self.files.get_mut(0).unwrap();
327 file.set_len(0).await?;
328 file.seek(SeekFrom::Start(0)).await?;
329
330 let mut progress_guard = self.progress.lock().unwrap();
331 progress_guard.common_data.total_processed = 0;
332 progress_guard.processed[0] = 0;
333
334 Ok(())
335 }
336
build_download_request(&self) -> Result<Request, TaskError>337 pub(crate) async fn build_download_request(&self) -> Result<Request, TaskError> {
338 let mut request_builder = self.build_request_builder()?;
339
340 let file = self.files.get_mut(0).unwrap();
341
342 let has_downloaded = file.metadata().await?.len();
343 let resume_download = has_downloaded > 0;
344 let require_range = self.require_range();
345
346 let begins = self.conf.common_data.begins;
347 let ends = self.conf.common_data.ends;
348
349 debug!(
350 "task {} build download request, resume_download: {}, require_range: {}",
351 self.task_id(),
352 resume_download,
353 require_range
354 );
355 match (resume_download, require_range) {
356 (true, false) => {
357 let (builder, support_range) = self.support_range(request_builder);
358 request_builder = builder;
359 if support_range {
360 request_builder =
361 self.range_request(request_builder, begins + has_downloaded, ends);
362 } else {
363 self.clear_downloaded_file().await?;
364 }
365 }
366 (false, true) => {
367 request_builder = self.range_request(request_builder, begins, ends);
368 }
369 (true, true) => {
370 let (builder, support_range) = self.support_range(request_builder);
371 request_builder = builder;
372 if support_range {
373 request_builder =
374 self.range_request(request_builder, begins + has_downloaded, ends);
375 } else {
376 return Err(TaskError::Failed(Reason::UnsupportedRangeRequest));
377 }
378 }
379 (false, false) => {}
380 };
381
382 let request = request_builder.body(Body::slice(self.conf.data.clone()))?;
383 Ok(request)
384 }
385
range_request( &self, request_builder: RequestBuilder, begins: u64, ends: i64, ) -> RequestBuilder386 fn range_request(
387 &self,
388 request_builder: RequestBuilder,
389 begins: u64,
390 ends: i64,
391 ) -> RequestBuilder {
392 let range = if ends < 0 {
393 format!("bytes={begins}-")
394 } else {
395 format!("bytes={begins}-{ends}")
396 };
397 request_builder.header("Range", range.as_str())
398 }
399
support_range(&self, mut request_builder: RequestBuilder) -> (RequestBuilder, bool)400 fn support_range(&self, mut request_builder: RequestBuilder) -> (RequestBuilder, bool) {
401 let progress_guard = self.progress.lock().unwrap();
402 let mut support_range = false;
403 if let Some(etag) = progress_guard.extras.get("etag") {
404 request_builder = request_builder.header("If-Range", etag.as_str());
405 support_range = true;
406 } else if let Some(last_modified) = progress_guard.extras.get("last-modified") {
407 request_builder = request_builder.header("If-Range", last_modified.as_str());
408 support_range = true;
409 }
410 if !support_range {
411 info!("task {} not support range", self.task_id());
412 }
413 (request_builder, support_range)
414 }
415
get_file_info(&self, response: &Response) -> Result<(), TaskError>416 pub(crate) fn get_file_info(&self, response: &Response) -> Result<(), TaskError> {
417 let content_type = response.headers().get("content-type");
418 if let Some(mime_type) = content_type {
419 if let Ok(value) = mime_type.to_string() {
420 *self.mime_type.lock().unwrap() = value;
421 }
422 }
423
424 let content_length = response.headers().get("content-length");
425 if let Some(Ok(len)) = content_length.map(|v| v.to_string()) {
426 match len.parse::<i64>() {
427 Ok(v) => {
428 let mut progress = self.progress.lock().unwrap();
429 progress.sizes = vec![v + progress.processed[0] as i64];
430 self.file_total_size.store(v, Ordering::SeqCst);
431 debug!("the download task content-length is {}", v);
432 }
433 Err(e) => {
434 error!("convert string to i64 error: {:?}", e);
435 sys_event!(
436 ExecFault,
437 DfxCode::TASK_FAULT_09,
438 &format!("convert string to i64 error: {:?}", e)
439 );
440 }
441 }
442 } else {
443 error!("cannot get content-length of the task");
444 sys_event!(
445 ExecFault,
446 DfxCode::TASK_FAULT_09,
447 "cannot get content-length of the task"
448 );
449 if self.conf.common_data.precise {
450 return Err(TaskError::Failed(Reason::GetFileSizeFailed));
451 }
452 }
453 Ok(())
454 }
455
handle_download_error( &self, err: HttpClientError, ) -> Result<(), TaskError>456 pub(crate) async fn handle_download_error(
457 &self,
458 err: HttpClientError,
459 ) -> Result<(), TaskError> {
460 if err.error_kind() != ErrorKind::UserAborted {
461 error!("Task {} {:?}", self.task_id(), err);
462 }
463 match err.error_kind() {
464 ErrorKind::Timeout => {
465 sys_event!(
466 ExecFault,
467 DfxCode::TASK_FAULT_01,
468 &format!("Task {} {:?}", self.task_id(), err)
469 );
470 Err(TaskError::Failed(Reason::ContinuousTaskTimeout))
471 }
472 // user triggered
473 ErrorKind::UserAborted => {
474 sys_event!(
475 ExecFault,
476 DfxCode::TASK_FAULT_09,
477 &format!("Task {} {:?}", self.task_id(), err)
478 );
479 Err(TaskError::Waiting(TaskPhase::UserAbort))
480 }
481 ErrorKind::BodyTransfer | ErrorKind::BodyDecode => {
482 self.network_retry().await?;
483 sys_event!(
484 ExecFault,
485 DfxCode::TASK_FAULT_09,
486 &format!("Task {} {:?}", self.task_id(), err)
487 );
488 Err(TaskError::Failed(Reason::OthersError))
489 }
490 _ => {
491 if format!("{}", err).contains("No space left on device") {
492 sys_event!(
493 ExecFault,
494 DfxCode::TASK_FAULT_09,
495 &format!("Task {} {:?}", self.task_id(), err)
496 );
497 Err(TaskError::Failed(Reason::InsufficientSpace))
498 } else {
499 sys_event!(
500 ExecFault,
501 DfxCode::TASK_FAULT_09,
502 &format!("Task {} {:?}", self.task_id(), err)
503 );
504 Err(TaskError::Failed(Reason::OthersError))
505 }
506 }
507 }
508 }
509
510 #[cfg(feature = "oh")]
notify_response(&self, response: &Response)511 pub(crate) fn notify_response(&self, response: &Response) {
512 let tid = self.conf.common_data.task_id;
513 let version: String = response.version().as_str().into();
514 let status_code: u32 = response.status().as_u16() as u32;
515 let status_message: String;
516 if let Some(reason) = response.status().reason() {
517 status_message = reason.into();
518 } else {
519 error!("bad status_message {:?}", status_code);
520 sys_event!(
521 ExecFault,
522 DfxCode::TASK_FAULT_02,
523 &format!("bad status_message {:?}", status_code)
524 );
525 return;
526 }
527 let headers = response.headers().clone();
528 debug!("notify_response");
529 self.client_manager
530 .send_response(tid, version, status_code, status_message, headers)
531 }
532
require_range(&self) -> bool533 pub(crate) fn require_range(&self) -> bool {
534 self.conf.common_data.begins > 0 || self.conf.common_data.ends >= 0
535 }
536
record_upload_response( &self, index: usize, response: Result<Response, HttpClientError>, )537 pub(crate) async fn record_upload_response(
538 &self,
539 index: usize,
540 response: Result<Response, HttpClientError>,
541 ) {
542 if let Ok(mut r) = response {
543 {
544 let mut guard = self.progress.lock().unwrap();
545 guard.extras.clear();
546 for (k, v) in r.headers() {
547 if let Ok(value) = v.to_string() {
548 guard.extras.insert(k.to_string().to_lowercase(), value);
549 }
550 }
551 }
552
553 let file = match self.body_files.get_mut(index) {
554 Some(file) => file,
555 None => return,
556 };
557 let _ = file.set_len(0).await;
558 loop {
559 let mut buf = [0u8; 1024];
560 let size = r.data(&mut buf).await;
561 let size = match size {
562 Ok(size) => size,
563 Err(_e) => break,
564 };
565
566 if size == 0 {
567 break;
568 }
569 let _ = file.write_all(&buf[..size]).await;
570 }
571 // Makes sure all the data has been written to the target file.
572 let _ = file.sync_all().await;
573 }
574 }
575
get_each_file_status(&self) -> Vec<EachFileStatus>576 pub(crate) fn get_each_file_status(&self) -> Vec<EachFileStatus> {
577 let mut vec = Vec::new();
578 // `unwrap` for propagating panics among threads.
579 let codes_guard = self.code.lock().unwrap();
580 for (i, file_spec) in self.conf.file_specs.iter().enumerate() {
581 let reason = *codes_guard.get(i).unwrap_or(&Reason::Default);
582 vec.push(EachFileStatus {
583 path: file_spec.path.clone(),
584 reason,
585 message: reason.to_str().into(),
586 });
587 }
588 vec
589 }
590
info(&self) -> TaskInfo591 pub(crate) fn info(&self) -> TaskInfo {
592 let status = self.status.lock().unwrap();
593 let progress = self.progress.lock().unwrap();
594 let mode = self.mode.load(Ordering::Acquire);
595 TaskInfo {
596 bundle: self.conf.bundle.clone(),
597 url: self.conf.url.clone(),
598 data: self.conf.data.clone(),
599 token: self.conf.token.clone(),
600 form_items: self.conf.form_items.clone(),
601 file_specs: self.conf.file_specs.clone(),
602 title: self.conf.title.clone(),
603 description: self.conf.description.clone(),
604 mime_type: {
605 match self.conf.version {
606 Version::API10 => match self.conf.common_data.action {
607 Action::Download => match self.conf.headers.get("Content-Type") {
608 None => "".into(),
609 Some(v) => v.clone(),
610 },
611 Action::Upload => "multipart/form-data".into(),
612 _ => "".into(),
613 },
614 Version::API9 => self.mime_type.lock().unwrap().clone(),
615 }
616 },
617 progress: progress.clone(),
618 extras: progress.extras.clone(),
619 common_data: CommonTaskInfo {
620 task_id: self.conf.common_data.task_id,
621 uid: self.conf.common_data.uid,
622 action: self.conf.common_data.action.repr,
623 mode,
624 ctime: self.ctime,
625 mtime: status.mtime,
626 reason: status.reason.repr,
627 gauge: self.conf.common_data.gauge,
628 retry: self.conf.common_data.retry,
629 tries: self.tries.load(Ordering::SeqCst),
630 version: self.conf.version as u8,
631 priority: self.conf.common_data.priority,
632 },
633 max_speed: self.max_speed.load(Ordering::SeqCst),
634 }
635 }
636
notify_header_receive(&self)637 pub(crate) fn notify_header_receive(&self) {
638 if self.conf.version == Version::API9 && self.conf.common_data.action == Action::Upload {
639 let notify_data = self.build_notify_data();
640
641 Notifier::header_receive(&self.client_manager, notify_data);
642 }
643 }
644 }
645
646 #[derive(Clone, Debug)]
647 pub(crate) struct TaskStatus {
648 pub(crate) mtime: u64,
649 pub(crate) state: State,
650 pub(crate) reason: Reason,
651 }
652
653 impl TaskStatus {
new(mtime: u64) -> Self654 pub(crate) fn new(mtime: u64) -> Self {
655 TaskStatus {
656 mtime,
657 state: State::Initialized,
658 reason: Reason::Default,
659 }
660 }
661 }
662
check_file_specs(file_specs: &[FileSpec]) -> bool663 fn check_file_specs(file_specs: &[FileSpec]) -> bool {
664 const EL1: &str = "/data/storage/el1/base/";
665 const EL2: &str = "/data/storage/el2/base/";
666 const EL5: &str = "/data/storage/el5/base/";
667
668 let mut result = true;
669 for (idx, spec) in file_specs.iter().enumerate() {
670 let path = &spec.path;
671 if !spec.is_user_file
672 && !path.starts_with(EL1)
673 && !path.starts_with(EL2)
674 && !path.starts_with(EL5)
675 {
676 error!("File path invalid - path: {}, idx: {}", path, idx);
677 sys_event!(
678 ExecFault,
679 DfxCode::TASK_FAULT_09,
680 &format!("File path invalid - path: {}, idx: {}", path, idx)
681 );
682 result = false;
683 break;
684 }
685 }
686
687 result
688 }
689
check_config( config: &TaskConfig, #[cfg(feature = "oh")] system: SystemConfig, ) -> Result<(AttachedFiles, Client), ErrorCode>690 pub(crate) fn check_config(
691 config: &TaskConfig,
692 #[cfg(feature = "oh")] system: SystemConfig,
693 ) -> Result<(AttachedFiles, Client), ErrorCode> {
694 if !check_file_specs(&config.file_specs) {
695 return Err(ErrorCode::Other);
696 }
697 let files = AttachedFiles::open(config).map_err(|_| ErrorCode::FileOperationErr)?;
698 #[cfg(feature = "oh")]
699 let client = build_client(config, system).map_err(|_| ErrorCode::Other)?;
700
701 #[cfg(not(feature = "oh"))]
702 let client = build_client(config).map_err(|_| ErrorCode::Other)?;
703 Ok((files, client))
704 }
705
706 impl From<HttpClientError> for TaskError {
from(_value: HttpClientError) -> Self707 fn from(_value: HttpClientError) -> Self {
708 TaskError::Failed(Reason::BuildRequestFailed)
709 }
710 }
711
712 impl From<io::Error> for TaskError {
from(_value: io::Error) -> Self713 fn from(_value: io::Error) -> Self {
714 TaskError::Failed(Reason::IoError)
715 }
716 }
717
718 #[derive(Debug, PartialEq, Eq)]
719 pub enum TaskPhase {
720 NeedRetry,
721 UserAbort,
722 NetworkOffline,
723 }
724
725 #[derive(Debug, PartialEq, Eq)]
726 pub enum TaskError {
727 Failed(Reason),
728 Waiting(TaskPhase),
729 }
730
731 #[cfg(test)]
732 mod test {
733 use crate::task::request_task::change_upload_size;
734
735 #[test]
ut_upload_size()736 fn ut_upload_size() {
737 assert_eq!(change_upload_size(0, -1, 30), 30);
738 assert_eq!(change_upload_size(10, -1, 30), 20);
739 assert_eq!(change_upload_size(0, 10, 30), 11);
740 assert_eq!(change_upload_size(10, 10, 100), 1);
741 assert_eq!(change_upload_size(0, 30, 30), 30);
742 assert_eq!(change_upload_size(0, 0, 0), 0);
743 assert_eq!(change_upload_size(10, 9, 100), 100);
744 }
745 }
746