env_variables/
lib.rs

1//! Extract different env variables.
2
3#[cfg(test)]
4#[macro_use]
5extern crate lazy_static;
6extern crate url;
7
8use std::env::var_os;
9use url::Url;
10
11fn is_no_proxy(url: &Url) -> bool {
12	let maybe_no_proxy = var_os("no_proxy")
13		.or_else(|| var_os("NO_PROXY"))
14		.map(|v| v.to_str().unwrap_or("").to_string());
15
16	if let Some(no_proxy) = maybe_no_proxy {
17		if no_proxy == "*" {
18			return true;
19		}
20		if let Some(host) = url.host_str() {
21			for elem in no_proxy.split(|c| c == ',' || c == ' ') {
22				if host.ends_with(elem) {
23					return true;
24				}
25			}
26		}
27	}
28	false
29}
30
31/// Extract proxy parameters for a URL by examining the environment variables.
32///
33/// Most environment variables described here can be defined either with an all-lowercase or an
34/// all-uppercase name. If both versions are defined, the all-lowercase name takes precedence
35///
36/// If __no_proxy__ is defined, check the host part of the URL against its components and return
37/// `None` if there is any match. The value of __no_proxy__ should be a space- or comma-separated
38/// list of host/domain names or IP addresses for which no proxying should be done, or a single
39/// ' __*__ ' (asterisk) which means that proxying is disabled for all hosts.
40///
41/// If the port is not explicitly defined in the proxy URL, the value 8080 is used.
42pub fn for_url(s: &str) -> Option<String> {
43	let url = if let Ok(u) = Url::parse(s) {
44		u
45	} else {
46		return None;
47	};
48
49	if is_no_proxy(&url) {
50		return None;
51	}
52
53	let maybe_https_proxy = var_os("https_proxy")
54		.or_else(|| var_os("HTTPS_PROXY"))
55		.map(|v| v.to_str().unwrap_or("").to_string());
56	let maybe_ftp_proxy = var_os("ftp_proxy")
57		.or_else(|| var_os("FTP_PROXY"))
58		.map(|v| v.to_str().unwrap_or("").to_string());
59	let maybe_http_proxy = var_os("http_proxy")
60		.or_else(|| var_os("HTTP_PROXY"))
61		.map(|v| v.to_str().unwrap_or("").to_string());
62	let maybe_all_proxy = var_os("all_proxy")
63		.or_else(|| var_os("ALL_PROXY"))
64		.map(|v| v.to_str().unwrap_or("").to_string());
65
66	if let Some(url_value) = match url.scheme() {
67		"https" => maybe_https_proxy.or(maybe_http_proxy.or(maybe_all_proxy)),
68		"http" => maybe_http_proxy.or(maybe_all_proxy),
69		"ftp" => maybe_ftp_proxy.or(maybe_http_proxy.or(maybe_all_proxy)),
70		_ => maybe_all_proxy,
71	} {
72		if let Ok(mut proxy_url) = Url::parse(&url_value) {
73			if proxy_url.host_str().is_some() {
74				if proxy_url.port().is_some() {
75					return Some(url_value);
76				} else {
77					if proxy_url.set_port(Some(8080)).is_ok() {
78						return Some(proxy_url.as_str().to_string());
79					}
80				}
81			}
82		}
83	}
84	None
85}
86
87#[cfg(test)]
88mod tests {
89	use std::env::{remove_var, set_var};
90	use std::sync::Mutex;
91	use super::*;
92
93	// environment is per-process, and we need it stable per-thread,
94	// hence locking
95	lazy_static! {
96	static ref LOCK: Mutex<()> = Mutex::new(());
97	}
98
99	fn scrub_env() {
100		remove_var("http_proxy");
101		remove_var("https_proxy");
102		remove_var("HTTPS_PROXY");
103		remove_var("ftp_proxy");
104		remove_var("FTP_PROXY");
105		remove_var("all_proxy");
106		remove_var("ALL_PROXY");
107		remove_var("no_proxy");
108		remove_var("NO_PROXY");
109	}
110
111	#[test]
112	fn no_proxy_simple_name() {
113		let _l = LOCK.lock();
114		scrub_env();
115		set_var("no_proxy", "example.org");
116		set_var("http_proxy", "http://proxy.example.com:8080");
117		assert!(for_url("http://example.org").is_none());
118	}
119
120	#[test]
121	fn no_proxy_global() {
122		let _l = LOCK.lock();
123		scrub_env();
124		set_var("no_proxy", "*");
125		set_var("http_proxy", "http://proxy.example.com:8080");
126		assert!(for_url("http://example.org").is_none());
127	}
128
129	#[test]
130	fn no_proxy_subdomain() {
131		let _l = LOCK.lock();
132		scrub_env();
133		set_var("no_proxy", "example.org");
134		set_var("http_proxy", "http://proxy.example.com:8080");
135		assert!(for_url("http://www.example.org").is_none());
136	}
137
138	#[test]
139	fn no_proxy_subdomain_dot() {
140		let _l = LOCK.lock();
141		scrub_env();
142		set_var("no_proxy", "example.org");
143		set_var("http_proxy", "http://proxy.example.com:8080");
144		assert!(for_url("http://www.example.org").is_none());
145	}
146
147	#[test]
148	fn no_proxy_multiple_list() {
149		let _l = LOCK.lock();
150		scrub_env();
151		set_var(
152			"no_proxy",
153			"www.example.org,www.example1.org,www.example.org",
154		);
155		set_var("http_proxy", "http://proxy.example.com:8080");
156		assert!(for_url("http://www.example.org").is_none());
157	}
158
159	#[test]
160	fn http_proxy_specific() {
161		let _l = LOCK.lock();
162		scrub_env();
163		set_var("http_proxy", "http://proxy.example.com:8080");
164		set_var("all_proxy", "http://proxy.example.org:8081");
165		assert_eq!(
166			for_url("http://www.example.org"),
167			Some(("http://proxy.example.com:8080".to_string()))
168		);
169	}
170
171	#[test]
172	fn http_proxy_fallback() {
173		let _l = LOCK.lock();
174		scrub_env();
175		set_var("ALL_PROXY", "http://proxy.example.com:8080");
176		assert_eq!(
177			for_url("http://www.example.org"),
178			Some(("http://proxy.example.com:8080".to_string()))
179		);
180		set_var("all_proxy", "http://proxy.example.org:8081");
181		assert_eq!(
182			for_url("http://www.example.org"),
183			Some(("http://proxy.example.org:8081".to_string()))
184		);
185	}
186
187	#[test]
188	fn https_proxy_specific() {
189		let _l = LOCK.lock();
190		scrub_env();
191		set_var("HTTPS_PROXY", "http://proxy.example.com:8080");
192		set_var("http_proxy", "http://proxy.example.org:8081");
193		set_var("all_proxy", "http://proxy.example.org:8081");
194		assert_eq!(
195			Some(("http://proxy.example.com:8080".to_string())),
196			for_url("https://www.example.org")
197		);
198		set_var("https_proxy", "http://proxy.example.com:8081");
199		assert_eq!(
200			for_url("https://www.example.org"),
201			Some(("http://proxy.example.com:8081".to_string()))
202		);
203	}
204
205	#[test]
206	fn https_proxy_fallback() {
207		let _l = LOCK.lock();
208		scrub_env();
209		set_var("http_proxy", "http://proxy.example.com:8080");
210		set_var("ALL_PROXY", "http://proxy.example.org:8081");
211		assert_eq!(
212			for_url("https://www.example.org"),
213			Some(("http://proxy.example.com:8080".to_string()))
214		);
215		remove_var("http_proxy");
216		assert_eq!(
217			for_url("https://www.example.org"),
218			Some(("http://proxy.example.org:8081".to_string()))
219		);
220		set_var("all_proxy", "http://proxy.example.org:8082");
221		assert_eq!(
222			for_url("https://www.example.org"),
223			Some(("http://proxy.example.org:8082".to_string()))
224		);
225	}
226
227	#[test]
228	fn ftp_proxy_specific() {
229		let _l = LOCK.lock();
230		scrub_env();
231		set_var("FTP_PROXY", "http://proxy.example.com:8080");
232		set_var("http_proxy", "http://proxy.example.org:8081");
233		set_var("all_proxy", "http://proxy.example.org:8081");
234		assert_eq!(
235			for_url("ftp://www.example.org"),
236			Some(("http://proxy.example.com:8080".to_string()))
237		);
238		set_var("ftp_proxy", "http://proxy.example.com:8081");
239		assert_eq!(
240			for_url("ftp://www.example.org"),
241			Some(("http://proxy.example.com:8081".to_string()))
242		);
243	}
244
245	#[test]
246	fn ftp_proxy_fallback() {
247		let _l = LOCK.lock();
248		scrub_env();
249		set_var("http_proxy", "http://proxy.example.com:8080");
250		set_var("ALL_PROXY", "http://proxy.example.org:8081");
251		assert_eq!(
252			for_url("ftp://www.example.org"),
253			Some(("http://proxy.example.com:8080".to_string()))
254		);
255		remove_var("http_proxy");
256		assert_eq!(
257			for_url("ftp://www.example.org"),
258			Some(("http://proxy.example.org:8081".to_string()))
259		);
260		set_var("all_proxy", "http://proxy.example.org:8082");
261		assert_eq!(
262			for_url("ftp://www.example.org"),
263			Some(("http://proxy.example.org:8082".to_string()))
264		);
265	}
266}