Skip to content

Commit

Permalink
Add back hyper-proxy
Browse files Browse the repository at this point in the history
  • Loading branch information
Johannesd3 committed Mar 27, 2021
1 parent 963d50e commit 95fedf5
Show file tree
Hide file tree
Showing 8 changed files with 168 additions and 187 deletions.
101 changes: 63 additions & 38 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ sha-1 = "0.9"

[features]
apresolve = ["librespot-core/apresolve"]
apresolve-http2 = ["librespot-core/apresolve-http2"]

alsa-backend = ["librespot-playback/alsa-backend"]
portaudio-backend = ["librespot-playback/portaudio-backend"]
Expand Down
6 changes: 3 additions & 3 deletions core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,13 @@ aes = "0.6"
base64 = "0.13"
byteorder = "1.4"
bytes = "1.0"
cfg-if = "1"
futures-core = { version = "0.3", default-features = false }
futures-util = { version = "0.3", default-features = false, features = ["alloc", "bilock", "unstable", "sink"] }
hmac = "0.10"
httparse = "1.3"
http = "0.2"
hyper = { version = "0.14", optional = true, features = ["client", "tcp", "http1"] }
hyper-proxy = { version = "0.9.1", optional = true, default-features = false }
log = "0.4"
num-bigint = "0.3"
num-integer = "0.1"
Expand Down Expand Up @@ -50,5 +51,4 @@ env_logger = "*"
tokio = {version = "1.0", features = ["macros"] }

[features]
apresolve = ["hyper"]
apresolve-http2 = ["apresolve", "hyper/http2"]
apresolve = ["hyper", "hyper-proxy"]
111 changes: 53 additions & 58 deletions core/src/apresolve.rs
Original file line number Diff line number Diff line change
@@ -1,73 +1,68 @@
const AP_FALLBACK: &str = "ap.spotify.com:443";
use std::error::Error;

use hyper::client::HttpConnector;
use hyper::{Body, Client, Method, Request, Uri};
use hyper_proxy::{Intercept, Proxy, ProxyConnector};
use serde::Deserialize;
use url::Url;

cfg_if! {
if #[cfg(feature = "apresolve")] {
const APRESOLVE_ENDPOINT: &str = "http://apresolve.spotify.com:80";
use super::AP_FALLBACK;

use std::error::Error;
const APRESOLVE_ENDPOINT: &str = "http://apresolve.spotify.com:80";

use hyper::{Body, Client, Method, Request, Uri};
use serde::{Serialize, Deserialize};

use crate::proxytunnel::ProxyTunnel;

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct APResolveData {
ap_list: Vec<String>,
}
#[derive(Clone, Debug, Deserialize)]
struct APResolveData {
ap_list: Vec<String>,
}

async fn apresolve(proxy: &Option<Url>, ap_port: &Option<u16>) -> Result<String, Box<dyn Error>> {
let port = ap_port.unwrap_or(443);
async fn try_apresolve(
proxy: Option<&Url>,
ap_port: Option<u16>,
) -> Result<String, Box<dyn Error>> {
let port = ap_port.unwrap_or(443);

let req = Request::builder()
.method(Method::GET)
.uri(
APRESOLVE_ENDPOINT
.parse::<Uri>()
.expect("invalid AP resolve URL"),
)
.body(Body::empty())?;
let mut req = Request::new(Body::empty());
*req.method_mut() = Method::GET;
// panic safety: APRESOLVE_ENDPOINT above is valid url.
*req.uri_mut() = APRESOLVE_ENDPOINT.parse().expect("invalid AP resolve URL");

let response = if let Some(url) = proxy {
Client::builder()
.build(ProxyTunnel::new(&url.socket_addrs(|| None)?[..])?)
.request(req)
.await?
} else {
Client::new().request(req).await?
};
let response = if let Some(url) = proxy {
// Panic safety: all URLs are valid URIs
let uri = url.to_string().parse().unwrap();
let proxy = Proxy::new(Intercept::All, uri);
let connector = HttpConnector::new();
let proxy_connector = ProxyConnector::from_proxy_unsecured(connector, proxy);
Client::builder()
.build(proxy_connector)
.request(req)
.await?
} else {
Client::new().request(req).await?
};

let body = hyper::body::to_bytes(response.into_body()).await?;
let data: APResolveData = serde_json::from_slice(body.as_ref())?;
let body = hyper::body::to_bytes(response.into_body()).await?;
let data: APResolveData = serde_json::from_slice(body.as_ref())?;

let ap = if ap_port.is_some() || proxy.is_some() {
data.ap_list.into_iter().find_map(|ap| {
if ap.parse::<Uri>().ok()?.port()? == port {
Some(ap)
} else {
None
}
})
let ap = if ap_port.is_some() || proxy.is_some() {
data.ap_list.into_iter().find_map(|ap| {
if ap.parse::<Uri>().ok()?.port()? == port {
Some(ap)
} else {
data.ap_list.into_iter().next()
None
}
.ok_or("empty AP List")?;

Ok(ap)
}

pub async fn apresolve_or_fallback(proxy: &Option<Url>, ap_port: &Option<u16>) -> String {
apresolve(proxy, ap_port).await.unwrap_or_else(|e| {
warn!("Failed to resolve Access Point: {}", e);
warn!("Using fallback \"{}\"", AP_FALLBACK);
AP_FALLBACK.into()
})
}
})
} else {
pub async fn apresolve_or_fallback(_: &Option<Url>, _: &Option<u16>) -> String {
AP_FALLBACK.to_string()
}
data.ap_list.into_iter().next()
}
.ok_or("empty AP List")?;

Ok(ap)
}

pub async fn apresolve(proxy: Option<&Url>, ap_port: Option<u16>) -> String {
try_apresolve(proxy, ap_port).await.unwrap_or_else(|e| {
warn!("Failed to resolve Access Point: {}", e);
warn!("Using fallback \"{}\"", AP_FALLBACK);
AP_FALLBACK.into()
})
}
Loading

0 comments on commit 95fedf5

Please sign in to comment.