123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101 |
- const AP_FALLBACK: &'static str = "ap.spotify.com:443";
- const APRESOLVE_ENDPOINT: &'static str = "http://apresolve.spotify.com/";
- use futures::{Future, Stream};
- use hyper::client::HttpConnector;
- use hyper::{self, Client, Method, Request, Uri};
- use hyper_proxy::{Intercept, Proxy, ProxyConnector};
- use serde_json;
- use std::str::FromStr;
- use tokio_core::reactor::Handle;
- use url::Url;
- error_chain! {}
- #[derive(Clone, Debug, Serialize, Deserialize)]
- pub struct APResolveData {
- ap_list: Vec<String>,
- }
- fn apresolve(
- handle: &Handle,
- proxy: &Option<Url>,
- ap_port: &Option<u16>,
- ) -> Box<dyn Future<Item = String, Error = Error>> {
- let url = Uri::from_str(APRESOLVE_ENDPOINT).expect("invalid AP resolve URL");
- let use_proxy = proxy.is_some();
- let mut req = Request::new(Method::Get, url.clone());
- let response = match *proxy {
- Some(ref val) => {
- let proxy_url = Uri::from_str(val.as_str()).expect("invalid http proxy");
- 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)
- }
- };
- 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"));
- let body =
- body.and_then(|body| String::from_utf8(body).chain_err(|| "invalid UTF8 in response"));
- let data = body
- .and_then(|body| serde_json::from_str::<APResolveData>(&body).chain_err(|| "invalid JSON"));
- let p = ap_port.clone();
- let ap = data.and_then(move |data| {
- let mut aps = data.ap_list.iter().filter(|ap| {
- if p.is_some() {
- Uri::from_str(ap).ok().map_or(false, |uri| {
- uri.port().map_or(false, |port| port == p.unwrap())
- })
- } else if use_proxy {
- // 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")?;
- Ok(ap.clone())
- });
- Box::new(ap)
- }
- pub(crate) fn apresolve_or_fallback<E>(
- handle: &Handle,
- proxy: &Option<Url>,
- ap_port: &Option<u16>,
- ) -> Box<dyn Future<Item = String, Error = E>>
- where
- E: 'static,
- {
- let ap = apresolve(handle, proxy, ap_port).or_else(|e| {
- warn!("Failed to resolve Access Point: {}", e.description());
- warn!("Using fallback \"{}\"", AP_FALLBACK);
- Ok(AP_FALLBACK.into())
- });
- Box::new(ap)
- }
|