minecraft_java_rs_core/net/
client.rs1use std::net::SocketAddr;
4use std::sync::Arc;
5use std::time::Duration;
6
7use reqwest::dns::{Addrs, Name, Resolve, Resolving};
8
9#[derive(Debug, Default)]
18struct Ipv4OnlyResolver;
19
20impl Resolve for Ipv4OnlyResolver {
21 fn resolve(&self, name: Name) -> Resolving {
22 let host = name.as_str().to_owned();
23 Box::pin(async move {
24 let addrs = tokio::net::lookup_host((host.as_str(), 0)).await?;
26 let v4: Vec<SocketAddr> = addrs.filter(SocketAddr::is_ipv4).collect();
27 Ok(Box::new(v4.into_iter()) as Addrs)
28 })
29 }
30}
31
32pub fn build_client(timeout_secs: u64, force_ipv4: bool) -> reqwest::Result<reqwest::Client> {
37 let mut builder = reqwest::Client::builder().timeout(Duration::from_secs(timeout_secs));
38 if force_ipv4 {
39 builder = builder.dns_resolver(Arc::new(Ipv4OnlyResolver));
40 }
41 builder.build()
42}
43
44pub fn describe_reqwest_error(err: &reqwest::Error) -> String {
53 if let Some(status) = err.status() {
54 let reason = status.canonical_reason().unwrap_or("unknown");
55 return format!("HTTP {status} {reason}");
56 }
57
58 let kind = if err.is_timeout() {
59 "connection timed out"
60 } else if err.is_connect() {
61 "could not establish connection"
62 } else if err.is_request() {
63 "request could not be sent"
64 } else {
65 "network error"
66 };
67
68 let mut root: Option<String> = None;
71 let mut src = std::error::Error::source(err);
72 while let Some(e) = src {
73 root = Some(e.to_string());
74 src = e.source();
75 }
76
77 match root {
78 Some(cause) => format!("{kind} ({cause})"),
79 None => kind.to_string(),
80 }
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86
87 #[test]
88 fn build_client_succeeds_in_both_modes() {
89 assert!(build_client(10, false).is_ok());
90 assert!(build_client(10, true).is_ok());
91 }
92}