• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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::collections::{HashMap, HashSet};
15 use std::fs::File;
16 use std::os::fd::{FromRawFd, IntoRawFd, RawFd};
17 
18 pub use ffi::{Action, Mode};
19 use ipc::IpcStatusCode;
20 
21 cfg_oh! {
22     use ipc::parcel::Serialize;
23     use ipc::parcel::Deserialize;
24 }
25 
26 use super::reason::Reason;
27 use super::ATOMIC_SERVICE;
28 use crate::manage::account::GetOhosAccountUid;
29 use crate::manage::network::{NetworkState, NetworkType};
30 use crate::utils::c_wrapper::{CFileSpec, CFormItem, CStringWrapper};
31 use crate::utils::form_item::{FileSpec, FormItem};
32 use crate::utils::{hashmap_to_string, query_calling_bundle};
33 
34 #[cxx::bridge(namespace = "OHOS::Request")]
35 mod ffi {
36     /// Action
37     #[derive(Clone, Copy, PartialEq, Debug)]
38     #[repr(u8)]
39     pub enum Action {
40         /// Download
41         Download = 0,
42         /// Upload
43         Upload,
44         /// Any
45         Any,
46     }
47 
48     /// Mode
49     #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
50     #[repr(u8)]
51     pub enum Mode {
52         /// BackGround
53         BackGround = 0,
54         /// ForeGround
55         FrontEnd,
56         /// Any
57         Any,
58     }
59 }
60 
61 #[derive(Clone, Copy, PartialEq, Debug)]
62 #[repr(u8)]
63 pub(crate) enum Version {
64     API9 = 1,
65     API10,
66 }
67 
68 /// NetworkConfig
69 #[derive(Clone, Copy, PartialEq, Debug)]
70 #[repr(u8)]
71 pub enum NetworkConfig {
72     /// Any
73     Any = 0,
74     /// Wifi
75     Wifi,
76     /// Cellular
77     Cellular,
78 }
79 
80 /// task min speed
81 #[derive(Copy, Clone, Debug, Default)]
82 pub struct MinSpeed {
83     pub(crate) speed: i64,
84     pub(crate) duration: i64,
85 }
86 
87 /// task Timeout
88 #[derive(Copy, Clone, Debug, Default)]
89 pub struct Timeout {
90     pub(crate) connection_timeout: u64,
91     pub(crate) total_timeout: u64,
92 }
93 
94 #[repr(C)]
95 #[derive(Copy, Clone, Debug)]
96 pub(crate) struct CommonTaskConfig {
97     pub(crate) task_id: u32,
98     pub(crate) uid: u64,
99     pub(crate) token_id: u64,
100     pub(crate) action: Action,
101     pub(crate) mode: Mode,
102     pub(crate) cover: bool,
103     pub(crate) network_config: NetworkConfig,
104     pub(crate) metered: bool,
105     pub(crate) roaming: bool,
106     pub(crate) retry: bool,
107     pub(crate) redirect: bool,
108     pub(crate) index: u32,
109     pub(crate) begins: u64,
110     pub(crate) ends: i64,
111     pub(crate) gauge: bool,
112     pub(crate) precise: bool,
113     pub(crate) priority: u32,
114     pub(crate) background: bool,
115     pub(crate) multipart: bool,
116     pub(crate) min_speed: MinSpeed,
117     pub(crate) timeout: Timeout,
118 }
119 
120 /// task config
121 #[derive(Clone, Debug)]
122 pub struct TaskConfig {
123     pub(crate) bundle: String,
124     pub(crate) bundle_type: u32,
125     pub(crate) atomic_account: String,
126     pub(crate) url: String,
127     pub(crate) title: String,
128     pub(crate) description: String,
129     pub(crate) method: String,
130     pub(crate) headers: HashMap<String, String>,
131     pub(crate) data: String,
132     pub(crate) token: String,
133     pub(crate) proxy: String,
134     pub(crate) certificate_pins: String,
135     pub(crate) extras: HashMap<String, String>,
136     pub(crate) version: Version,
137     pub(crate) form_items: Vec<FormItem>,
138     pub(crate) file_specs: Vec<FileSpec>,
139     pub(crate) body_file_paths: Vec<String>,
140     pub(crate) certs_path: Vec<String>,
141     pub(crate) common_data: CommonTaskConfig,
142 }
143 
144 impl TaskConfig {
satisfy_network(&self, network: &NetworkState) -> Result<(), Reason>145     pub(crate) fn satisfy_network(&self, network: &NetworkState) -> Result<(), Reason> {
146         // NetworkConfig::Cellular with NetworkType::Wifi is allowed
147         match network {
148             NetworkState::Offline => Err(Reason::NetworkOffline),
149             NetworkState::Online(info) => match self.common_data.network_config {
150                 NetworkConfig::Any => Ok(()),
151                 NetworkConfig::Wifi if info.network_type == NetworkType::Cellular => {
152                     Err(Reason::UnsupportedNetworkType)
153                 }
154                 _ => {
155                     if (self.common_data.roaming || !info.is_roaming)
156                         && (self.common_data.metered || !info.is_metered)
157                     {
158                         Ok(())
159                     } else {
160                         Err(Reason::UnsupportedNetworkType)
161                     }
162                 }
163             },
164         }
165     }
166 
satisfy_foreground(&self, foreground_abilities: &HashSet<u64>) -> bool167     pub(crate) fn satisfy_foreground(&self, foreground_abilities: &HashSet<u64>) -> bool {
168         self.common_data.mode == Mode::BackGround
169             || foreground_abilities.contains(&self.common_data.uid)
170     }
171 }
172 
173 pub(crate) struct ConfigSet {
174     pub(crate) headers: String,
175     pub(crate) extras: String,
176     pub(crate) form_items: Vec<CFormItem>,
177     pub(crate) file_specs: Vec<CFileSpec>,
178     pub(crate) body_file_names: Vec<CStringWrapper>,
179     pub(crate) certs_path: Vec<CStringWrapper>,
180 }
181 
182 impl PartialOrd for Mode {
partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering>183     fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
184         Some(self.cmp(other))
185     }
186 }
187 
188 impl Ord for Mode {
cmp(&self, other: &Self) -> std::cmp::Ordering189     fn cmp(&self, other: &Self) -> std::cmp::Ordering {
190         self.to_usize().cmp(&other.to_usize())
191     }
192 }
193 
194 impl Mode {
to_usize(self) -> usize195     fn to_usize(self) -> usize {
196         match self {
197             Mode::FrontEnd => 0,
198             Mode::Any => 1,
199             Mode::BackGround => 2,
200             _ => unreachable!(),
201         }
202     }
203 }
204 
205 impl From<u8> for Mode {
from(value: u8) -> Self206     fn from(value: u8) -> Self {
207         match value {
208             0 => Mode::BackGround,
209             1 => Mode::FrontEnd,
210             _ => Mode::Any,
211         }
212     }
213 }
214 
215 impl From<u8> for Action {
from(value: u8) -> Self216     fn from(value: u8) -> Self {
217         match value {
218             0 => Action::Download,
219             1 => Action::Upload,
220             _ => Action::Any,
221         }
222     }
223 }
224 
225 impl From<u8> for Version {
from(value: u8) -> Self226     fn from(value: u8) -> Self {
227         match value {
228             2 => Version::API10,
229             _ => Version::API9,
230         }
231     }
232 }
233 
234 impl From<u8> for NetworkConfig {
from(value: u8) -> Self235     fn from(value: u8) -> Self {
236         match value {
237             0 => NetworkConfig::Any,
238             2 => NetworkConfig::Cellular,
239             _ => NetworkConfig::Wifi,
240         }
241     }
242 }
243 
244 impl TaskConfig {
build_config_set(&self) -> ConfigSet245     pub(crate) fn build_config_set(&self) -> ConfigSet {
246         ConfigSet {
247             headers: hashmap_to_string(&self.headers),
248             extras: hashmap_to_string(&self.extras),
249             form_items: self.form_items.iter().map(|x| x.to_c_struct()).collect(),
250             file_specs: self.file_specs.iter().map(|x| x.to_c_struct()).collect(),
251             body_file_names: self
252                 .body_file_paths
253                 .iter()
254                 .map(CStringWrapper::from)
255                 .collect(),
256             certs_path: self.certs_path.iter().map(CStringWrapper::from).collect(),
257         }
258     }
259 
contains_user_file(&self) -> bool260     pub(crate) fn contains_user_file(&self) -> bool {
261         for specs in self.file_specs.iter() {
262             if specs.is_user_file {
263                 return true;
264             }
265         }
266         false
267     }
268 }
269 
270 impl Default for TaskConfig {
default() -> Self271     fn default() -> Self {
272         Self {
273             bundle_type: 0,
274             atomic_account: "ohosAnonymousUid".to_string(),
275             bundle: "xxx".to_string(),
276             url: "".to_string(),
277             title: "xxx".to_string(),
278             description: "xxx".to_string(),
279             method: "GET".to_string(),
280             headers: Default::default(),
281             data: "".to_string(),
282             token: "xxx".to_string(),
283             proxy: "".to_string(),
284             extras: Default::default(),
285             version: Version::API10,
286             form_items: vec![],
287             file_specs: vec![],
288             body_file_paths: vec![],
289             certs_path: vec![],
290             certificate_pins: "".to_string(),
291             common_data: CommonTaskConfig {
292                 task_id: 0,
293                 uid: 0,
294                 token_id: 0,
295                 action: Action::Download,
296                 mode: Mode::BackGround,
297                 cover: false,
298                 network_config: NetworkConfig::Any,
299                 metered: false,
300                 roaming: false,
301                 retry: false,
302                 redirect: true,
303                 index: 0,
304                 begins: 0,
305                 ends: -1,
306                 gauge: false,
307                 precise: false,
308                 priority: 0,
309                 background: false,
310                 multipart: false,
311                 min_speed: MinSpeed::default(),
312                 timeout: Timeout::default(),
313             },
314         }
315     }
316 }
317 
318 /// ConfigBuilder
319 pub struct ConfigBuilder {
320     inner: TaskConfig,
321 }
322 
323 impl ConfigBuilder {
324     /// Create a new ConfigBuilder
new() -> Self325     pub fn new() -> Self {
326         Self {
327             inner: TaskConfig::default(),
328         }
329     }
330     /// Set url
url(&mut self, url: &str) -> &mut Self331     pub fn url(&mut self, url: &str) -> &mut Self {
332         self.inner.url = url.to_string();
333         self
334     }
335 
336     /// set version
version(&mut self, version: u8) -> &mut Self337     pub fn version(&mut self, version: u8) -> &mut Self {
338         self.inner.version = version.into();
339         self
340     }
341 
342     /// Set title
file_spec(&mut self, file: File) -> &mut Self343     pub fn file_spec(&mut self, file: File) -> &mut Self {
344         self.inner.file_specs.push(FileSpec::user_file(file));
345         self
346     }
347     /// Set action
action(&mut self, action: Action) -> &mut Self348     pub fn action(&mut self, action: Action) -> &mut Self {
349         self.inner.common_data.action = action;
350         self
351     }
352 
353     /// Set mode
mode(&mut self, mode: Mode) -> &mut Self354     pub fn mode(&mut self, mode: Mode) -> &mut Self {
355         self.inner.common_data.mode = mode;
356         self
357     }
358 
359     /// Set bundle name
bundle_name(&mut self, bundle_name: &str) -> &mut Self360     pub fn bundle_name(&mut self, bundle_name: &str) -> &mut Self {
361         self.inner.bundle = bundle_name.to_string();
362         self
363     }
364 
365     /// Set uid
uid(&mut self, uid: u64) -> &mut Self366     pub fn uid(&mut self, uid: u64) -> &mut Self {
367         self.inner.common_data.uid = uid;
368         self
369     }
370 
371     /// set network
network(&mut self, network: NetworkConfig) -> &mut Self372     pub fn network(&mut self, network: NetworkConfig) -> &mut Self {
373         self.inner.common_data.network_config = network;
374         self
375     }
376 
377     /// Set metered
roaming(&mut self, roaming: bool) -> &mut Self378     pub fn roaming(&mut self, roaming: bool) -> &mut Self {
379         self.inner.common_data.roaming = roaming;
380         self
381     }
382 
383     /// set metered
metered(&mut self, metered: bool) -> &mut Self384     pub fn metered(&mut self, metered: bool) -> &mut Self {
385         self.inner.common_data.metered = metered;
386         self
387     }
388 
389     /// build
build(&mut self) -> TaskConfig390     pub fn build(&mut self) -> TaskConfig {
391         self.inner.clone()
392     }
393 
394     /// redirect
redirect(&mut self, redirect: bool) -> &mut Self395     pub fn redirect(&mut self, redirect: bool) -> &mut Self {
396         self.inner.common_data.redirect = redirect;
397         self
398     }
399 
400     /// begins
begins(&mut self, begins: u64) -> &mut Self401     pub fn begins(&mut self, begins: u64) -> &mut Self {
402         self.inner.common_data.begins = begins;
403         self
404     }
405 
406     /// ends
ends(&mut self, ends: u64) -> &mut Self407     pub fn ends(&mut self, ends: u64) -> &mut Self {
408         self.inner.common_data.ends = ends as i64;
409         self
410     }
411 
412     /// method
method(&mut self, metered: &str) -> &mut Self413     pub fn method(&mut self, metered: &str) -> &mut Self {
414         self.inner.method = metered.to_string();
415         self
416     }
417 
418     /// retry
retry(&mut self, retry: bool) -> &mut Self419     pub fn retry(&mut self, retry: bool) -> &mut Self {
420         self.inner.common_data.retry = retry;
421         self
422     }
423 }
424 
425 #[cfg(feature = "oh")]
426 impl Serialize for TaskConfig {
serialize(&self, parcel: &mut ipc::parcel::MsgParcel) -> ipc::IpcResult<()>427     fn serialize(&self, parcel: &mut ipc::parcel::MsgParcel) -> ipc::IpcResult<()> {
428         parcel.write(&(self.common_data.action.repr as u32))?;
429         parcel.write(&(self.version as u32))?;
430         parcel.write(&(self.common_data.mode.repr as u32))?;
431         parcel.write(&self.bundle_type)?;
432         parcel.write(&self.common_data.cover)?;
433         parcel.write(&(self.common_data.network_config as u32))?;
434         parcel.write(&(self.common_data.metered))?;
435         parcel.write(&self.common_data.roaming)?;
436         parcel.write(&(self.common_data.retry))?;
437         parcel.write(&(self.common_data.redirect))?;
438         parcel.write(&(self.common_data.background))?;
439         parcel.write(&(self.common_data.multipart))?;
440         parcel.write(&self.common_data.index)?;
441         parcel.write(&(self.common_data.begins as i64))?;
442         parcel.write(&self.common_data.ends)?;
443         parcel.write(&self.common_data.gauge)?;
444         parcel.write(&self.common_data.precise)?;
445         parcel.write(&self.common_data.priority)?;
446         parcel.write(&self.common_data.min_speed.speed)?;
447         parcel.write(&self.common_data.min_speed.duration)?;
448         parcel.write(&self.common_data.timeout.connection_timeout)?;
449         parcel.write(&self.common_data.timeout.total_timeout)?;
450         parcel.write(&self.url)?;
451         parcel.write(&self.title)?;
452         parcel.write(&self.method)?;
453         parcel.write(&self.token)?;
454         parcel.write(&self.description)?;
455         parcel.write(&self.data)?;
456         parcel.write(&self.proxy)?;
457         parcel.write(&self.certificate_pins)?;
458 
459         parcel.write(&(self.certs_path.len() as u32))?;
460         for cert_path in &self.certs_path {
461             parcel.write(cert_path)?;
462         }
463 
464         parcel.write(&(self.form_items.len() as u32))?;
465         for form_item in &self.form_items {
466             parcel.write(&form_item.name)?;
467             parcel.write(&form_item.value)?;
468         }
469         parcel.write(&(self.file_specs.len() as u32))?;
470         for file_spec in &self.file_specs {
471             parcel.write(&file_spec.name)?;
472             parcel.write(&file_spec.path)?;
473             parcel.write(&file_spec.file_name)?;
474             parcel.write(&file_spec.mime_type)?;
475             parcel.write(&file_spec.is_user_file)?;
476             if file_spec.is_user_file {
477                 let file = unsafe { File::from_raw_fd(file_spec.fd.unwrap()) };
478                 parcel.write_file(file)?;
479             }
480         }
481 
482         parcel.write(&(self.body_file_paths.len() as u32))?;
483         for body_file_paths in self.body_file_paths.iter() {
484             parcel.write(body_file_paths)?;
485         }
486         parcel.write(&(self.headers.len() as u32))?;
487         for header in self.headers.iter() {
488             parcel.write(header.0)?;
489             parcel.write(header.1)?;
490         }
491 
492         parcel.write(&(self.extras.len() as u32))?;
493         for extra in self.extras.iter() {
494             parcel.write(extra.0)?;
495             parcel.write(extra.1)?;
496         }
497 
498         Ok(())
499     }
500 }
501 
502 #[cfg(feature = "oh")]
503 impl Deserialize for TaskConfig {
deserialize(parcel: &mut ipc::parcel::MsgParcel) -> ipc::IpcResult<Self>504     fn deserialize(parcel: &mut ipc::parcel::MsgParcel) -> ipc::IpcResult<Self> {
505         let action: u32 = parcel.read()?;
506         let action: Action = Action::from(action as u8);
507         let version: u32 = parcel.read()?;
508         let version: Version = Version::from(version as u8);
509         let mode: u32 = parcel.read()?;
510         let mode: Mode = Mode::from(mode as u8);
511         let bundle_type: u32 = parcel.read()?;
512         let cover: bool = parcel.read()?;
513         let network: u32 = parcel.read()?;
514         let network_config = NetworkConfig::from(network as u8);
515         let metered: bool = parcel.read()?;
516         let roaming: bool = parcel.read()?;
517         let retry: bool = parcel.read()?;
518         let redirect: bool = parcel.read()?;
519         let background: bool = parcel.read()?;
520         let multipart: bool = parcel.read()?;
521         let index: u32 = parcel.read()?;
522         let begins: i64 = parcel.read()?;
523         let ends: i64 = parcel.read()?;
524         let gauge: bool = parcel.read()?;
525         let precise: bool = parcel.read()?;
526         let priority: u32 = parcel.read()?;
527         let min_speed: i64 = parcel.read()?;
528         let min_duration: i64 = parcel.read()?;
529         let connection_timeout: u64 = parcel.read()?;
530         let total_timeout: u64 = parcel.read()?;
531         let url: String = parcel.read()?;
532         let title: String = parcel.read()?;
533         let method: String = parcel.read()?;
534         let token: String = parcel.read()?;
535         let description: String = parcel.read()?;
536         let data_base: String = parcel.read()?;
537         let proxy: String = parcel.read()?;
538         let certificate_pins: String = parcel.read()?;
539         let bundle = query_calling_bundle();
540         let uid = ipc::Skeleton::calling_uid();
541         let token_id = ipc::Skeleton::calling_full_token_id();
542         let certs_path_size: u32 = parcel.read()?;
543         if certs_path_size > parcel.readable() as u32 {
544             error!("deserialize failed: certs_path_size too large");
545             sys_event!(
546                 ExecFault,
547                 DfxCode::INVALID_IPC_MESSAGE_A00,
548                 "deserialize failed: certs_path_size too large"
549             );
550             return Err(IpcStatusCode::Failed);
551         }
552         let mut certs_path = Vec::new();
553         for _ in 0..certs_path_size {
554             let cert_path: String = parcel.read()?;
555             certs_path.push(cert_path);
556         }
557 
558         let form_size: u32 = parcel.read()?;
559         if form_size > parcel.readable() as u32 {
560             error!("deserialize failed: form_size too large");
561             sys_event!(
562                 ExecFault,
563                 DfxCode::INVALID_IPC_MESSAGE_A00,
564                 "deserialize failed: form_size too large"
565             );
566             return Err(IpcStatusCode::Failed);
567         }
568         let mut form_items = Vec::new();
569         for _ in 0..form_size {
570             let name: String = parcel.read()?;
571             let value: String = parcel.read()?;
572             form_items.push(FormItem { name, value });
573         }
574 
575         let file_size: u32 = parcel.read()?;
576         if file_size > parcel.readable() as u32 {
577             error!("deserialize failed: file_specs size too large");
578             sys_event!(
579                 ExecFault,
580                 DfxCode::INVALID_IPC_MESSAGE_A00,
581                 "deserialize failed: file_specs size too large"
582             );
583             return Err(IpcStatusCode::Failed);
584         }
585         let mut file_specs: Vec<FileSpec> = Vec::new();
586         for _ in 0..file_size {
587             let name: String = parcel.read()?;
588             let path: String = parcel.read()?;
589             let file_name: String = parcel.read()?;
590             let mime_type: String = parcel.read()?;
591             let is_user_file: bool = parcel.read()?;
592             let mut fd: Option<RawFd> = None;
593             if is_user_file {
594                 let raw_fd = unsafe { parcel.read_raw_fd() };
595                 if raw_fd < 0 {
596                     error!("Failed to open user file, fd: {}", raw_fd);
597                     sys_event!(
598                         ExecFault,
599                         DfxCode::INVALID_IPC_MESSAGE_A00,
600                         "deserialize failed: failed to open user file"
601                     );
602                     return Err(IpcStatusCode::Failed);
603                 }
604                 let ipc_fd = unsafe { File::from_raw_fd(raw_fd) };
605                 fd = Some(ipc_fd.into_raw_fd());
606             }
607             file_specs.push(FileSpec {
608                 name,
609                 path,
610                 file_name,
611                 mime_type,
612                 is_user_file,
613                 fd,
614             });
615         }
616 
617         // Response bodies fd.
618         let body_file_size: u32 = parcel.read()?;
619         if body_file_size > parcel.readable() as u32 {
620             error!("deserialize failed: body_file size too large");
621             sys_event!(
622                 ExecFault,
623                 DfxCode::INVALID_IPC_MESSAGE_A00,
624                 "deserialize failed: body_file size too large"
625             );
626             return Err(IpcStatusCode::Failed);
627         }
628 
629         let mut body_file_paths: Vec<String> = Vec::new();
630         for _ in 0..body_file_size {
631             let file_name: String = parcel.read()?;
632             body_file_paths.push(file_name);
633         }
634 
635         let header_size: u32 = parcel.read()?;
636         if header_size > parcel.readable() as u32 {
637             error!("deserialize failed: header size too large");
638             sys_event!(
639                 ExecFault,
640                 DfxCode::INVALID_IPC_MESSAGE_A00,
641                 "deserialize failed: header size too large"
642             );
643             return Err(IpcStatusCode::Failed);
644         }
645         let mut headers: HashMap<String, String> = HashMap::new();
646         for _ in 0..header_size {
647             let key: String = parcel.read()?;
648             let value: String = parcel.read()?;
649             headers.insert(key, value);
650         }
651 
652         let extras_size: u32 = parcel.read()?;
653         if extras_size > parcel.readable() as u32 {
654             error!("deserialize failed: extras size too large");
655             sys_event!(
656                 ExecFault,
657                 DfxCode::INVALID_IPC_MESSAGE_A00,
658                 "deserialize failed: extras size too large"
659             );
660             return Err(IpcStatusCode::Failed);
661         }
662         let mut extras: HashMap<String, String> = HashMap::new();
663         for _ in 0..extras_size {
664             let key: String = parcel.read()?;
665             let value: String = parcel.read()?;
666             extras.insert(key, value);
667         }
668 
669         let atomic_account = if bundle_type == ATOMIC_SERVICE {
670             GetOhosAccountUid()
671         } else {
672             "".to_string()
673         };
674 
675         let task_config = TaskConfig {
676             bundle,
677             bundle_type,
678             atomic_account,
679             url,
680             title,
681             description,
682             method,
683             headers,
684             data: data_base,
685             token,
686             proxy,
687             certificate_pins,
688             extras,
689             version,
690             form_items,
691             file_specs,
692             body_file_paths,
693             certs_path,
694             common_data: CommonTaskConfig {
695                 task_id: 0,
696                 uid,
697                 token_id,
698                 action,
699                 mode,
700                 cover,
701                 network_config,
702                 metered,
703                 roaming,
704                 retry,
705                 redirect,
706                 index,
707                 begins: begins as u64,
708                 ends,
709                 gauge,
710                 precise,
711                 priority,
712                 background,
713                 multipart,
714                 min_speed: MinSpeed {
715                     speed: min_speed,
716                     duration: min_duration,
717                 },
718                 timeout: Timeout {
719                     connection_timeout,
720                     total_timeout,
721                 },
722             },
723         };
724         Ok(task_config)
725     }
726 }
727 
728 #[cfg(test)]
729 mod ut_config {
730     include!("../../tests/ut/task/ut_config.rs");
731 }
732