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