Skip to main content

pkg/net_backend/
curl_backend.rs

1use 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/// Network backend using external curl
13#[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}