From 8cff10e983bcf87b44de01229e2e5154a125369e Mon Sep 17 00:00:00 2001 From: johannesd3 Date: Wed, 10 Feb 2021 22:50:08 +0100 Subject: [PATCH] Put apresolve behind feature flag --- Cargo.lock | 2 +- Cargo.toml | 5 +- core/Cargo.toml | 10 ++-- core/src/apresolve.rs | 103 ++++++++++++++++++++++------------------ core/src/lib.rs | 2 + core/src/proxytunnel.rs | 103 +++++++++++++++++++++------------------- 6 files changed, 124 insertions(+), 101 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c9bcef5b..282888dd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1450,6 +1450,7 @@ dependencies = [ "base64", "byteorder", "bytes", + "cfg-if 1.0.0", "env_logger", "error-chain", "futures", @@ -1473,7 +1474,6 @@ dependencies = [ "shannon", "tokio", "tokio-util", - "tower-service", "url 1.7.2", "vergen", ] diff --git a/Cargo.toml b/Cargo.toml index d34189ec..617ae086 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,6 +61,9 @@ sha-1 = "0.8" hex = "0.4" [features] +apresolve = ["librespot-core/apresolve"] +apresolve-http2 = ["librespot-core/apresolve-http2"] + alsa-backend = ["librespot-playback/alsa-backend"] portaudio-backend = ["librespot-playback/portaudio-backend"] pulseaudio-backend = ["librespot-playback/pulseaudio-backend"] @@ -75,7 +78,7 @@ with-vorbis = ["librespot-audio/with-vorbis"] # with-dns-sd = ["librespot-connect/with-dns-sd"] -default = ["librespot-playback/rodio-backend"] +default = ["rodio-backend", "apresolve"] [package.metadata.deb] maintainer = "librespot-org" diff --git a/core/Cargo.toml b/core/Cargo.toml index 673637a0..f1a15eb1 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -17,11 +17,12 @@ aes = "0.6" base64 = "0.13" byteorder = "1.4" bytes = "1.0" +cfg-if = "1" error-chain = { version = "0.12", default-features = false } futures = { version = "0.3", features = ["bilock", "unstable"] } hmac = "0.10" httparse = "1.3" -hyper = { version = "0.14", features = ["client", "tcp", "http1", "http2"] } +hyper = { version = "0.14", optional = true, features = ["client", "tcp", "http1"] } log = "0.4" num-bigint = "0.3" num-integer = "0.1" @@ -38,7 +39,6 @@ sha-1 = "0.9" shannon = "0.2.0" tokio = { version = "1.0", features = ["io-util", "rt-multi-thread"] } tokio-util = { version = "0.6", features = ["codec"] } -tower-service = "0.3" url = "1.7" [build-dependencies] @@ -47,4 +47,8 @@ vergen = "3.0.4" [dev-dependencies] env_logger = "*" -tokio = {version = "1.0", features = ["macros"] } \ No newline at end of file +tokio = {version = "1.0", features = ["macros"] } + +[features] +apresolve = ["hyper"] +apresolve-http2 = ["apresolve", "hyper/http2"] diff --git a/core/src/apresolve.rs b/core/src/apresolve.rs index 81340c9d..cd354d88 100644 --- a/core/src/apresolve.rs +++ b/core/src/apresolve.rs @@ -1,61 +1,72 @@ const AP_FALLBACK: &'static str = "ap.spotify.com:443"; -const APRESOLVE_ENDPOINT: &'static str = "http://apresolve.spotify.com:80"; -use hyper::{Body, Client, Method, Request, Uri}; -use std::error::Error; use url::Url; -use crate::proxytunnel::ProxyTunnel; +cfg_if! { + if #[cfg(feature = "apresolve")] { + const APRESOLVE_ENDPOINT: &'static str = "http://apresolve.spotify.com:80"; -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct APResolveData { - ap_list: Vec, -} + use std::error::Error; -async fn apresolve(proxy: &Option, ap_port: &Option) -> Result> { - let port = ap_port.unwrap_or(443); + use hyper::{Body, Client, Method, Request, Uri}; - let req = Request::builder() - .method(Method::GET) - .uri( - APRESOLVE_ENDPOINT - .parse::() - .expect("invalid AP resolve URL"), - ) - .body(Body::empty())?; + use crate::proxytunnel::ProxyTunnel; - let response = if let Some(url) = proxy { - Client::builder() - .build(ProxyTunnel::new(url)?) - .request(req) - .await? - } else { - Client::new().request(req).await? - }; + #[derive(Clone, Debug, Serialize, Deserialize)] + pub struct APResolveData { + ap_list: Vec, + } - let body = hyper::body::to_bytes(response.into_body()).await?; - let data: APResolveData = serde_json::from_slice(body.as_ref())?; + async fn apresolve(proxy: &Option, ap_port: &Option) -> Result> { + let port = ap_port.unwrap_or(443); - let ap = if ap_port.is_some() || proxy.is_some() { - data.ap_list.into_iter().find_map(|ap| { - if ap.parse::().ok()?.port()? == port { - Some(ap) + let req = Request::builder() + .method(Method::GET) + .uri( + APRESOLVE_ENDPOINT + .parse::() + .expect("invalid AP resolve URL"), + ) + .body(Body::empty())?; + + let response = if let Some(url) = proxy { + Client::builder() + .build(ProxyTunnel::new(url)?) + .request(req) + .await? } else { - None + 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 ap = if ap_port.is_some() || proxy.is_some() { + data.ap_list.into_iter().find_map(|ap| { + if ap.parse::().ok()?.port()? == port { + Some(ap) + } else { + None + } + }) + } else { + data.ap_list.into_iter().next() } - }) + .ok_or("empty AP List")?; + + Ok(ap) + } + + pub async fn apresolve_or_fallback(proxy: &Option, ap_port: &Option) -> 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 { - data.ap_list.into_iter().next() + pub async fn apresolve_or_fallback(_: &Option, _: &Option) -> String { + AP_FALLBACK.to_string() + } } - .ok_or("empty AP List")?; - - Ok(ap) -} - -pub async fn apresolve_or_fallback(proxy: &Option, ap_port: &Option) -> 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() - }) } diff --git a/core/src/lib.rs b/core/src/lib.rs index 6c180c2e..65fa898a 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -3,6 +3,8 @@ #[macro_use] extern crate log; #[macro_use] +extern crate cfg_if; +#[macro_use] extern crate serde_derive; #[macro_use] extern crate pin_project_lite; diff --git a/core/src/proxytunnel.rs b/core/src/proxytunnel.rs index c2033c85..158d314f 100644 --- a/core/src/proxytunnel.rs +++ b/core/src/proxytunnel.rs @@ -1,16 +1,6 @@ -use futures::Future; -use hyper::Uri; -use std::{ - io, - net::{SocketAddr, ToSocketAddrs}, - pin::Pin, - task::Poll, -}; -use tokio::{ - io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}, - net::TcpStream, -}; -use tower_service::Service; +use std::io; + +use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; pub async fn connect( mut proxy_connection: T, @@ -64,43 +54,56 @@ pub async fn connect( } } -#[derive(Clone)] -pub struct ProxyTunnel { - proxy_addr: SocketAddr, -} +cfg_if! { + if #[cfg(feature = "apresolve")] { + use std::future::Future; + use std::net::{SocketAddr, ToSocketAddrs}; + use std::pin::Pin; + use std::task::Poll; -impl ProxyTunnel { - pub fn new(addr: T) -> io::Result { - let addr = addr.to_socket_addrs()?.next().ok_or_else(|| { - io::Error::new(io::ErrorKind::InvalidInput, "No socket address given") - })?; - Ok(Self { proxy_addr: addr }) - } -} - -impl Service for ProxyTunnel { - type Response = TcpStream; - type Error = io::Error; - type Future = Pin> + Send>>; - - fn poll_ready(&mut self, _: &mut std::task::Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } - - fn call(&mut self, url: Uri) -> Self::Future { - let proxy_addr = self.proxy_addr; - let fut = async move { - let host = url - .host() - .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "Host is missing"))?; - let port = url - .port() - .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "Port is missing"))?; - - let conn = TcpStream::connect(proxy_addr).await?; - connect(conn, host, port.as_u16()).await - }; - - Box::pin(fut) + use hyper::service::Service; + use hyper::Uri; + use tokio::net::TcpStream; + + #[derive(Clone)] + pub struct ProxyTunnel { + proxy_addr: SocketAddr, + } + + impl ProxyTunnel { + pub fn new(addr: T) -> io::Result { + let addr = addr.to_socket_addrs()?.next().ok_or_else(|| { + io::Error::new(io::ErrorKind::InvalidInput, "No socket address given") + })?; + Ok(Self { proxy_addr: addr }) + } + } + + impl Service for ProxyTunnel { + type Response = TcpStream; + type Error = io::Error; + type Future = Pin> + Send>>; + + fn poll_ready(&mut self, _: &mut std::task::Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, url: Uri) -> Self::Future { + let proxy_addr = self.proxy_addr; + let fut = async move { + let host = url + .host() + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "Host is missing"))?; + let port = url + .port() + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "Port is missing"))?; + + let conn = TcpStream::connect(proxy_addr).await?; + connect(conn, host, port.as_u16()).await + }; + + Box::pin(fut) + } + } } }