]>
Commit | Line | Data |
---|---|---|
9e490a74 DM |
1 | use failure::*; |
2 | use std::io::Write; | |
3 | use std::sync::Arc; | |
4 | ||
5 | use chrono::{DateTime, Utc}; | |
6 | use serde_json::{json, Value}; | |
7 | ||
8 | use proxmox::tools::digest_to_hex; | |
9 | ||
10 | use crate::tools::futures::Canceller; | |
296c50ba | 11 | use crate::backup::*; |
9e490a74 DM |
12 | |
13 | use super::{HttpClient, H2Client}; | |
14 | ||
913acb41 | 15 | /// Backup Reader |
9e490a74 DM |
16 | pub struct BackupReader { |
17 | h2: H2Client, | |
18 | canceller: Canceller, | |
296c50ba | 19 | crypt_config: Option<Arc<CryptConfig>>, |
9e490a74 DM |
20 | } |
21 | ||
22 | impl Drop for BackupReader { | |
23 | ||
24 | fn drop(&mut self) { | |
25 | self.canceller.cancel(); | |
26 | } | |
27 | } | |
28 | ||
29 | impl BackupReader { | |
30 | ||
296c50ba DM |
31 | fn new(h2: H2Client, canceller: Canceller, crypt_config: Option<Arc<CryptConfig>>) -> Arc<Self> { |
32 | Arc::new(Self { h2, canceller, crypt_config}) | |
9e490a74 DM |
33 | } |
34 | ||
913acb41 | 35 | /// Create a new instance by upgrading the connection at '/api2/json/reader' |
9e490a74 DM |
36 | pub async fn start( |
37 | client: HttpClient, | |
296c50ba | 38 | crypt_config: Option<Arc<CryptConfig>>, |
9e490a74 DM |
39 | datastore: &str, |
40 | backup_type: &str, | |
41 | backup_id: &str, | |
42 | backup_time: DateTime<Utc>, | |
43 | debug: bool, | |
44 | ) -> Result<Arc<BackupReader>, Error> { | |
45 | ||
46 | let param = json!({ | |
47 | "backup-type": backup_type, | |
48 | "backup-id": backup_id, | |
49 | "backup-time": backup_time.timestamp(), | |
50 | "store": datastore, | |
51 | "debug": debug, | |
52 | }); | |
53 | let req = HttpClient::request_builder(client.server(), "GET", "/api2/json/reader", Some(param)).unwrap(); | |
54 | ||
55 | let (h2, canceller) = client.start_h2_connection(req, String::from(PROXMOX_BACKUP_READER_PROTOCOL_ID_V1!())).await?; | |
56 | ||
296c50ba | 57 | Ok(BackupReader::new(h2, canceller, crypt_config)) |
9e490a74 DM |
58 | } |
59 | ||
913acb41 | 60 | /// Execute a GET request |
9e490a74 DM |
61 | pub async fn get( |
62 | &self, | |
63 | path: &str, | |
64 | param: Option<Value>, | |
65 | ) -> Result<Value, Error> { | |
66 | self.h2.get(path, param).await | |
67 | } | |
68 | ||
913acb41 | 69 | /// Execute a PUT request |
9e490a74 DM |
70 | pub async fn put( |
71 | &self, | |
72 | path: &str, | |
73 | param: Option<Value>, | |
74 | ) -> Result<Value, Error> { | |
75 | self.h2.put(path, param).await | |
76 | } | |
77 | ||
913acb41 | 78 | /// Execute a POST request |
9e490a74 DM |
79 | pub async fn post( |
80 | &self, | |
81 | path: &str, | |
82 | param: Option<Value>, | |
83 | ) -> Result<Value, Error> { | |
84 | self.h2.post(path, param).await | |
85 | } | |
86 | ||
913acb41 | 87 | /// Execute a GET request and send output to a writer |
9e490a74 DM |
88 | pub async fn download<W: Write + Send>( |
89 | &self, | |
90 | file_name: &str, | |
91 | output: W, | |
92 | ) -> Result<W, Error> { | |
93 | let path = "download"; | |
94 | let param = json!({ "file-name": file_name }); | |
95 | self.h2.download(path, Some(param), output).await | |
96 | } | |
97 | ||
913acb41 DM |
98 | /// Execute a special GET request and send output to a writer |
99 | /// | |
100 | /// This writes random data, and is only useful to test download speed. | |
9e490a74 DM |
101 | pub async fn speedtest<W: Write + Send>( |
102 | &self, | |
103 | output: W, | |
104 | ) -> Result<W, Error> { | |
105 | self.h2.download("speedtest", None, output).await | |
106 | } | |
107 | ||
913acb41 | 108 | /// Download a specific chunk |
9e490a74 DM |
109 | pub async fn download_chunk<W: Write + Send>( |
110 | &self, | |
111 | digest: &[u8; 32], | |
112 | output: W, | |
113 | ) -> Result<W, Error> { | |
114 | let path = "chunk"; | |
115 | let param = json!({ "digest": digest_to_hex(digest) }); | |
116 | self.h2.download(path, Some(param), output).await | |
117 | } | |
118 | ||
119 | pub fn force_close(self) { | |
120 | self.canceller.cancel(); | |
121 | } | |
296c50ba DM |
122 | |
123 | /// Download backup manifest (index.json) | |
124 | pub async fn download_manifest(&self) -> Result<Value, Error> { | |
125 | ||
126 | let raw_data = self.download(INDEX_BLOB_NAME, Vec::with_capacity(64*1024)).await?; | |
127 | let blob = DataBlob::from_raw(raw_data)?; | |
128 | blob.verify_crc()?; | |
129 | let data = blob.decode(self.crypt_config.as_ref().map(Arc::as_ref))?; | |
130 | let result: Value = serde_json::from_slice(&data[..])?; | |
131 | Ok(result) | |
132 | } | |
9e490a74 | 133 | } |