apresolve.rs 1.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657
  1. const AP_FALLBACK : &'static str = "ap.spotify.com:80";
  2. const APRESOLVE_ENDPOINT : &'static str = "http://apresolve.spotify.com/";
  3. use std::str::FromStr;
  4. use futures::{Future, Stream};
  5. use hyper::{self, Uri, Client};
  6. use serde_json;
  7. use tokio_core::reactor::Handle;
  8. error_chain! { }
  9. #[derive(Clone, Debug, Serialize, Deserialize)]
  10. pub struct APResolveData {
  11. ap_list: Vec<String>
  12. }
  13. fn apresolve(handle: &Handle) -> Box<Future<Item=String, Error=Error>> {
  14. let url = Uri::from_str(APRESOLVE_ENDPOINT).expect("invalid AP resolve URL");
  15. let client = Client::new(handle);
  16. let response = client.get(url);
  17. let body = response.and_then(|response| {
  18. response.body().fold(Vec::new(), |mut acc, chunk| {
  19. acc.extend_from_slice(chunk.as_ref());
  20. Ok::<_, hyper::Error>(acc)
  21. })
  22. });
  23. let body = body.then(|result| result.chain_err(|| "HTTP error"));
  24. let body = body.and_then(|body| {
  25. String::from_utf8(body).chain_err(|| "invalid UTF8 in response")
  26. });
  27. let data = body.and_then(|body| {
  28. serde_json::from_str::<APResolveData>(&body)
  29. .chain_err(|| "invalid JSON")
  30. });
  31. let ap = data.and_then(|data| {
  32. let ap = data.ap_list.first().ok_or("empty AP List")?;
  33. Ok(ap.clone())
  34. });
  35. Box::new(ap)
  36. }
  37. pub(crate) fn apresolve_or_fallback<E>(handle: &Handle)
  38. -> Box<Future<Item=String, Error=E>>
  39. where E: 'static {
  40. let ap = apresolve(handle).or_else(|e| {
  41. warn!("Failed to resolve Access Point: {}", e.description());
  42. warn!("Using fallback \"{}\"", AP_FALLBACK);
  43. Ok(AP_FALLBACK.into())
  44. });
  45. Box::new(ap)
  46. }