apresolve.rs 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  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(
  17. handle: &Handle,
  18. proxy: &Option<Url>,
  19. ap_port: &Option<u16>,
  20. ) -> Box<Future<Item = String, Error = Error>> {
  21. let url = Uri::from_str(APRESOLVE_ENDPOINT).expect("invalid AP resolve URL");
  22. let use_proxy = proxy.is_some();
  23. let mut req = Request::new(Method::Get, url.clone());
  24. let response = match *proxy {
  25. Some(ref val) => {
  26. let proxy_url = Uri::from_str(val.as_str()).expect("invalid http proxy");
  27. let proxy = Proxy::new(Intercept::All, proxy_url);
  28. let connector = HttpConnector::new(4, handle);
  29. let proxy_connector = ProxyConnector::from_proxy_unsecured(connector, proxy);
  30. if let Some(headers) = proxy_connector.http_headers(&url) {
  31. req.headers_mut().extend(headers.iter());
  32. req.set_proxy(true);
  33. }
  34. let client = Client::configure().connector(proxy_connector).build(handle);
  35. client.request(req)
  36. }
  37. _ => {
  38. let client = Client::new(handle);
  39. client.request(req)
  40. }
  41. };
  42. let body = response.and_then(|response| {
  43. response.body().fold(Vec::new(), |mut acc, chunk| {
  44. acc.extend_from_slice(chunk.as_ref());
  45. Ok::<_, hyper::Error>(acc)
  46. })
  47. });
  48. let body = body.then(|result| result.chain_err(|| "HTTP error"));
  49. let body = body.and_then(|body| String::from_utf8(body).chain_err(|| "invalid UTF8 in response"));
  50. let data =
  51. body.and_then(|body| serde_json::from_str::<APResolveData>(&body).chain_err(|| "invalid JSON"));
  52. let p = ap_port.clone();
  53. let ap = data.and_then(move |data| {
  54. let mut aps = data.ap_list.iter().filter(|ap| {
  55. if p.is_some() {
  56. Uri::from_str(ap)
  57. .ok()
  58. .map_or(false, |uri| uri.port().map_or(false, |port| port == p.unwrap()))
  59. } else if use_proxy {
  60. // It is unlikely that the proxy will accept CONNECT on anything other than 443.
  61. Uri::from_str(ap)
  62. .ok()
  63. .map_or(false, |uri| uri.port().map_or(false, |port| port == 443))
  64. } else {
  65. true
  66. }
  67. });
  68. let ap = aps.next().ok_or("empty AP List")?;
  69. Ok(ap.clone())
  70. });
  71. Box::new(ap)
  72. }
  73. pub(crate) fn apresolve_or_fallback<E>(
  74. handle: &Handle,
  75. proxy: &Option<Url>,
  76. ap_port: &Option<u16>,
  77. ) -> Box<Future<Item = String, Error = E>>
  78. where
  79. E: 'static,
  80. {
  81. let ap = apresolve(handle, proxy, ap_port).or_else(|e| {
  82. warn!("Failed to resolve Access Point: {}", e.description());
  83. warn!("Using fallback \"{}\"", AP_FALLBACK);
  84. Ok(AP_FALLBACK.into())
  85. });
  86. Box::new(ap)
  87. }