pkg/net_backend/
curl_backend.rs1use std::{cell::RefCell, rc::Rc};
2use std::{
3 io::{Read, Write},
4 process::{Command, Stdio},
5};
6
7use crate::callback::Callback;
8use crate::net_backend::DownloadBackendWriter;
9
10use super::{DownloadBackend, DownloadError};
11
12#[derive(Clone, Default)]
14pub struct CurlBackend;
15
16impl DownloadBackend for CurlBackend {
17 fn new() -> Result<Self, DownloadError> {
18 Ok(Self)
19 }
20
21 fn download(
22 &self,
23 remote_path: &str,
24 remote_len: Option<u64>,
25 writer: &mut DownloadBackendWriter,
26 callback: Rc<RefCell<dyn Callback>>,
27 ) -> Result<(), DownloadError> {
28 let mut child = Command::new("curl")
29 .arg("-sSL")
30 .arg(remote_path)
31 .stdout(Stdio::piped())
32 .stderr(Stdio::piped())
33 .spawn()?;
34
35 let mut stdout = child.stdout.take().ok_or_else(|| {
36 DownloadError::IO(std::io::Error::from(std::io::ErrorKind::BrokenPipe))
37 })?;
38
39 let mut stderr = child.stderr.take().ok_or_else(|| {
40 DownloadError::IO(std::io::Error::from(std::io::ErrorKind::BrokenPipe))
41 })?;
42
43 let mut callback = callback.borrow_mut();
44 callback.download_start(remote_len.unwrap_or(0), remote_path);
45
46 let mut data = [0; 8192];
47 loop {
48 let count = stdout.read(&mut data)?;
49
50 if count == 0 {
51 break;
52 }
53
54 writer.write_all(&data[..count])?;
55 callback.download_increment(count as u64);
56 }
57
58 writer.flush()?;
59 callback.download_end();
60
61 let status = child.wait()?;
62
63 if !status.success() {
64 let mut buf = Vec::new();
65 let _ = stderr.read_to_end(&mut buf);
66 return Err(DownloadError::Other(format!(
67 "curl exit code {}:\n{}",
68 status.code().unwrap_or(0),
69 String::from_utf8_lossy(&buf)
70 )));
71 }
72
73 Ok(())
74 }
75}