rust_releases_io/client/
remote_client.rs1use crate::client::errors::{HttpError, IoError};
2use crate::{Document, ResourceFile, RetrievalLocation, RetrievedDocument, RustReleasesClient};
3use std::io::Read;
4use std::time::Duration;
5
6const DEFAULT_MEMORY_SIZE: usize = 4096;
7
8const DEFAULT_TIMEOUT: Duration = Duration::from_secs(150);
9
10#[derive(Debug)]
17pub struct HttpClient {
18 agent: ureq::Agent,
19}
20
21impl HttpClient {
22 pub fn new(timeout: Duration) -> Self {
32 let config = ureq::Agent::config_builder()
33 .user_agent("rust-releases (github.com/foresterre/rust-releases/issues)")
34 .proxy(ureq::Proxy::try_from_env())
35 .timeout_global(Some(timeout))
36 .build();
37
38 let agent = config.new_agent();
39
40 Self { agent }
41 }
42
43 fn fetch_url(&self, url: &str) -> Result<Box<dyn Read + Send + Sync>, ClientError> {
45 let response = self.agent.get(url).call().map_err(|err| HttpError {
46 error: Box::new(err),
47 })?;
48
49 let reader = Box::new(response.into_body().into_reader());
50
51 Ok(reader)
52 }
53}
54
55impl Default for HttpClient {
56 fn default() -> Self {
64 Self::new(DEFAULT_TIMEOUT)
65 }
66}
67
68impl RustReleasesClient for HttpClient {
69 type Error = ClientError;
70
71 fn fetch(&self, resource: ResourceFile) -> Result<RetrievedDocument, Self::Error> {
72 let mut reader = self.fetch_url(resource.url())?;
73
74 let document = write_document(&mut reader)?;
76
77 Ok(RetrievedDocument::new(
78 document,
79 RetrievalLocation::Url(resource.url.to_string()),
80 ))
81 }
82}
83
84fn write_document(reader: &mut Box<dyn Read + Send + Sync>) -> Result<Document, ClientError> {
85 let mut buffer = Vec::with_capacity(DEFAULT_MEMORY_SIZE);
86
87 let bytes_read = reader
88 .read_to_end(&mut buffer)
89 .map_err(IoError::auxiliary)?;
90
91 if bytes_read == 0 {
92 return Err(ClientError::Empty);
93 }
94
95 Ok(Document::new(buffer))
96}
97
98#[derive(Debug, thiserror::Error)]
100#[non_exhaustive]
101pub enum ClientError {
102 #[error("Received empty file")]
104 Empty,
105
106 #[error(transparent)]
108 Http(#[from] HttpError),
109
110 #[error(transparent)]
112 Io(#[from] IoError),
113}