apresolve.rs 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  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<dyn 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 =
  50. body.and_then(|body| String::from_utf8(body).chain_err(|| "invalid UTF8 in response"));
  51. let data = body
  52. .and_then(|body| serde_json::from_str::<APResolveData>(&body).chain_err(|| "invalid JSON"));
  53. let p = ap_port.clone();
  54. let ap = data.and_then(move |data| {
  55. let mut aps = data.ap_list.iter().filter(|ap| {
  56. if p.is_some() {
  57. Uri::from_str(ap).ok().map_or(false, |uri| {
  58. uri.port().map_or(false, |port| port == p.unwrap())
  59. })
  60. } else if use_proxy {
  61. // It is unlikely that the proxy will accept CONNECT on anything other than 443.
  62. Uri::from_str(ap)
  63. .ok()
  64. .map_or(false, |uri| uri.port().map_or(false, |port| port == 443))
  65. } else {
  66. true
  67. }
  68. });
  69. let ap = aps.next().ok_or("empty AP List")?;
  70. Ok(ap.clone())
  71. });
  72. Box::new(ap)
  73. }
  74. pub(crate) fn apresolve_or_fallback<E>(
  75. handle: &Handle,
  76. proxy: &Option<Url>,
  77. ap_port: &Option<u16>,
  78. ) -> Box<dyn Future<Item = String, Error = E>>
  79. where
  80. E: 'static,
  81. {
  82. let ap = apresolve(handle, proxy, ap_port).or_else(|e| {
  83. warn!("Failed to resolve Access Point: {}", e.description());
  84. warn!("Using fallback \"{}\"", AP_FALLBACK);
  85. Ok(AP_FALLBACK.into())
  86. });
  87. Box::new(ap)
  88. }