18e9cee63Sopenharmony_ci// Copyright (C) 2023 Huawei Device Co., Ltd. 28e9cee63Sopenharmony_ci// Licensed under the Apache License, Version 2.0 (the "License"); 38e9cee63Sopenharmony_ci// you may not use this file except in compliance with the License. 48e9cee63Sopenharmony_ci// You may obtain a copy of the License at 58e9cee63Sopenharmony_ci// 68e9cee63Sopenharmony_ci// http://www.apache.org/licenses/LICENSE-2.0 78e9cee63Sopenharmony_ci// 88e9cee63Sopenharmony_ci// Unless required by applicable law or agreed to in writing, software 98e9cee63Sopenharmony_ci// distributed under the License is distributed on an "AS IS" BASIS, 108e9cee63Sopenharmony_ci// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 118e9cee63Sopenharmony_ci// See the License for the specific language governing permissions and 128e9cee63Sopenharmony_ci// limitations under the License. 138e9cee63Sopenharmony_ci 148e9cee63Sopenharmony_ciuse std::io::{self, SeekFrom}; 158e9cee63Sopenharmony_ciuse std::sync::atomic::{AtomicBool, AtomicI64, AtomicU32, AtomicU64, Ordering}; 168e9cee63Sopenharmony_ciuse std::sync::{Mutex, MutexGuard}; 178e9cee63Sopenharmony_ciuse std::time::Duration; 188e9cee63Sopenharmony_ci 198e9cee63Sopenharmony_ciuse ylong_http_client::async_impl::{Body, Client, Request, RequestBuilder, Response}; 208e9cee63Sopenharmony_ciuse ylong_http_client::{ErrorKind, HttpClientError}; 218e9cee63Sopenharmony_ciuse ylong_runtime::io::{AsyncSeekExt, AsyncWriteExt}; 228e9cee63Sopenharmony_ci 238e9cee63Sopenharmony_cicfg_oh! { 248e9cee63Sopenharmony_ci use crate::manage::SystemConfig; 258e9cee63Sopenharmony_ci} 268e9cee63Sopenharmony_ci 278e9cee63Sopenharmony_ciuse super::config::{Mode, Version}; 288e9cee63Sopenharmony_ciuse super::info::{CommonTaskInfo, State, TaskInfo, UpdateInfo}; 298e9cee63Sopenharmony_ciuse super::notify::{EachFileStatus, NotifyData, Progress}; 308e9cee63Sopenharmony_ciuse super::reason::Reason; 318e9cee63Sopenharmony_ciuse crate::error::ErrorCode; 328e9cee63Sopenharmony_ciuse crate::manage::database::RequestDb; 338e9cee63Sopenharmony_ciuse crate::manage::network::Network; 348e9cee63Sopenharmony_ciuse crate::manage::notifier::Notifier; 358e9cee63Sopenharmony_ciuse crate::service::client::ClientManagerEntry; 368e9cee63Sopenharmony_ciuse crate::task::client::build_client; 378e9cee63Sopenharmony_ciuse crate::task::config::{Action, TaskConfig}; 388e9cee63Sopenharmony_ciuse crate::task::files::{AttachedFiles, Files}; 398e9cee63Sopenharmony_ciuse crate::utils::form_item::FileSpec; 408e9cee63Sopenharmony_ciuse crate::utils::get_current_timestamp; 418e9cee63Sopenharmony_ci 428e9cee63Sopenharmony_ciconst RETRY_TIMES: u32 = 4; 438e9cee63Sopenharmony_ciconst RETRY_INTERVAL: u64 = 400; 448e9cee63Sopenharmony_ci 458e9cee63Sopenharmony_cipub(crate) struct RequestTask { 468e9cee63Sopenharmony_ci pub(crate) conf: TaskConfig, 478e9cee63Sopenharmony_ci pub(crate) client: Client, 488e9cee63Sopenharmony_ci pub(crate) files: Files, 498e9cee63Sopenharmony_ci pub(crate) body_files: Files, 508e9cee63Sopenharmony_ci pub(crate) ctime: u64, 518e9cee63Sopenharmony_ci pub(crate) mime_type: Mutex<String>, 528e9cee63Sopenharmony_ci pub(crate) progress: Mutex<Progress>, 538e9cee63Sopenharmony_ci pub(crate) status: Mutex<TaskStatus>, 548e9cee63Sopenharmony_ci pub(crate) code: Mutex<Vec<Reason>>, 558e9cee63Sopenharmony_ci pub(crate) tries: AtomicU32, 568e9cee63Sopenharmony_ci pub(crate) background_notify_time: AtomicU64, 578e9cee63Sopenharmony_ci pub(crate) file_total_size: AtomicI64, 588e9cee63Sopenharmony_ci pub(crate) rate_limiting: AtomicU64, 598e9cee63Sopenharmony_ci pub(crate) last_notify: AtomicU64, 608e9cee63Sopenharmony_ci pub(crate) client_manager: ClientManagerEntry, 618e9cee63Sopenharmony_ci pub(crate) running_result: Mutex<Option<Result<(), Reason>>>, 628e9cee63Sopenharmony_ci pub(crate) network: Network, 638e9cee63Sopenharmony_ci pub(crate) timeout_tries: AtomicU32, 648e9cee63Sopenharmony_ci pub(crate) upload_resume: AtomicBool, 658e9cee63Sopenharmony_ci} 668e9cee63Sopenharmony_ci 678e9cee63Sopenharmony_ciimpl RequestTask { 688e9cee63Sopenharmony_ci pub(crate) fn task_id(&self) -> u32 { 698e9cee63Sopenharmony_ci self.conf.common_data.task_id 708e9cee63Sopenharmony_ci } 718e9cee63Sopenharmony_ci 728e9cee63Sopenharmony_ci pub(crate) fn uid(&self) -> u64 { 738e9cee63Sopenharmony_ci self.conf.common_data.uid 748e9cee63Sopenharmony_ci } 758e9cee63Sopenharmony_ci 768e9cee63Sopenharmony_ci pub(crate) fn config(&self) -> &TaskConfig { 778e9cee63Sopenharmony_ci &self.conf 788e9cee63Sopenharmony_ci } 798e9cee63Sopenharmony_ci 808e9cee63Sopenharmony_ci // only use for download task 818e9cee63Sopenharmony_ci pub(crate) fn mime_type(&self) -> String { 828e9cee63Sopenharmony_ci self.mime_type.lock().unwrap().clone() 838e9cee63Sopenharmony_ci } 848e9cee63Sopenharmony_ci 858e9cee63Sopenharmony_ci pub(crate) fn action(&self) -> Action { 868e9cee63Sopenharmony_ci self.conf.common_data.action 878e9cee63Sopenharmony_ci } 888e9cee63Sopenharmony_ci 898e9cee63Sopenharmony_ci pub(crate) fn mode(&self) -> Mode { 908e9cee63Sopenharmony_ci self.conf.common_data.mode 918e9cee63Sopenharmony_ci } 928e9cee63Sopenharmony_ci 938e9cee63Sopenharmony_ci pub(crate) fn speed_limit(&self, limit: u64) { 948e9cee63Sopenharmony_ci let old = self.rate_limiting.swap(limit, Ordering::SeqCst); 958e9cee63Sopenharmony_ci if old != limit { 968e9cee63Sopenharmony_ci info!("task {} speed_limit {}", self.task_id(), limit); 978e9cee63Sopenharmony_ci } 988e9cee63Sopenharmony_ci } 998e9cee63Sopenharmony_ci 1008e9cee63Sopenharmony_ci pub(crate) async fn network_retry(&self) -> Result<(), TaskError> { 1018e9cee63Sopenharmony_ci if self.tries.load(Ordering::SeqCst) < RETRY_TIMES { 1028e9cee63Sopenharmony_ci self.tries.fetch_add(1, Ordering::SeqCst); 1038e9cee63Sopenharmony_ci if !self.network.is_online() { 1048e9cee63Sopenharmony_ci return Err(TaskError::Waiting(TaskPhase::NetworkOffline)); 1058e9cee63Sopenharmony_ci } else { 1068e9cee63Sopenharmony_ci ylong_runtime::time::sleep(Duration::from_millis(RETRY_INTERVAL)).await; 1078e9cee63Sopenharmony_ci return Err(TaskError::Waiting(TaskPhase::NeedRetry)); 1088e9cee63Sopenharmony_ci } 1098e9cee63Sopenharmony_ci } 1108e9cee63Sopenharmony_ci Ok(()) 1118e9cee63Sopenharmony_ci } 1128e9cee63Sopenharmony_ci} 1138e9cee63Sopenharmony_ci 1148e9cee63Sopenharmony_cipub(crate) fn change_upload_size(begins: u64, mut ends: i64, size: i64) -> i64 { 1158e9cee63Sopenharmony_ci if ends < 0 || ends >= size { 1168e9cee63Sopenharmony_ci ends = size - 1; 1178e9cee63Sopenharmony_ci } 1188e9cee63Sopenharmony_ci if begins as i64 > ends { 1198e9cee63Sopenharmony_ci return size; 1208e9cee63Sopenharmony_ci } 1218e9cee63Sopenharmony_ci ends - begins as i64 + 1 1228e9cee63Sopenharmony_ci} 1238e9cee63Sopenharmony_ci 1248e9cee63Sopenharmony_ciimpl RequestTask { 1258e9cee63Sopenharmony_ci pub(crate) fn new( 1268e9cee63Sopenharmony_ci config: TaskConfig, 1278e9cee63Sopenharmony_ci files: AttachedFiles, 1288e9cee63Sopenharmony_ci client: Client, 1298e9cee63Sopenharmony_ci client_manager: ClientManagerEntry, 1308e9cee63Sopenharmony_ci network: Network, 1318e9cee63Sopenharmony_ci upload_resume: bool, 1328e9cee63Sopenharmony_ci ) -> RequestTask { 1338e9cee63Sopenharmony_ci let file_len = files.files.len(); 1348e9cee63Sopenharmony_ci let action = config.common_data.action; 1358e9cee63Sopenharmony_ci 1368e9cee63Sopenharmony_ci let file_total_size = match action { 1378e9cee63Sopenharmony_ci Action::Upload => { 1388e9cee63Sopenharmony_ci let mut file_total_size = 0i64; 1398e9cee63Sopenharmony_ci // If the total size overflows, ignore it. 1408e9cee63Sopenharmony_ci for size in files.sizes.iter() { 1418e9cee63Sopenharmony_ci file_total_size += *size; 1428e9cee63Sopenharmony_ci } 1438e9cee63Sopenharmony_ci file_total_size 1448e9cee63Sopenharmony_ci } 1458e9cee63Sopenharmony_ci Action::Download => -1, 1468e9cee63Sopenharmony_ci _ => unreachable!("Action::Any in RequestTask::new never reach"), 1478e9cee63Sopenharmony_ci }; 1488e9cee63Sopenharmony_ci 1498e9cee63Sopenharmony_ci let mut sizes = files.sizes.clone(); 1508e9cee63Sopenharmony_ci 1518e9cee63Sopenharmony_ci if action == Action::Upload && config.common_data.index < sizes.len() as u32 { 1528e9cee63Sopenharmony_ci sizes[config.common_data.index as usize] = change_upload_size( 1538e9cee63Sopenharmony_ci config.common_data.begins, 1548e9cee63Sopenharmony_ci config.common_data.ends, 1558e9cee63Sopenharmony_ci sizes[config.common_data.index as usize], 1568e9cee63Sopenharmony_ci ); 1578e9cee63Sopenharmony_ci } 1588e9cee63Sopenharmony_ci 1598e9cee63Sopenharmony_ci let time = get_current_timestamp(); 1608e9cee63Sopenharmony_ci let status = TaskStatus::new(time); 1618e9cee63Sopenharmony_ci let progress = Progress::new(sizes); 1628e9cee63Sopenharmony_ci 1638e9cee63Sopenharmony_ci RequestTask { 1648e9cee63Sopenharmony_ci conf: config, 1658e9cee63Sopenharmony_ci client, 1668e9cee63Sopenharmony_ci files: files.files, 1678e9cee63Sopenharmony_ci body_files: files.body_files, 1688e9cee63Sopenharmony_ci ctime: time, 1698e9cee63Sopenharmony_ci mime_type: Mutex::new(String::new()), 1708e9cee63Sopenharmony_ci progress: Mutex::new(progress), 1718e9cee63Sopenharmony_ci tries: AtomicU32::new(0), 1728e9cee63Sopenharmony_ci status: Mutex::new(status), 1738e9cee63Sopenharmony_ci code: Mutex::new(vec![Reason::Default; file_len]), 1748e9cee63Sopenharmony_ci background_notify_time: AtomicU64::new(time), 1758e9cee63Sopenharmony_ci file_total_size: AtomicI64::new(file_total_size), 1768e9cee63Sopenharmony_ci rate_limiting: AtomicU64::new(0), 1778e9cee63Sopenharmony_ci last_notify: AtomicU64::new(time), 1788e9cee63Sopenharmony_ci client_manager, 1798e9cee63Sopenharmony_ci running_result: Mutex::new(None), 1808e9cee63Sopenharmony_ci network, 1818e9cee63Sopenharmony_ci timeout_tries: AtomicU32::new(0), 1828e9cee63Sopenharmony_ci upload_resume: AtomicBool::new(upload_resume), 1838e9cee63Sopenharmony_ci } 1848e9cee63Sopenharmony_ci } 1858e9cee63Sopenharmony_ci 1868e9cee63Sopenharmony_ci pub(crate) fn new_by_info( 1878e9cee63Sopenharmony_ci config: TaskConfig, 1888e9cee63Sopenharmony_ci #[cfg(feature = "oh")] system: SystemConfig, 1898e9cee63Sopenharmony_ci info: TaskInfo, 1908e9cee63Sopenharmony_ci client_manager: ClientManagerEntry, 1918e9cee63Sopenharmony_ci network: Network, 1928e9cee63Sopenharmony_ci upload_resume: bool, 1938e9cee63Sopenharmony_ci ) -> Result<RequestTask, ErrorCode> { 1948e9cee63Sopenharmony_ci #[cfg(feature = "oh")] 1958e9cee63Sopenharmony_ci let (files, client) = check_config(&config, system)?; 1968e9cee63Sopenharmony_ci #[cfg(not(feature = "oh"))] 1978e9cee63Sopenharmony_ci let (files, client) = check_config(&config)?; 1988e9cee63Sopenharmony_ci 1998e9cee63Sopenharmony_ci let file_len = files.files.len(); 2008e9cee63Sopenharmony_ci let action = config.common_data.action; 2018e9cee63Sopenharmony_ci let time = get_current_timestamp(); 2028e9cee63Sopenharmony_ci 2038e9cee63Sopenharmony_ci let file_total_size = match action { 2048e9cee63Sopenharmony_ci Action::Upload => { 2058e9cee63Sopenharmony_ci let mut file_total_size = 0i64; 2068e9cee63Sopenharmony_ci // If the total size overflows, ignore it. 2078e9cee63Sopenharmony_ci for size in files.sizes.iter() { 2088e9cee63Sopenharmony_ci file_total_size += *size; 2098e9cee63Sopenharmony_ci } 2108e9cee63Sopenharmony_ci file_total_size 2118e9cee63Sopenharmony_ci } 2128e9cee63Sopenharmony_ci Action::Download => *info.progress.sizes.first().unwrap_or(&-1), 2138e9cee63Sopenharmony_ci _ => unreachable!("Action::Any in RequestTask::new never reach"), 2148e9cee63Sopenharmony_ci }; 2158e9cee63Sopenharmony_ci 2168e9cee63Sopenharmony_ci // If `TaskInfo` is provided, use data of it. 2178e9cee63Sopenharmony_ci let ctime = info.common_data.ctime; 2188e9cee63Sopenharmony_ci let mime_type = info.mime_type.clone(); 2198e9cee63Sopenharmony_ci let tries = info.common_data.tries; 2208e9cee63Sopenharmony_ci let status = TaskStatus { 2218e9cee63Sopenharmony_ci mtime: time, 2228e9cee63Sopenharmony_ci state: State::from(info.progress.common_data.state), 2238e9cee63Sopenharmony_ci reason: Reason::from(info.common_data.reason), 2248e9cee63Sopenharmony_ci }; 2258e9cee63Sopenharmony_ci let progress = info.progress; 2268e9cee63Sopenharmony_ci 2278e9cee63Sopenharmony_ci Ok(RequestTask { 2288e9cee63Sopenharmony_ci conf: config, 2298e9cee63Sopenharmony_ci client, 2308e9cee63Sopenharmony_ci files: files.files, 2318e9cee63Sopenharmony_ci body_files: files.body_files, 2328e9cee63Sopenharmony_ci ctime, 2338e9cee63Sopenharmony_ci mime_type: Mutex::new(mime_type), 2348e9cee63Sopenharmony_ci progress: Mutex::new(progress), 2358e9cee63Sopenharmony_ci tries: AtomicU32::new(tries), 2368e9cee63Sopenharmony_ci status: Mutex::new(status), 2378e9cee63Sopenharmony_ci code: Mutex::new(vec![Reason::Default; file_len]), 2388e9cee63Sopenharmony_ci background_notify_time: AtomicU64::new(time), 2398e9cee63Sopenharmony_ci file_total_size: AtomicI64::new(file_total_size), 2408e9cee63Sopenharmony_ci rate_limiting: AtomicU64::new(0), 2418e9cee63Sopenharmony_ci last_notify: AtomicU64::new(time), 2428e9cee63Sopenharmony_ci client_manager, 2438e9cee63Sopenharmony_ci running_result: Mutex::new(None), 2448e9cee63Sopenharmony_ci network, 2458e9cee63Sopenharmony_ci timeout_tries: AtomicU32::new(0), 2468e9cee63Sopenharmony_ci upload_resume: AtomicBool::new(upload_resume), 2478e9cee63Sopenharmony_ci }) 2488e9cee63Sopenharmony_ci } 2498e9cee63Sopenharmony_ci 2508e9cee63Sopenharmony_ci pub(crate) fn build_notify_data(&self) -> NotifyData { 2518e9cee63Sopenharmony_ci let vec = self.get_each_file_status(); 2528e9cee63Sopenharmony_ci NotifyData { 2538e9cee63Sopenharmony_ci bundle: self.conf.bundle.clone(), 2548e9cee63Sopenharmony_ci // `unwrap` for propagating panics among threads. 2558e9cee63Sopenharmony_ci progress: self.progress.lock().unwrap().clone(), 2568e9cee63Sopenharmony_ci action: self.conf.common_data.action, 2578e9cee63Sopenharmony_ci version: self.conf.version, 2588e9cee63Sopenharmony_ci each_file_status: vec, 2598e9cee63Sopenharmony_ci task_id: self.conf.common_data.task_id, 2608e9cee63Sopenharmony_ci } 2618e9cee63Sopenharmony_ci } 2628e9cee63Sopenharmony_ci 2638e9cee63Sopenharmony_ci pub(crate) fn update_progress_in_database(&self) { 2648e9cee63Sopenharmony_ci let mtime = self.status.lock().unwrap().mtime; 2658e9cee63Sopenharmony_ci let reason = self.status.lock().unwrap().reason; 2668e9cee63Sopenharmony_ci let progress = self.progress.lock().unwrap().clone(); 2678e9cee63Sopenharmony_ci let update_info = UpdateInfo { 2688e9cee63Sopenharmony_ci mtime, 2698e9cee63Sopenharmony_ci reason: reason.repr, 2708e9cee63Sopenharmony_ci progress, 2718e9cee63Sopenharmony_ci each_file_status: RequestTask::get_each_file_status_by_code( 2728e9cee63Sopenharmony_ci &self.code.lock().unwrap(), 2738e9cee63Sopenharmony_ci &self.conf.file_specs, 2748e9cee63Sopenharmony_ci ), 2758e9cee63Sopenharmony_ci tries: self.tries.load(Ordering::SeqCst), 2768e9cee63Sopenharmony_ci mime_type: self.mime_type(), 2778e9cee63Sopenharmony_ci }; 2788e9cee63Sopenharmony_ci RequestDb::get_instance().update_task(self.task_id(), update_info); 2798e9cee63Sopenharmony_ci } 2808e9cee63Sopenharmony_ci 2818e9cee63Sopenharmony_ci pub(crate) fn build_request_builder(&self) -> Result<RequestBuilder, HttpClientError> { 2828e9cee63Sopenharmony_ci use ylong_http_client::async_impl::PercentEncoder; 2838e9cee63Sopenharmony_ci 2848e9cee63Sopenharmony_ci let url = self.conf.url.clone(); 2858e9cee63Sopenharmony_ci let url = match PercentEncoder::encode(url.as_str()) { 2868e9cee63Sopenharmony_ci Ok(value) => value, 2878e9cee63Sopenharmony_ci Err(e) => { 2888e9cee63Sopenharmony_ci error!("url percent encoding error is {:?}", e); 2898e9cee63Sopenharmony_ci return Err(e); 2908e9cee63Sopenharmony_ci } 2918e9cee63Sopenharmony_ci }; 2928e9cee63Sopenharmony_ci 2938e9cee63Sopenharmony_ci let method = match self.conf.method.to_uppercase().as_str() { 2948e9cee63Sopenharmony_ci "PUT" => "PUT", 2958e9cee63Sopenharmony_ci "POST" => "POST", 2968e9cee63Sopenharmony_ci "GET" => "GET", 2978e9cee63Sopenharmony_ci _ => match self.conf.common_data.action { 2988e9cee63Sopenharmony_ci Action::Upload => { 2998e9cee63Sopenharmony_ci if self.conf.version == Version::API10 { 3008e9cee63Sopenharmony_ci "PUT" 3018e9cee63Sopenharmony_ci } else { 3028e9cee63Sopenharmony_ci "POST" 3038e9cee63Sopenharmony_ci } 3048e9cee63Sopenharmony_ci } 3058e9cee63Sopenharmony_ci Action::Download => "GET", 3068e9cee63Sopenharmony_ci _ => "", 3078e9cee63Sopenharmony_ci }, 3088e9cee63Sopenharmony_ci }; 3098e9cee63Sopenharmony_ci let mut request = RequestBuilder::new().method(method).url(url.as_str()); 3108e9cee63Sopenharmony_ci for (key, value) in self.conf.headers.iter() { 3118e9cee63Sopenharmony_ci request = request.header(key.as_str(), value.as_str()); 3128e9cee63Sopenharmony_ci } 3138e9cee63Sopenharmony_ci Ok(request) 3148e9cee63Sopenharmony_ci } 3158e9cee63Sopenharmony_ci 3168e9cee63Sopenharmony_ci pub(crate) async fn clear_downloaded_file(&self) -> Result<(), std::io::Error> { 3178e9cee63Sopenharmony_ci info!("task {} clear downloaded file", self.task_id()); 3188e9cee63Sopenharmony_ci let file = self.files.get_mut(0).unwrap(); 3198e9cee63Sopenharmony_ci file.set_len(0).await?; 3208e9cee63Sopenharmony_ci file.seek(SeekFrom::Start(0)).await?; 3218e9cee63Sopenharmony_ci 3228e9cee63Sopenharmony_ci let mut progress_guard = self.progress.lock().unwrap(); 3238e9cee63Sopenharmony_ci progress_guard.common_data.total_processed = 0; 3248e9cee63Sopenharmony_ci progress_guard.processed[0] = 0; 3258e9cee63Sopenharmony_ci 3268e9cee63Sopenharmony_ci Ok(()) 3278e9cee63Sopenharmony_ci } 3288e9cee63Sopenharmony_ci 3298e9cee63Sopenharmony_ci pub(crate) async fn build_download_request(&self) -> Result<Request, TaskError> { 3308e9cee63Sopenharmony_ci let mut request_builder = self.build_request_builder()?; 3318e9cee63Sopenharmony_ci 3328e9cee63Sopenharmony_ci let file = self.files.get_mut(0).unwrap(); 3338e9cee63Sopenharmony_ci 3348e9cee63Sopenharmony_ci let has_downloaded = file.metadata().await?.len(); 3358e9cee63Sopenharmony_ci let resume_download = has_downloaded > 0; 3368e9cee63Sopenharmony_ci let require_range = self.require_range(); 3378e9cee63Sopenharmony_ci 3388e9cee63Sopenharmony_ci let begins = self.conf.common_data.begins; 3398e9cee63Sopenharmony_ci let ends = self.conf.common_data.ends; 3408e9cee63Sopenharmony_ci 3418e9cee63Sopenharmony_ci debug!( 3428e9cee63Sopenharmony_ci "task {} build download request, resume_download: {}, require_range: {}", 3438e9cee63Sopenharmony_ci self.task_id(), 3448e9cee63Sopenharmony_ci resume_download, 3458e9cee63Sopenharmony_ci require_range 3468e9cee63Sopenharmony_ci ); 3478e9cee63Sopenharmony_ci match (resume_download, require_range) { 3488e9cee63Sopenharmony_ci (true, false) => { 3498e9cee63Sopenharmony_ci let (builder, support_range) = self.support_range(request_builder); 3508e9cee63Sopenharmony_ci request_builder = builder; 3518e9cee63Sopenharmony_ci if support_range { 3528e9cee63Sopenharmony_ci request_builder = 3538e9cee63Sopenharmony_ci self.range_request(request_builder, begins + has_downloaded, ends); 3548e9cee63Sopenharmony_ci } else { 3558e9cee63Sopenharmony_ci self.clear_downloaded_file().await?; 3568e9cee63Sopenharmony_ci } 3578e9cee63Sopenharmony_ci } 3588e9cee63Sopenharmony_ci (false, true) => { 3598e9cee63Sopenharmony_ci request_builder = self.range_request(request_builder, begins, ends); 3608e9cee63Sopenharmony_ci } 3618e9cee63Sopenharmony_ci (true, true) => { 3628e9cee63Sopenharmony_ci let (builder, support_range) = self.support_range(request_builder); 3638e9cee63Sopenharmony_ci request_builder = builder; 3648e9cee63Sopenharmony_ci if support_range { 3658e9cee63Sopenharmony_ci request_builder = 3668e9cee63Sopenharmony_ci self.range_request(request_builder, begins + has_downloaded, ends); 3678e9cee63Sopenharmony_ci } else { 3688e9cee63Sopenharmony_ci return Err(TaskError::Failed(Reason::UnsupportedRangeRequest)); 3698e9cee63Sopenharmony_ci } 3708e9cee63Sopenharmony_ci } 3718e9cee63Sopenharmony_ci (false, false) => {} 3728e9cee63Sopenharmony_ci }; 3738e9cee63Sopenharmony_ci 3748e9cee63Sopenharmony_ci let request = request_builder.body(Body::slice(self.conf.data.clone()))?; 3758e9cee63Sopenharmony_ci Ok(request) 3768e9cee63Sopenharmony_ci } 3778e9cee63Sopenharmony_ci 3788e9cee63Sopenharmony_ci fn range_request( 3798e9cee63Sopenharmony_ci &self, 3808e9cee63Sopenharmony_ci request_builder: RequestBuilder, 3818e9cee63Sopenharmony_ci begins: u64, 3828e9cee63Sopenharmony_ci ends: i64, 3838e9cee63Sopenharmony_ci ) -> RequestBuilder { 3848e9cee63Sopenharmony_ci let range = if ends < 0 { 3858e9cee63Sopenharmony_ci format!("bytes={begins}-") 3868e9cee63Sopenharmony_ci } else { 3878e9cee63Sopenharmony_ci format!("bytes={begins}-{ends}") 3888e9cee63Sopenharmony_ci }; 3898e9cee63Sopenharmony_ci request_builder.header("Range", range.as_str()) 3908e9cee63Sopenharmony_ci } 3918e9cee63Sopenharmony_ci 3928e9cee63Sopenharmony_ci fn support_range(&self, mut request_builder: RequestBuilder) -> (RequestBuilder, bool) { 3938e9cee63Sopenharmony_ci let progress_guard = self.progress.lock().unwrap(); 3948e9cee63Sopenharmony_ci let mut support_range = false; 3958e9cee63Sopenharmony_ci if let Some(etag) = progress_guard.extras.get("etag") { 3968e9cee63Sopenharmony_ci request_builder = request_builder.header("If-Range", etag.as_str()); 3978e9cee63Sopenharmony_ci support_range = true; 3988e9cee63Sopenharmony_ci } else if let Some(last_modified) = progress_guard.extras.get("last-modified") { 3998e9cee63Sopenharmony_ci request_builder = request_builder.header("If-Range", last_modified.as_str()); 4008e9cee63Sopenharmony_ci support_range = true; 4018e9cee63Sopenharmony_ci } 4028e9cee63Sopenharmony_ci if !support_range { 4038e9cee63Sopenharmony_ci info!("task {} not support range", self.task_id()); 4048e9cee63Sopenharmony_ci } 4058e9cee63Sopenharmony_ci (request_builder, support_range) 4068e9cee63Sopenharmony_ci } 4078e9cee63Sopenharmony_ci 4088e9cee63Sopenharmony_ci pub(crate) fn get_file_info(&self, response: &Response) -> Result<(), TaskError> { 4098e9cee63Sopenharmony_ci let content_type = response.headers().get("content-type"); 4108e9cee63Sopenharmony_ci if let Some(mime_type) = content_type { 4118e9cee63Sopenharmony_ci if let Ok(value) = mime_type.to_string() { 4128e9cee63Sopenharmony_ci *self.mime_type.lock().unwrap() = value; 4138e9cee63Sopenharmony_ci } 4148e9cee63Sopenharmony_ci } 4158e9cee63Sopenharmony_ci 4168e9cee63Sopenharmony_ci let content_length = response.headers().get("content-length"); 4178e9cee63Sopenharmony_ci if let Some(Ok(len)) = content_length.map(|v| v.to_string()) { 4188e9cee63Sopenharmony_ci match len.parse::<i64>() { 4198e9cee63Sopenharmony_ci Ok(v) => { 4208e9cee63Sopenharmony_ci let mut progress = self.progress.lock().unwrap(); 4218e9cee63Sopenharmony_ci progress.sizes = vec![v + progress.processed[0] as i64]; 4228e9cee63Sopenharmony_ci self.file_total_size.store(v, Ordering::SeqCst); 4238e9cee63Sopenharmony_ci debug!("the download task content-length is {}", v); 4248e9cee63Sopenharmony_ci } 4258e9cee63Sopenharmony_ci Err(e) => { 4268e9cee63Sopenharmony_ci error!("convert string to i64 error: {:?}", e); 4278e9cee63Sopenharmony_ci } 4288e9cee63Sopenharmony_ci } 4298e9cee63Sopenharmony_ci } else { 4308e9cee63Sopenharmony_ci error!("cannot get content-length of the task"); 4318e9cee63Sopenharmony_ci if self.conf.common_data.precise { 4328e9cee63Sopenharmony_ci return Err(TaskError::Failed(Reason::GetFileSizeFailed)); 4338e9cee63Sopenharmony_ci } 4348e9cee63Sopenharmony_ci } 4358e9cee63Sopenharmony_ci Ok(()) 4368e9cee63Sopenharmony_ci } 4378e9cee63Sopenharmony_ci 4388e9cee63Sopenharmony_ci pub(crate) async fn handle_download_error( 4398e9cee63Sopenharmony_ci &self, 4408e9cee63Sopenharmony_ci err: HttpClientError, 4418e9cee63Sopenharmony_ci ) -> Result<(), TaskError> { 4428e9cee63Sopenharmony_ci if err.error_kind() != ErrorKind::UserAborted { 4438e9cee63Sopenharmony_ci error!("Task {} {:?}", self.task_id(), err); 4448e9cee63Sopenharmony_ci } 4458e9cee63Sopenharmony_ci match err.error_kind() { 4468e9cee63Sopenharmony_ci ErrorKind::Timeout => Err(TaskError::Failed(Reason::ContinuousTaskTimeout)), 4478e9cee63Sopenharmony_ci // user triggered 4488e9cee63Sopenharmony_ci ErrorKind::UserAborted => Err(TaskError::Waiting(TaskPhase::UserAbort)), 4498e9cee63Sopenharmony_ci ErrorKind::BodyTransfer | ErrorKind::BodyDecode => { 4508e9cee63Sopenharmony_ci self.network_retry().await?; 4518e9cee63Sopenharmony_ci Err(TaskError::Failed(Reason::OthersError)) 4528e9cee63Sopenharmony_ci } 4538e9cee63Sopenharmony_ci _ => { 4548e9cee63Sopenharmony_ci if format!("{}", err).contains("No space left on device") { 4558e9cee63Sopenharmony_ci Err(TaskError::Failed(Reason::InsufficientSpace)) 4568e9cee63Sopenharmony_ci } else { 4578e9cee63Sopenharmony_ci Err(TaskError::Failed(Reason::OthersError)) 4588e9cee63Sopenharmony_ci } 4598e9cee63Sopenharmony_ci } 4608e9cee63Sopenharmony_ci } 4618e9cee63Sopenharmony_ci } 4628e9cee63Sopenharmony_ci 4638e9cee63Sopenharmony_ci #[cfg(feature = "oh")] 4648e9cee63Sopenharmony_ci pub(crate) fn notify_response(&self, response: &Response) { 4658e9cee63Sopenharmony_ci let tid = self.conf.common_data.task_id; 4668e9cee63Sopenharmony_ci let version: String = response.version().as_str().into(); 4678e9cee63Sopenharmony_ci let status_code: u32 = response.status().as_u16() as u32; 4688e9cee63Sopenharmony_ci let status_message: String; 4698e9cee63Sopenharmony_ci if let Some(reason) = response.status().reason() { 4708e9cee63Sopenharmony_ci status_message = reason.into(); 4718e9cee63Sopenharmony_ci } else { 4728e9cee63Sopenharmony_ci error!("bad status_message {:?}", status_code); 4738e9cee63Sopenharmony_ci return; 4748e9cee63Sopenharmony_ci } 4758e9cee63Sopenharmony_ci let headers = response.headers().clone(); 4768e9cee63Sopenharmony_ci debug!("notify_response"); 4778e9cee63Sopenharmony_ci self.client_manager 4788e9cee63Sopenharmony_ci .send_response(tid, version, status_code, status_message, headers) 4798e9cee63Sopenharmony_ci } 4808e9cee63Sopenharmony_ci 4818e9cee63Sopenharmony_ci pub(crate) fn require_range(&self) -> bool { 4828e9cee63Sopenharmony_ci self.conf.common_data.begins > 0 || self.conf.common_data.ends >= 0 4838e9cee63Sopenharmony_ci } 4848e9cee63Sopenharmony_ci 4858e9cee63Sopenharmony_ci pub(crate) async fn record_upload_response( 4868e9cee63Sopenharmony_ci &self, 4878e9cee63Sopenharmony_ci index: usize, 4888e9cee63Sopenharmony_ci response: Result<Response, HttpClientError>, 4898e9cee63Sopenharmony_ci ) { 4908e9cee63Sopenharmony_ci if let Ok(mut r) = response { 4918e9cee63Sopenharmony_ci { 4928e9cee63Sopenharmony_ci let mut guard = self.progress.lock().unwrap(); 4938e9cee63Sopenharmony_ci guard.extras.clear(); 4948e9cee63Sopenharmony_ci for (k, v) in r.headers() { 4958e9cee63Sopenharmony_ci if let Ok(value) = v.to_string() { 4968e9cee63Sopenharmony_ci guard.extras.insert(k.to_string().to_lowercase(), value); 4978e9cee63Sopenharmony_ci } 4988e9cee63Sopenharmony_ci } 4998e9cee63Sopenharmony_ci } 5008e9cee63Sopenharmony_ci 5018e9cee63Sopenharmony_ci let file = match self.body_files.get_mut(index) { 5028e9cee63Sopenharmony_ci Some(file) => file, 5038e9cee63Sopenharmony_ci None => return, 5048e9cee63Sopenharmony_ci }; 5058e9cee63Sopenharmony_ci let _ = file.set_len(0).await; 5068e9cee63Sopenharmony_ci loop { 5078e9cee63Sopenharmony_ci let mut buf = [0u8; 1024]; 5088e9cee63Sopenharmony_ci let size = r.data(&mut buf).await; 5098e9cee63Sopenharmony_ci let size = match size { 5108e9cee63Sopenharmony_ci Ok(size) => size, 5118e9cee63Sopenharmony_ci Err(_e) => break, 5128e9cee63Sopenharmony_ci }; 5138e9cee63Sopenharmony_ci 5148e9cee63Sopenharmony_ci if size == 0 { 5158e9cee63Sopenharmony_ci break; 5168e9cee63Sopenharmony_ci } 5178e9cee63Sopenharmony_ci let _ = file.write_all(&buf[..size]).await; 5188e9cee63Sopenharmony_ci } 5198e9cee63Sopenharmony_ci // Makes sure all the data has been written to the target file. 5208e9cee63Sopenharmony_ci let _ = file.sync_all().await; 5218e9cee63Sopenharmony_ci } 5228e9cee63Sopenharmony_ci } 5238e9cee63Sopenharmony_ci 5248e9cee63Sopenharmony_ci pub(crate) fn get_each_file_status(&self) -> Vec<EachFileStatus> { 5258e9cee63Sopenharmony_ci let mut vec = Vec::new(); 5268e9cee63Sopenharmony_ci // `unwrap` for propagating panics among threads. 5278e9cee63Sopenharmony_ci let codes_guard = self.code.lock().unwrap(); 5288e9cee63Sopenharmony_ci for (i, file_spec) in self.conf.file_specs.iter().enumerate() { 5298e9cee63Sopenharmony_ci let reason = *codes_guard.get(i).unwrap_or(&Reason::Default); 5308e9cee63Sopenharmony_ci vec.push(EachFileStatus { 5318e9cee63Sopenharmony_ci path: file_spec.path.clone(), 5328e9cee63Sopenharmony_ci reason, 5338e9cee63Sopenharmony_ci message: reason.to_str().into(), 5348e9cee63Sopenharmony_ci }); 5358e9cee63Sopenharmony_ci } 5368e9cee63Sopenharmony_ci vec 5378e9cee63Sopenharmony_ci } 5388e9cee63Sopenharmony_ci 5398e9cee63Sopenharmony_ci pub(crate) fn get_each_file_status_by_code( 5408e9cee63Sopenharmony_ci codes_guard: &MutexGuard<Vec<Reason>>, 5418e9cee63Sopenharmony_ci file_specs: &[FileSpec], 5428e9cee63Sopenharmony_ci ) -> Vec<EachFileStatus> { 5438e9cee63Sopenharmony_ci let mut vec = Vec::new(); 5448e9cee63Sopenharmony_ci for (i, file_spec) in file_specs.iter().enumerate() { 5458e9cee63Sopenharmony_ci let reason = *codes_guard.get(i).unwrap_or(&Reason::Default); 5468e9cee63Sopenharmony_ci vec.push(EachFileStatus { 5478e9cee63Sopenharmony_ci path: file_spec.path.clone(), 5488e9cee63Sopenharmony_ci reason, 5498e9cee63Sopenharmony_ci message: reason.to_str().into(), 5508e9cee63Sopenharmony_ci }); 5518e9cee63Sopenharmony_ci } 5528e9cee63Sopenharmony_ci vec 5538e9cee63Sopenharmony_ci } 5548e9cee63Sopenharmony_ci 5558e9cee63Sopenharmony_ci pub(crate) fn info(&self) -> TaskInfo { 5568e9cee63Sopenharmony_ci let status = self.status.lock().unwrap(); 5578e9cee63Sopenharmony_ci let progress = self.progress.lock().unwrap(); 5588e9cee63Sopenharmony_ci TaskInfo { 5598e9cee63Sopenharmony_ci bundle: self.conf.bundle.clone(), 5608e9cee63Sopenharmony_ci url: self.conf.url.clone(), 5618e9cee63Sopenharmony_ci data: self.conf.data.clone(), 5628e9cee63Sopenharmony_ci token: self.conf.token.clone(), 5638e9cee63Sopenharmony_ci form_items: self.conf.form_items.clone(), 5648e9cee63Sopenharmony_ci file_specs: self.conf.file_specs.clone(), 5658e9cee63Sopenharmony_ci title: self.conf.title.clone(), 5668e9cee63Sopenharmony_ci description: self.conf.description.clone(), 5678e9cee63Sopenharmony_ci mime_type: { 5688e9cee63Sopenharmony_ci match self.conf.version { 5698e9cee63Sopenharmony_ci Version::API10 => match self.conf.common_data.action { 5708e9cee63Sopenharmony_ci Action::Download => match self.conf.headers.get("Content-Type") { 5718e9cee63Sopenharmony_ci None => "".into(), 5728e9cee63Sopenharmony_ci Some(v) => v.clone(), 5738e9cee63Sopenharmony_ci }, 5748e9cee63Sopenharmony_ci Action::Upload => "multipart/form-data".into(), 5758e9cee63Sopenharmony_ci _ => "".into(), 5768e9cee63Sopenharmony_ci }, 5778e9cee63Sopenharmony_ci Version::API9 => self.mime_type.lock().unwrap().clone(), 5788e9cee63Sopenharmony_ci } 5798e9cee63Sopenharmony_ci }, 5808e9cee63Sopenharmony_ci progress: progress.clone(), 5818e9cee63Sopenharmony_ci extras: progress.extras.clone(), 5828e9cee63Sopenharmony_ci each_file_status: self.get_each_file_status(), 5838e9cee63Sopenharmony_ci common_data: CommonTaskInfo { 5848e9cee63Sopenharmony_ci task_id: self.conf.common_data.task_id, 5858e9cee63Sopenharmony_ci uid: self.conf.common_data.uid, 5868e9cee63Sopenharmony_ci action: self.conf.common_data.action.repr, 5878e9cee63Sopenharmony_ci mode: self.conf.common_data.mode.repr, 5888e9cee63Sopenharmony_ci ctime: self.ctime, 5898e9cee63Sopenharmony_ci mtime: status.mtime, 5908e9cee63Sopenharmony_ci reason: status.reason.repr, 5918e9cee63Sopenharmony_ci gauge: self.conf.common_data.gauge, 5928e9cee63Sopenharmony_ci retry: self.conf.common_data.retry, 5938e9cee63Sopenharmony_ci tries: self.tries.load(Ordering::SeqCst), 5948e9cee63Sopenharmony_ci version: self.conf.version as u8, 5958e9cee63Sopenharmony_ci priority: self.conf.common_data.priority, 5968e9cee63Sopenharmony_ci }, 5978e9cee63Sopenharmony_ci } 5988e9cee63Sopenharmony_ci } 5998e9cee63Sopenharmony_ci 6008e9cee63Sopenharmony_ci pub(crate) fn notify_header_receive(&self) { 6018e9cee63Sopenharmony_ci if self.conf.version == Version::API9 && self.conf.common_data.action == Action::Upload { 6028e9cee63Sopenharmony_ci let notify_data = self.build_notify_data(); 6038e9cee63Sopenharmony_ci 6048e9cee63Sopenharmony_ci Notifier::header_receive(&self.client_manager, notify_data); 6058e9cee63Sopenharmony_ci } 6068e9cee63Sopenharmony_ci } 6078e9cee63Sopenharmony_ci} 6088e9cee63Sopenharmony_ci 6098e9cee63Sopenharmony_ci#[derive(Clone, Debug)] 6108e9cee63Sopenharmony_cipub(crate) struct TaskStatus { 6118e9cee63Sopenharmony_ci pub(crate) mtime: u64, 6128e9cee63Sopenharmony_ci pub(crate) state: State, 6138e9cee63Sopenharmony_ci pub(crate) reason: Reason, 6148e9cee63Sopenharmony_ci} 6158e9cee63Sopenharmony_ci 6168e9cee63Sopenharmony_ciimpl TaskStatus { 6178e9cee63Sopenharmony_ci pub(crate) fn new(mtime: u64) -> Self { 6188e9cee63Sopenharmony_ci TaskStatus { 6198e9cee63Sopenharmony_ci mtime, 6208e9cee63Sopenharmony_ci state: State::Initialized, 6218e9cee63Sopenharmony_ci reason: Reason::Default, 6228e9cee63Sopenharmony_ci } 6238e9cee63Sopenharmony_ci } 6248e9cee63Sopenharmony_ci} 6258e9cee63Sopenharmony_ci 6268e9cee63Sopenharmony_cifn check_file_specs(file_specs: &[FileSpec]) -> bool { 6278e9cee63Sopenharmony_ci const EL1: &str = "/data/storage/el1/base/"; 6288e9cee63Sopenharmony_ci const EL2: &str = "/data/storage/el2/base/"; 6298e9cee63Sopenharmony_ci const EL5: &str = "/data/storage/el5/base/"; 6308e9cee63Sopenharmony_ci 6318e9cee63Sopenharmony_ci let mut result = true; 6328e9cee63Sopenharmony_ci for (idx, spec) in file_specs.iter().enumerate() { 6338e9cee63Sopenharmony_ci let path = &spec.path; 6348e9cee63Sopenharmony_ci if !spec.is_user_file 6358e9cee63Sopenharmony_ci && !path.starts_with(EL1) 6368e9cee63Sopenharmony_ci && !path.starts_with(EL2) 6378e9cee63Sopenharmony_ci && !path.starts_with(EL5) 6388e9cee63Sopenharmony_ci { 6398e9cee63Sopenharmony_ci error!("File path invalid - path: {}, idx: {}", path, idx); 6408e9cee63Sopenharmony_ci result = false; 6418e9cee63Sopenharmony_ci break; 6428e9cee63Sopenharmony_ci } 6438e9cee63Sopenharmony_ci } 6448e9cee63Sopenharmony_ci 6458e9cee63Sopenharmony_ci result 6468e9cee63Sopenharmony_ci} 6478e9cee63Sopenharmony_ci 6488e9cee63Sopenharmony_cipub(crate) fn check_config( 6498e9cee63Sopenharmony_ci config: &TaskConfig, 6508e9cee63Sopenharmony_ci #[cfg(feature = "oh")] system: SystemConfig, 6518e9cee63Sopenharmony_ci) -> Result<(AttachedFiles, Client), ErrorCode> { 6528e9cee63Sopenharmony_ci if !check_file_specs(&config.file_specs) { 6538e9cee63Sopenharmony_ci return Err(ErrorCode::Other); 6548e9cee63Sopenharmony_ci } 6558e9cee63Sopenharmony_ci let files = AttachedFiles::open(config).map_err(|_| ErrorCode::FileOperationErr)?; 6568e9cee63Sopenharmony_ci #[cfg(feature = "oh")] 6578e9cee63Sopenharmony_ci let client = build_client(config, system).map_err(|_| ErrorCode::Other)?; 6588e9cee63Sopenharmony_ci 6598e9cee63Sopenharmony_ci #[cfg(not(feature = "oh"))] 6608e9cee63Sopenharmony_ci let client = build_client(config).map_err(|_| ErrorCode::Other)?; 6618e9cee63Sopenharmony_ci Ok((files, client)) 6628e9cee63Sopenharmony_ci} 6638e9cee63Sopenharmony_ci 6648e9cee63Sopenharmony_ciimpl From<HttpClientError> for TaskError { 6658e9cee63Sopenharmony_ci fn from(_value: HttpClientError) -> Self { 6668e9cee63Sopenharmony_ci TaskError::Failed(Reason::BuildRequestFailed) 6678e9cee63Sopenharmony_ci } 6688e9cee63Sopenharmony_ci} 6698e9cee63Sopenharmony_ci 6708e9cee63Sopenharmony_ciimpl From<io::Error> for TaskError { 6718e9cee63Sopenharmony_ci fn from(_value: io::Error) -> Self { 6728e9cee63Sopenharmony_ci TaskError::Failed(Reason::IoError) 6738e9cee63Sopenharmony_ci } 6748e9cee63Sopenharmony_ci} 6758e9cee63Sopenharmony_ci 6768e9cee63Sopenharmony_ci#[derive(Debug, PartialEq, Eq)] 6778e9cee63Sopenharmony_cipub enum TaskPhase { 6788e9cee63Sopenharmony_ci NeedRetry, 6798e9cee63Sopenharmony_ci UserAbort, 6808e9cee63Sopenharmony_ci NetworkOffline, 6818e9cee63Sopenharmony_ci} 6828e9cee63Sopenharmony_ci 6838e9cee63Sopenharmony_ci#[derive(Debug, PartialEq, Eq)] 6848e9cee63Sopenharmony_cipub enum TaskError { 6858e9cee63Sopenharmony_ci Failed(Reason), 6868e9cee63Sopenharmony_ci Waiting(TaskPhase), 6878e9cee63Sopenharmony_ci} 6888e9cee63Sopenharmony_ci 6898e9cee63Sopenharmony_ci#[cfg(test)] 6908e9cee63Sopenharmony_cimod test { 6918e9cee63Sopenharmony_ci use crate::task::request_task::change_upload_size; 6928e9cee63Sopenharmony_ci 6938e9cee63Sopenharmony_ci #[test] 6948e9cee63Sopenharmony_ci fn ut_upload_size() { 6958e9cee63Sopenharmony_ci assert_eq!(change_upload_size(0, -1, 30), 30); 6968e9cee63Sopenharmony_ci assert_eq!(change_upload_size(10, -1, 30), 20); 6978e9cee63Sopenharmony_ci assert_eq!(change_upload_size(0, 10, 30), 11); 6988e9cee63Sopenharmony_ci assert_eq!(change_upload_size(10, 10, 100), 1); 6998e9cee63Sopenharmony_ci assert_eq!(change_upload_size(0, 30, 30), 30); 7008e9cee63Sopenharmony_ci assert_eq!(change_upload_size(0, 0, 0), 0); 7018e9cee63Sopenharmony_ci assert_eq!(change_upload_size(10, 9, 100), 100); 7028e9cee63Sopenharmony_ci } 7038e9cee63Sopenharmony_ci} 704