1use std::env;
2use std::time::Duration;
3use once_cell::sync::Lazy;
4use reqwest::Client;
5use tokio::fs;
6use thiserror::Error;
7
8static APP_USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"));
10
11pub static HTTP_CLIENT: Lazy<Client> = Lazy::new(|| {
13 Client::builder()
14 .pool_max_idle_per_host(100)
16 .pool_idle_timeout(Some(Duration::from_secs(90)))
17
18 .http2_initial_stream_window_size(Some(2 * 1024 * 1024))
20 .http2_initial_connection_window_size(Some(4 * 1024 * 1024))
21 .http2_adaptive_window(true)
22 .http2_max_frame_size(Some(16 * 1024))
23
24 .tcp_keepalive(Some(Duration::from_secs(60)))
26 .tcp_nodelay(true)
27
28 .timeout(Duration::from_secs(60))
30 .connect_timeout(Duration::from_secs(5))
31
32 .zstd(true)
34 .gzip(true)
35 .brotli(true)
36
37 .build()
38 .expect("Failed to build HTTP client with default configuration - this should never fail")
39});
40
41
42#[cfg(target_os = "windows")]
44const HOSTS_PATH: &str = "System32\\drivers\\etc\\hosts";
45
46#[cfg(not(target_os = "windows"))]
47const HOSTS_PATH: &str = "etc/hosts";
48
49const HOSTS: [&str; 3] = [
51 "mojang.com",
52 "minecraft.net",
53 "lightylauncher.fr",
54];
55
56#[derive(Debug, Error)]
58pub enum HostsError {
59 #[error("Failed to read hosts file at {0}")]
60 HostsReadError(String),
61
62 #[error("Hosts file contains blocked entries: {0}")]
63 HostsBlocked(String),
64
65 #[error("I/O error: {0}")]
66 IoError(#[from] std::io::Error),
67}
68
69pub type HostsResult<T> = std::result::Result<T, HostsError>;
70
71pub async fn check_hosts_file() -> HostsResult<()> {
73 let hosts_path = if cfg!(target_os = "windows") {
74 let system_drive = env::var("SystemDrive").unwrap_or("C:".to_string());
75 format!("{}\\{}", system_drive, HOSTS_PATH)
76 } else {
77 format!("/{}", HOSTS_PATH)
78 };
79
80 if !fs::try_exists(&hosts_path).await? {
81 return Ok(());
82 }
83
84 let hosts_file = fs::read_to_string(&hosts_path)
85 .await
86 .map_err(|_| HostsError::HostsReadError(hosts_path.clone()))?;
87
88 let flagged_entries: Vec<_> = hosts_file
89 .lines()
90 .filter(|line| !line.trim_start().starts_with('#'))
91 .flat_map(|line| {
92 let mut parts = line.split_whitespace();
93 let _ip = parts.next();
94 parts.filter(|domain| HOSTS.iter().any(|&entry| domain.contains(entry)))
95 })
96 .map(|s| s.to_string())
97 .collect();
98
99 if !flagged_entries.is_empty() {
100 return Err(HostsError::HostsBlocked(flagged_entries.join("\n")));
101 }
102
103 Ok(())
104}