Skip to content

Commit a107b77

Browse files
authored
feat(downloader): allow remote::Client to be customised
This is for usage outside of binstall itself. Sometimes it's useful to be able to specify more reqwest options, such as doing custom DNS resolution. This leaves the existing API identical but adds a couple methods that make it possible to construct a client with custom options. Signed-off-by: Félix Saparelli <felix@passcod.name>
1 parent f68f769 commit a107b77

File tree

1 file changed

+68
-47
lines changed

1 file changed

+68
-47
lines changed

crates/binstalk-downloader/src/remote.rs

+68-47
Original file line numberDiff line numberDiff line change
@@ -86,69 +86,90 @@ pub struct Client(Arc<Inner>);
8686

8787
#[cfg_attr(not(feature = "__tls"), allow(unused_variables, unused_mut))]
8888
impl Client {
89+
/// Construct a new downloader client
90+
///
8991
/// * `per_millis` - The duration (in millisecond) for which at most
90-
/// `num_request` can be sent, itcould be increased if rate-limit
91-
/// happens.
92+
/// `num_request` can be sent. Increase it if rate-limit errors
93+
/// happen.
9294
/// * `num_request` - maximum number of requests to be processed for
93-
/// each `per` duration.
95+
/// each `per_millis` duration.
9496
///
95-
/// The Client created would use at least tls 1.2
97+
/// The [`reqwest::Client`] constructed has secure defaults, such as allowing
98+
/// only TLS v1.2 and above, and disallowing plaintext HTTP altogether. If you
99+
/// need more control, use the `from_builder` variant.
96100
pub fn new(
97101
user_agent: impl AsRef<str>,
98102
min_tls: Option<TLSVersion>,
99103
per_millis: NonZeroU16,
100104
num_request: NonZeroU64,
101105
certificates: impl IntoIterator<Item = Certificate>,
102106
) -> Result<Self, Error> {
103-
fn inner(
104-
user_agent: &str,
105-
min_tls: Option<TLSVersion>,
106-
per_millis: NonZeroU16,
107-
num_request: NonZeroU64,
108-
certificates: &mut dyn Iterator<Item = Certificate>,
109-
) -> Result<Client, Error> {
110-
let mut builder = reqwest::ClientBuilder::new()
111-
.user_agent(user_agent)
112-
.https_only(true)
113-
.tcp_nodelay(false);
114-
115-
#[cfg(feature = "hickory-dns")]
116-
{
117-
builder = builder.dns_resolver(Arc::new(TrustDnsResolver::default()));
118-
}
119-
120-
#[cfg(feature = "__tls")]
121-
{
122-
let tls_ver = min_tls
123-
.map(|tls| tls.max(DEFAULT_MIN_TLS))
124-
.unwrap_or(DEFAULT_MIN_TLS);
107+
let builder = Self::default_builder(
108+
user_agent.as_ref(),
109+
min_tls,
110+
per_millis,
111+
num_request,
112+
&mut certificates.into_iter(),
113+
);
114+
Self::from_builder(builder)
115+
}
125116

126-
builder = builder.min_tls_version(tls_ver.into());
117+
/// Constructs a default [`reqwest::ClientBuilder`].
118+
///
119+
/// This may be used alongside [`Client::from_builder`] to start from reasonable
120+
/// defaults, but still be able to customise the reqwest instance. Arguments are
121+
/// as [`Client::new`], but without generic parameters.
122+
pub fn default_builder(
123+
user_agent: &str,
124+
min_tls: Option<TLSVersion>,
125+
per_millis: NonZeroU16,
126+
num_request: NonZeroU64,
127+
certificates: &mut dyn Iterator<Item = Certificate>,
128+
) -> reqwest::ClientBuilder {
129+
let mut builder = reqwest::ClientBuilder::new()
130+
.user_agent(user_agent)
131+
.https_only(true)
132+
.tcp_nodelay(false);
133+
134+
#[cfg(feature = "hickory-dns")]
135+
{
136+
builder = builder.dns_resolver(Arc::new(TrustDnsResolver::default()));
137+
}
127138

128-
for certificate in certificates {
129-
builder = builder.add_root_certificate(certificate.0);
130-
}
131-
}
139+
#[cfg(feature = "__tls")]
140+
{
141+
let tls_ver = min_tls
142+
.map(|tls| tls.max(DEFAULT_MIN_TLS))
143+
.unwrap_or(DEFAULT_MIN_TLS);
132144

133-
let client = builder.build()?;
145+
builder = builder.min_tls_version(tls_ver.into());
134146

135-
Ok(Client(Arc::new(Inner {
136-
client: client.clone(),
137-
service: DelayRequest::new(
138-
num_request,
139-
Duration::from_millis(per_millis.get() as u64),
140-
client,
141-
),
142-
})))
147+
for certificate in certificates {
148+
builder = builder.add_root_certificate(certificate.0);
149+
}
143150
}
144151

145-
inner(
146-
user_agent.as_ref(),
147-
min_tls,
148-
per_millis,
149-
num_request,
150-
&mut certificates.into_iter(),
151-
)
152+
builder
153+
}
154+
155+
/// Construct a custom client from a [`reqwest::ClientBuilder`].
156+
///
157+
/// You may want to also use [`Client::default_builder`].
158+
pub fn from_builder(
159+
builder: reqwest::ClientBuilder,
160+
per_millis: NonZeroU16,
161+
num_request: NonZeroU64,
162+
) -> Result<Self, Error> {
163+
let client = builder.build()?;
164+
165+
Ok(Client(Arc::new(Inner {
166+
client: client.clone(),
167+
service: DelayRequest::new(
168+
num_request,
169+
Duration::from_millis(per_millis.get() as u64),
170+
client,
171+
),
172+
})))
152173
}
153174

154175
/// Return inner reqwest client.

0 commit comments

Comments
 (0)