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        // do not handle callback as curl has it's own progress bar
27        _: Rc<RefCell<dyn Callback>>,
28    ) -> Result<(), DownloadError> {
29        let mut child = Command::new("curl")
30            .arg("-L")
31            .arg("-#")
32            .arg("-S")
33            .arg(remote_path)
34            .stdout(Stdio::piped())
35            .spawn()?;
36
37        let mut stdout = child.stdout.take().ok_or_else(|| {
38            DownloadError::IO(std::io::Error::from(std::io::ErrorKind::BrokenPipe))
39        })?;
40
41        let mut data = [0; 8192];
42        loop {
43            let count = stdout.read(&mut data)?;
44
45            if count == 0 {
46                break;
47            }
48
49            writer.write_all(&data[..count])?;
50        }
51
52        writer.flush()?;
53
54        let status = child.wait()?;
55
56        if !status.success() {
57            return Err(DownloadError::IO(std::io::Error::from(
58                std::io::ErrorKind::NotFound,
59            )));
60        }
61
62        Ok(())
63    }
64}