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