1 use crate::error::Error; 2 use crate::globals; 3 4 use fractal_api::reqwest; 5 use gio::prelude::*; 6 7 use std::sync::Mutex; 8 use std::time::Duration; 9 10 // Special URI used by gio to indicate no proxy 11 const PROXY_DIRECT_URI: &str = "direct://"; 12 13 #[derive(Debug, Default, Eq, PartialEq)] 14 pub struct ProxySettings { 15 http_proxy: Vec<String>, 16 https_proxy: Vec<String>, 17 } 18 19 impl ProxySettings { new<I, T>(http_proxy: T, https_proxy: T) -> Self where I: PartialEq<str> + Into<String>, T: IntoIterator<Item = I>,20 fn new<I, T>(http_proxy: T, https_proxy: T) -> Self 21 where 22 I: PartialEq<str> + Into<String>, 23 T: IntoIterator<Item = I>, 24 { 25 Self { 26 http_proxy: http_proxy 27 .into_iter() 28 .filter(|proxy| proxy != PROXY_DIRECT_URI) 29 .map(Into::into) 30 .collect(), 31 https_proxy: https_proxy 32 .into_iter() 33 .filter(|proxy| proxy != PROXY_DIRECT_URI) 34 .map(Into::into) 35 .collect(), 36 } 37 } 38 current() -> Result<Self, Error>39 pub fn current() -> Result<Self, Error> { 40 Ok(Self::new( 41 PROXY_RESOLVER.with(|resolver| resolver.lookup("http://", gio::NONE_CANCELLABLE))?, 42 PROXY_RESOLVER.with(|resolver| resolver.lookup("https://", gio::NONE_CANCELLABLE))?, 43 )) 44 } 45 apply_to_client_builder( &self, mut builder: fractal_api::reqwest::blocking::ClientBuilder, ) -> fractal_api::reqwest::blocking::ClientBuilder46 pub fn apply_to_client_builder( 47 &self, 48 mut builder: fractal_api::reqwest::blocking::ClientBuilder, 49 ) -> fractal_api::reqwest::blocking::ClientBuilder { 50 // Reqwest only supports one proxy for each type 51 if let Some(http_proxy) = self 52 .http_proxy 53 .get(0) 54 .map(reqwest::Proxy::http) 55 .and_then(Result::ok) 56 { 57 builder = builder.proxy(http_proxy); 58 } 59 if let Some(https_proxy) = self 60 .https_proxy 61 .get(0) 62 .map(reqwest::Proxy::http) 63 .and_then(Result::ok) 64 { 65 builder = builder.proxy(https_proxy); 66 } 67 68 builder 69 } 70 } 71 72 // gio::ProxyResolver can't be sent or shared 73 thread_local! { 74 static PROXY_RESOLVER: gio::ProxyResolver = 75 gio::ProxyResolver::get_default().expect("Couldn't get proxy resolver"); 76 } 77 78 #[derive(Debug)] 79 struct ClientInner { 80 client: fractal_api::reqwest::blocking::Client, 81 proxy_settings: ProxySettings, 82 } 83 84 #[derive(Debug)] 85 pub struct Client { 86 inner: Mutex<ClientInner>, 87 } 88 89 impl Client { new() -> Client90 pub fn new() -> Client { 91 Client { 92 inner: Mutex::new(ClientInner { 93 client: Self::build(fractal_api::reqwest::blocking::Client::builder()), 94 proxy_settings: Default::default(), 95 }), 96 } 97 } 98 get_client(&self) -> fractal_api::reqwest::blocking::Client99 pub fn get_client(&self) -> fractal_api::reqwest::blocking::Client { 100 // Lock first so we don't overwrite proxy settings with outdated information 101 let mut inner = self.inner.lock().unwrap(); 102 103 let new_proxy_settings = ProxySettings::current().unwrap_or_default(); 104 105 if inner.proxy_settings != new_proxy_settings { 106 let mut builder = fractal_api::reqwest::blocking::Client::builder(); 107 builder = new_proxy_settings.apply_to_client_builder(builder); 108 let client = Self::build(builder); 109 110 inner.client = client; 111 inner.proxy_settings = new_proxy_settings; 112 } 113 114 inner.client.clone() 115 } 116 build( builder: fractal_api::reqwest::blocking::ClientBuilder, ) -> fractal_api::reqwest::blocking::Client117 fn build( 118 builder: fractal_api::reqwest::blocking::ClientBuilder, 119 ) -> fractal_api::reqwest::blocking::Client { 120 builder 121 .gzip(true) 122 .timeout(Duration::from_secs(globals::TIMEOUT)) 123 .build() 124 .expect("Couldn't create a http client") 125 } 126 } 127