2018-03-23 05:15:15 +00:00
|
|
|
const AP_FALLBACK: &'static str = "ap.spotify.com:443";
|
2018-02-11 11:37:08 +00:00
|
|
|
const APRESOLVE_ENDPOINT: &'static str = "http://apresolve.spotify.com/";
|
2016-03-13 20:03:09 +00:00
|
|
|
|
2018-03-23 15:52:24 +00:00
|
|
|
use futures::{Future, Stream};
|
|
|
|
use hyper::client::HttpConnector;
|
|
|
|
use hyper::{self, Client, Method, Request, Uri};
|
|
|
|
use hyper_proxy::{Intercept, Proxy, ProxyConnector};
|
2016-07-06 01:29:38 +00:00
|
|
|
use serde_json;
|
2018-02-11 11:37:08 +00:00
|
|
|
use std::str::FromStr;
|
2017-01-18 15:17:10 +00:00
|
|
|
use tokio_core::reactor::Handle;
|
2018-03-24 08:00:38 +00:00
|
|
|
use url::Url;
|
2016-03-13 20:03:09 +00:00
|
|
|
|
2019-10-08 09:31:18 +00:00
|
|
|
error_chain! {}
|
2017-01-19 22:45:24 +00:00
|
|
|
|
2016-07-06 01:29:38 +00:00
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
2016-03-13 20:03:09 +00:00
|
|
|
pub struct APResolveData {
|
2018-02-11 11:37:08 +00:00
|
|
|
ap_list: Vec<String>,
|
2016-03-13 20:03:09 +00:00
|
|
|
}
|
|
|
|
|
2018-07-03 11:09:22 +00:00
|
|
|
fn apresolve(
|
|
|
|
handle: &Handle,
|
|
|
|
proxy: &Option<Url>,
|
|
|
|
ap_port: &Option<u16>,
|
2019-10-08 09:31:18 +00:00
|
|
|
) -> Box<dyn Future<Item = String, Error = Error>> {
|
2017-04-29 10:41:48 +00:00
|
|
|
let url = Uri::from_str(APRESOLVE_ENDPOINT).expect("invalid AP resolve URL");
|
2018-03-23 15:52:24 +00:00
|
|
|
let use_proxy = proxy.is_some();
|
2017-01-18 15:17:10 +00:00
|
|
|
|
2018-03-23 15:52:24 +00:00
|
|
|
let mut req = Request::new(Method::Get, url.clone());
|
2018-03-24 08:00:38 +00:00
|
|
|
let response = match *proxy {
|
|
|
|
Some(ref val) => {
|
|
|
|
let proxy_url = Uri::from_str(val.as_str()).expect("invalid http proxy");
|
2018-03-23 15:52:24 +00:00
|
|
|
let proxy = Proxy::new(Intercept::All, proxy_url);
|
|
|
|
let connector = HttpConnector::new(4, handle);
|
|
|
|
let proxy_connector = ProxyConnector::from_proxy_unsecured(connector, proxy);
|
|
|
|
if let Some(headers) = proxy_connector.http_headers(&url) {
|
|
|
|
req.headers_mut().extend(headers.iter());
|
|
|
|
req.set_proxy(true);
|
|
|
|
}
|
|
|
|
let client = Client::configure().connector(proxy_connector).build(handle);
|
|
|
|
client.request(req)
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
let client = Client::new(handle);
|
|
|
|
client.request(req)
|
|
|
|
}
|
|
|
|
};
|
2017-01-18 15:17:10 +00:00
|
|
|
|
|
|
|
let body = response.and_then(|response| {
|
|
|
|
response.body().fold(Vec::new(), |mut acc, chunk| {
|
|
|
|
acc.extend_from_slice(chunk.as_ref());
|
|
|
|
Ok::<_, hyper::Error>(acc)
|
|
|
|
})
|
|
|
|
});
|
|
|
|
let body = body.then(|result| result.chain_err(|| "HTTP error"));
|
2019-10-08 09:31:18 +00:00
|
|
|
let body =
|
|
|
|
body.and_then(|body| String::from_utf8(body).chain_err(|| "invalid UTF8 in response"));
|
2017-01-18 15:17:10 +00:00
|
|
|
|
2019-10-08 09:31:18 +00:00
|
|
|
let data = body
|
|
|
|
.and_then(|body| serde_json::from_str::<APResolveData>(&body).chain_err(|| "invalid JSON"));
|
2017-01-18 15:17:10 +00:00
|
|
|
|
2018-07-03 11:09:22 +00:00
|
|
|
let p = ap_port.clone();
|
|
|
|
|
2018-03-23 15:52:24 +00:00
|
|
|
let ap = data.and_then(move |data| {
|
|
|
|
let mut aps = data.ap_list.iter().filter(|ap| {
|
2018-07-03 11:09:22 +00:00
|
|
|
if p.is_some() {
|
2019-10-08 09:31:18 +00:00
|
|
|
Uri::from_str(ap).ok().map_or(false, |uri| {
|
|
|
|
uri.port().map_or(false, |port| port == p.unwrap())
|
|
|
|
})
|
2018-07-03 11:09:22 +00:00
|
|
|
} else if use_proxy {
|
2018-03-23 15:52:24 +00:00
|
|
|
// It is unlikely that the proxy will accept CONNECT on anything other than 443.
|
|
|
|
Uri::from_str(ap)
|
|
|
|
.ok()
|
|
|
|
.map_or(false, |uri| uri.port().map_or(false, |port| port == 443))
|
|
|
|
} else {
|
|
|
|
true
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
let ap = aps.next().ok_or("empty AP List")?;
|
2017-01-18 15:17:10 +00:00
|
|
|
Ok(ap.clone())
|
|
|
|
});
|
|
|
|
|
|
|
|
Box::new(ap)
|
|
|
|
}
|
2016-12-31 13:30:01 +00:00
|
|
|
|
2018-03-23 05:15:15 +00:00
|
|
|
pub(crate) fn apresolve_or_fallback<E>(
|
|
|
|
handle: &Handle,
|
2018-03-24 08:00:38 +00:00
|
|
|
proxy: &Option<Url>,
|
2018-07-03 11:09:22 +00:00
|
|
|
ap_port: &Option<u16>,
|
2019-10-08 09:31:18 +00:00
|
|
|
) -> Box<dyn Future<Item = String, Error = E>>
|
2018-02-11 11:37:08 +00:00
|
|
|
where
|
|
|
|
E: 'static,
|
|
|
|
{
|
2018-07-03 11:09:22 +00:00
|
|
|
let ap = apresolve(handle, proxy, ap_port).or_else(|e| {
|
2018-03-23 15:52:24 +00:00
|
|
|
warn!("Failed to resolve Access Point: {}", e.description());
|
|
|
|
warn!("Using fallback \"{}\"", AP_FALLBACK);
|
|
|
|
Ok(AP_FALLBACK.into())
|
|
|
|
});
|
|
|
|
|
|
|
|
Box::new(ap)
|
2016-03-13 20:03:09 +00:00
|
|
|
}
|