pkg/net_backend/
reqwest_backend.rs

1use std::{
2    cell::RefCell,
3    fs::File,
4    io::{Read, Write},
5    path::Path,
6    rc::Rc,
7    time::Duration,
8};
9
10use super::{Callback, DownloadBackend, DownloadError};
11use reqwest::blocking::Client;
12
13#[derive(Clone)]
14pub struct ReqwestBackend {
15    client: Client,
16    client_no_brotli: Client,
17}
18
19impl DownloadBackend for ReqwestBackend {
20    fn new() -> Result<Self, DownloadError> {
21        let client = Client::builder()
22            .brotli(true)
23            .connect_timeout(Duration::new(5, 0))
24            .build()?;
25        let client_no_brotli = Client::builder()
26            .brotli(false)
27            .connect_timeout(Duration::new(5, 0))
28            .build()?;
29        Ok(Self {
30            client,
31            client_no_brotli,
32        })
33    }
34
35    fn download(
36        &self,
37        remote_path: &str,
38        local_path: &Path,
39        callback: Rc<RefCell<dyn Callback>>,
40    ) -> Result<(), DownloadError> {
41        let mut callback = callback.borrow_mut();
42
43        let mut resp = self.client.get(remote_path).send()?.error_for_status()?;
44
45        let len: u64 = resp.content_length().unwrap_or_else(|| {
46            self.client_no_brotli
47                .head(remote_path)
48                .send()
49                .ok()
50                .and_then(|resp_inner| {
51                    resp_inner
52                        .headers()
53                        .get("content-length")
54                        .and_then(|header| header.to_str().ok())
55                        .and_then(|s| s.parse::<u64>().ok())
56                })
57                .unwrap_or(0)
58        });
59
60        let mut output = File::create(local_path)?;
61
62        callback.start_download(len, remote_path);
63
64        let mut data = [0; 8192];
65        loop {
66            let count = resp.read(&mut data)?;
67            output.write(&data[..count])?;
68            if count == 0 {
69                break;
70            }
71            callback.increment_downloaded(count);
72        }
73        output.flush()?;
74
75        callback.end_download();
76
77        Ok(())
78    }
79}