Skip to content

Commit

Permalink
Add basic websocket support
Browse files Browse the repository at this point in the history
  • Loading branch information
Johannesd3 committed May 26, 2021
1 parent 08ba3ad commit 1ade02b
Show file tree
Hide file tree
Showing 11 changed files with 1,040 additions and 68 deletions.
136 changes: 136 additions & 0 deletions Cargo.lock

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

3 changes: 2 additions & 1 deletion core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,9 @@ serde_json = "1.0"
sha-1 = "0.9"
shannon = "0.2.0"
thiserror = "1.0.7"
tokio = { version = "1.0", features = ["io-util", "net", "rt", "sync"] }
tokio = { version = "1.5", features = ["io-util", "macros", "net", "rt", "time", "sync"] }
tokio-stream = "0.1.1"
tokio-tungstenite = { version = "0.14", default-features = false, features = ["rustls-tls"] }
tokio-util = { version = "0.6", features = ["codec"] }
url = "2.1"
uuid = { version = "0.8", default-features = false, features = ["v4"] }
Expand Down
34 changes: 18 additions & 16 deletions core/src/apresolve.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use std::error::Error;

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

use super::AP_FALLBACK;
use super::ap_fallback;

const APRESOLVE_ENDPOINT: &str = "http://apresolve.spotify.com:80";

Expand All @@ -18,7 +18,7 @@ struct ApResolveData {
async fn try_apresolve(
proxy: Option<&Url>,
ap_port: Option<u16>,
) -> Result<String, Box<dyn Error>> {
) -> Result<(String, u16), Box<dyn Error>> {
let port = ap_port.unwrap_or(443);

let mut req = Request::new(Body::empty());
Expand All @@ -43,27 +43,29 @@ async fn try_apresolve(
let body = hyper::body::to_bytes(response.into_body()).await?;
let data: ApResolveData = serde_json::from_slice(body.as_ref())?;

let mut aps = data.ap_list.into_iter().filter_map(|ap| {
let mut split = ap.rsplitn(2, ':');
let port = split
.next()
.expect("rsplitn should not return empty iterator");
let host = split.next()?.to_owned();
let port: u16 = port.parse().ok()?;
Some((host, port))
});
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
}
})
aps.find(|(_, p)| *p == port)
} else {
data.ap_list.into_iter().next()
aps.next()
}
.ok_or("empty AP List")?;
.ok_or("no valid AP in list")?;

Ok(ap)
}

pub async fn apresolve(proxy: Option<&Url>, ap_port: Option<u16>) -> String {
pub async fn apresolve(proxy: Option<&Url>, ap_port: Option<u16>) -> (String, u16) {
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()
warn!("Failed to resolve Access Point: {}, using fallback.", e);
ap_fallback()
})
}

Expand Down
48 changes: 2 additions & 46 deletions core/src/connection/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ pub use self::codec::ApCodec;
pub use self::handshake::handshake;

use std::io::{self, ErrorKind};
use std::net::ToSocketAddrs;

use futures_util::{SinkExt, StreamExt};
use protobuf::{self, Message, ProtobufError};
Expand All @@ -16,7 +15,6 @@ use url::Url;

use crate::authentication::Credentials;
use crate::protocol::keyexchange::{APLoginFailed, ErrorCode};
use crate::proxytunnel;
use crate::version;

pub type Transport = Framed<TcpStream, ApCodec>;
Expand Down Expand Up @@ -58,50 +56,8 @@ impl From<APLoginFailed> for AuthenticationError {
}
}

pub async fn connect(addr: String, proxy: Option<&Url>) -> io::Result<Transport> {
let socket = if let Some(proxy_url) = proxy {
info!("Using proxy \"{}\"", proxy_url);

let socket_addr = proxy_url.socket_addrs(|| None).and_then(|addrs| {
addrs.into_iter().next().ok_or_else(|| {
io::Error::new(
io::ErrorKind::NotFound,
"Can't resolve proxy server address",
)
})
})?;
let socket = TcpStream::connect(&socket_addr).await?;

let uri = addr.parse::<http::Uri>().map_err(|_| {
io::Error::new(
io::ErrorKind::InvalidData,
"Can't parse access point address",
)
})?;
let host = uri.host().ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidInput,
"The access point address contains no hostname",
)
})?;
let port = uri.port().ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidInput,
"The access point address contains no port",
)
})?;

proxytunnel::proxy_connect(socket, host, port.as_str()).await?
} else {
let socket_addr = addr.to_socket_addrs()?.next().ok_or_else(|| {
io::Error::new(
io::ErrorKind::NotFound,
"Can't resolve access point address",
)
})?;

TcpStream::connect(&socket_addr).await?
};
pub async fn connect(host: &str, port: u16, proxy: Option<&Url>) -> io::Result<Transport> {
let socket = crate::socket::connect(host, port, proxy).await?;

handshake(socket).await
}
Expand Down
Loading

0 comments on commit 1ade02b

Please sign in to comment.