2016-12-30 11:01:43 +00:00
|
|
|
const AP_FALLBACK : &'static str = "ap.spotify.com:80";
|
2017-01-18 15:17:10 +00:00
|
|
|
const APRESOLVE_ENDPOINT : &'static str = "http://apresolve.spotify.com/";
|
2016-03-13 20:03:09 +00:00
|
|
|
|
2017-01-18 15:17:10 +00:00
|
|
|
use futures::{Future, Stream};
|
|
|
|
use hyper::{self, Url, Client};
|
2016-07-06 01:29:38 +00:00
|
|
|
use serde_json;
|
2017-01-18 15:17:10 +00:00
|
|
|
use tokio_core::reactor::Handle;
|
2016-03-13 20:03:09 +00:00
|
|
|
|
2017-01-18 15:17:10 +00:00
|
|
|
error_chain! { }
|
2017-01-19 22:45:24 +00:00
|
|
|
|
2016-07-06 01:29:38 +00:00
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
2016-03-13 20:03:09 +00:00
|
|
|
pub struct APResolveData {
|
|
|
|
ap_list: Vec<String>
|
|
|
|
}
|
|
|
|
|
2017-01-18 15:17:10 +00:00
|
|
|
pub fn apresolve(handle: &Handle) -> Box<Future<Item=String, Error=Error>> {
|
|
|
|
let url = Url::parse(APRESOLVE_ENDPOINT).expect("invalid AP resolve URL");
|
|
|
|
|
|
|
|
let client = Client::new(&handle);
|
|
|
|
let response = client.get(url);
|
|
|
|
|
|
|
|
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 ap = data.and_then(|data| {
|
|
|
|
let ap = data.ap_list.first().ok_or("empty AP List")?;
|
|
|
|
Ok(ap.clone())
|
|
|
|
});
|
|
|
|
|
|
|
|
Box::new(ap)
|
|
|
|
}
|
2016-12-31 13:30:01 +00:00
|
|
|
|
2017-01-18 15:17:10 +00:00
|
|
|
pub fn apresolve_or_fallback<E>(handle: &Handle)
|
|
|
|
-> Box<Future<Item=String, Error=E>>
|
|
|
|
where E: 'static
|
|
|
|
{
|
|
|
|
let ap = apresolve(handle).or_else(|e| {
|
|
|
|
warn!("Failed to resolve Access Point: {}", e.description());
|
|
|
|
warn!("Using fallback \"{}\"", AP_FALLBACK);
|
|
|
|
Ok(AP_FALLBACK.into())
|
|
|
|
});
|
2016-03-13 20:03:09 +00:00
|
|
|
|
2017-01-18 15:17:10 +00:00
|
|
|
Box::new(ap)
|
2016-03-13 20:03:09 +00:00
|
|
|
}
|