diff --git a/Cargo.lock b/Cargo.lock index e4176518..7606ed82 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -995,31 +995,6 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" -[[package]] -name = "headers" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62689dc57c7456e69712607ffcbd0aa1dfcccf9af73727e9b25bc1825375cac3" -dependencies = [ - "base64 0.13.0", - "bitflags 1.2.1", - "bytes", - "headers-core", - "http", - "mime", - "sha-1", - "time 0.1.43", -] - -[[package]] -name = "headers-core" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" -dependencies = [ - "http", -] - [[package]] name = "heck" version = "0.3.2" @@ -1111,20 +1086,6 @@ dependencies = [ "want", ] -[[package]] -name = "hyper-proxy" -version = "0.8.0" -source = "git+https://github.com/e00E/hyper-proxy.git?branch=upgrade-tokio#4be706f2f0297bd3d14f301b6ea0be8f3078bb17" -dependencies = [ - "bytes", - "futures", - "headers", - "http", - "hyper", - "tokio", - "tower-service", -] - [[package]] name = "ident_case" version = "1.0.1" @@ -1402,7 +1363,6 @@ dependencies = [ "hmac", "httparse", "hyper", - "hyper-proxy", "librespot-protocol", "log", "num-bigint", @@ -1420,6 +1380,7 @@ dependencies = [ "shannon", "tokio", "tokio-util", + "tower-service", "url 1.7.2", "uuid", "vergen", @@ -1532,12 +1493,6 @@ version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" -[[package]] -name = "mime" -version = "0.3.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" - [[package]] name = "miniz_oxide" version = "0.4.3" diff --git a/core/Cargo.toml b/core/Cargo.toml index 1f7b9afc..e0d79527 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -21,7 +21,6 @@ futures = { version = "0.3", features = ["bilock", "unstable"] } hmac = "0.7" httparse = "1.3" hyper = { version = "0.14", features = ["client", "tcp", "http1", "http2"] } -hyper-proxy = { git = "https://github.com/e00E/hyper-proxy.git", branch="upgrade-tokio", default_features = false } log = "0.4" num-bigint = "0.3" num-integer = "0.1" @@ -38,6 +37,7 @@ sha-1 = "~0.8" 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" uuid = { version = "0.8", features = ["v4"] } diff --git a/core/src/apresolve.rs b/core/src/apresolve.rs index d35b2091..81340c9d 100644 --- a/core/src/apresolve.rs +++ b/core/src/apresolve.rs @@ -1,11 +1,12 @@ const AP_FALLBACK: &'static str = "ap.spotify.com:443"; -const APRESOLVE_ENDPOINT: &'static str = "http://apresolve.spotify.com/"; +const APRESOLVE_ENDPOINT: &'static str = "http://apresolve.spotify.com:80"; -use hyper::{client::HttpConnector, Body, Client, Method, Request, Uri}; -use hyper_proxy::{Intercept, Proxy, ProxyConnector}; +use hyper::{Body, Client, Method, Request, Uri}; use std::error::Error; use url::Url; +use crate::proxytunnel::ProxyTunnel; + #[derive(Clone, Debug, Serialize, Deserialize)] pub struct APResolveData { ap_list: Vec, @@ -14,7 +15,7 @@ pub struct APResolveData { async fn apresolve(proxy: &Option, ap_port: &Option) -> Result> { let port = ap_port.unwrap_or(443); - let mut req = Request::builder() + let req = Request::builder() .method(Method::GET) .uri( APRESOLVE_ENDPOINT @@ -24,18 +25,10 @@ async fn apresolve(proxy: &Option, ap_port: &Option) -> Result; pub async fn connect(addr: String, proxy: &Option) -> io::Result { let socket = if let Some(proxy) = proxy { info!("Using proxy \"{}\"", proxy); + + let mut split = addr.rsplit(':'); + + let port = split + .next() + .unwrap() // will never panic, split iterator contains at least one element + .parse() + .map_err(|e| { + io::Error::new(io::ErrorKind::InvalidInput, format!("Invalid port: {}", e)) + })?; + + let host = split + .next() + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "Missing port"))?; + let socket_addr = proxy.to_socket_addrs().and_then(|mut iter| { iter.next().ok_or_else(|| { io::Error::new( @@ -31,7 +46,8 @@ pub async fn connect(addr: String, proxy: &Option) -> io::Result }) })?; let socket = TcpStream::connect(&socket_addr).await?; - proxytunnel::connect(socket, &addr).await? + + proxytunnel::connect(socket, host, port).await? } else { let socket_addr = addr.to_socket_addrs().and_then(|mut iter| { iter.next().ok_or_else(|| { diff --git a/core/src/lib.rs b/core/src/lib.rs index 4fb632a0..3e332c28 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -14,7 +14,6 @@ extern crate futures; extern crate hmac; extern crate httparse; extern crate hyper; -extern crate hyper_proxy; extern crate num_bigint; extern crate num_integer; extern crate num_traits; @@ -28,6 +27,7 @@ extern crate sha1; extern crate shannon; pub extern crate tokio; extern crate tokio_util; +extern crate tower_service; extern crate url; extern crate uuid; diff --git a/core/src/proxytunnel.rs b/core/src/proxytunnel.rs index c8e9eab6..c2033c85 100644 --- a/core/src/proxytunnel.rs +++ b/core/src/proxytunnel.rs @@ -1,27 +1,36 @@ -use std::io; - +use futures::Future; use hyper::Uri; -use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; +use std::{ + io, + net::{SocketAddr, ToSocketAddrs}, + pin::Pin, + task::Poll, +}; +use tokio::{ + io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}, + net::TcpStream, +}; +use tower_service::Service; pub async fn connect( - mut connection: T, - connect_url: &str, + mut proxy_connection: T, + connect_host: &str, + connect_port: u16, ) -> io::Result { - let uri = connect_url.parse::().unwrap(); - let mut buffer = format!( - "CONNECT {0}:{1} HTTP/1.1\r\n\ - \r\n", - uri.host().unwrap_or_else(|| panic!("No host in {}", uri)), - uri.port().unwrap_or_else(|| panic!("No port in {}", uri)) - ) - .into_bytes(); - connection.write_all(buffer.as_ref()).await?; + let mut buffer = Vec::new(); + buffer.extend_from_slice(b"CONNECT "); + buffer.extend_from_slice(connect_host.as_bytes()); + buffer.push(b':'); + buffer.extend_from_slice(connect_port.to_string().as_bytes()); + buffer.extend_from_slice(b" HTTP/1.1\r\n\r\n"); + + proxy_connection.write_all(buffer.as_ref()).await?; buffer.resize(buffer.capacity(), 0); let mut offset = 0; loop { - let bytes_read = connection.read(&mut buffer[offset..]).await?; + let bytes_read = proxy_connection.read(&mut buffer[offset..]).await?; if bytes_read == 0 { return Err(io::Error::new(io::ErrorKind::Other, "Early EOF from proxy")); } @@ -36,7 +45,7 @@ pub async fn connect( if status.is_complete() { return match response.code { - Some(200) => Ok(connection), // Proxy says all is well + Some(200) => Ok(proxy_connection), // Proxy says all is well Some(code) => { let reason = response.reason.unwrap_or("no reason"); let msg = format!("Proxy responded with {}: {}", code, reason); @@ -54,3 +63,44 @@ pub async fn connect( } } } + +#[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) + } +}