apresolve.rs 2.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. const AP_FALLBACK: &'static str = "ap.spotify.com:443";
  2. const APRESOLVE_ENDPOINT: &'static str = "http://apresolve.spotify.com/";
  3. use futures::{Future, Stream};
  4. use hyper::client::HttpConnector;
  5. use hyper::{self, Client, Method, Request, Uri};
  6. use hyper_proxy::{Intercept, Proxy, ProxyConnector};
  7. use serde_json;
  8. use std::str::FromStr;
  9. use tokio_core::reactor::Handle;
  10. use url::Url;
  11. error_chain!{}
  12. #[derive(Clone, Debug, Serialize, Deserialize)]
  13. pub struct APResolveData {
  14. ap_list: Vec<String>,
  15. }
  16. fn apresolve(handle: &Handle, proxy: &Option<Url>) -> Box<Future<Item = String, Error = Error>> {
  17. let url = Uri::from_str(APRESOLVE_ENDPOINT).expect("invalid AP resolve URL");
  18. let use_proxy = proxy.is_some();
  19. let mut req = Request::new(Method::Get, url.clone());
  20. let response = match *proxy {
  21. Some(ref val) => {
  22. let proxy_url = Uri::from_str(val.as_str()).expect("invalid http proxy");
  23. let proxy = Proxy::new(Intercept::All, proxy_url);
  24. let connector = HttpConnector::new(4, handle);
  25. let proxy_connector = ProxyConnector::from_proxy_unsecured(connector, proxy);
  26. if let Some(headers) = proxy_connector.http_headers(&url) {
  27. req.headers_mut().extend(headers.iter());
  28. req.set_proxy(true);
  29. }
  30. let client = Client::configure().connector(proxy_connector).build(handle);
  31. client.request(req)
  32. }
  33. _ => {
  34. let client = Client::new(handle);
  35. client.request(req)
  36. }
  37. };
  38. let body = response.and_then(|response| {
  39. response.body().fold(Vec::new(), |mut acc, chunk| {
  40. acc.extend_from_slice(chunk.as_ref());
  41. Ok::<_, hyper::Error>(acc)
  42. })
  43. });
  44. let body = body.then(|result| result.chain_err(|| "HTTP error"));
  45. let body = body.and_then(|body| String::from_utf8(body).chain_err(|| "invalid UTF8 in response"));
  46. let data =
  47. body.and_then(|body| serde_json::from_str::<APResolveData>(&body).chain_err(|| "invalid JSON"));
  48. let ap = data.and_then(move |data| {
  49. let mut aps = data.ap_list.iter().filter(|ap| {
  50. if use_proxy {
  51. // It is unlikely that the proxy will accept CONNECT on anything other than 443.
  52. Uri::from_str(ap)
  53. .ok()
  54. .map_or(false, |uri| uri.port().map_or(false, |port| port == 443))
  55. } else {
  56. true
  57. }
  58. });
  59. let ap = aps.next().ok_or("empty AP List")?;
  60. Ok(ap.clone())
  61. });
  62. Box::new(ap)
  63. }
  64. pub(crate) fn apresolve_or_fallback<E>(
  65. handle: &Handle,
  66. proxy: &Option<Url>,
  67. ) -> Box<Future<Item = String, Error = E>>
  68. where
  69. E: 'static,
  70. {
  71. let ap = apresolve(handle, proxy).or_else(|e| {
  72. warn!("Failed to resolve Access Point: {}", e.description());
  73. warn!("Using fallback \"{}\"", AP_FALLBACK);
  74. Ok(AP_FALLBACK.into())
  75. });
  76. Box::new(ap)
  77. }