Skip to content

Commit 1562884

Browse files
committed
Introduce HTTP client
1 parent ce4f8dc commit 1562884

File tree

5 files changed

+58
-33
lines changed

5 files changed

+58
-33
lines changed

core/src/apresolve.rs

+12-26
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
1-
use std::error::Error;
2-
3-
use hyper::client::HttpConnector;
4-
use hyper::{Body, Client, Request};
5-
use hyper_proxy::{Intercept, Proxy, ProxyConnector};
1+
use crate::http_client::HttpClient;
2+
use hyper::{Body, Request};
63
use serde::Deserialize;
7-
use url::Url;
4+
use std::error::Error;
85

96
const APRESOLVE_ENDPOINT: &str =
107
"http://apresolve.spotify.com/?type=accesspoint&type=dealer&type=spclient";
@@ -56,35 +53,21 @@ fn select_ap(data: Vec<String>, fallback: &str, ap_port: Option<u16>) -> SocketA
5653
ap.unwrap_or_else(|| (String::from(fallback), port))
5754
}
5855

59-
async fn try_apresolve(proxy: Option<&Url>) -> Result<ApResolveData, Box<dyn Error>> {
56+
async fn try_apresolve(http_client: &HttpClient) -> Result<ApResolveData, Box<dyn Error>> {
6057
let req = Request::builder()
6158
.method("GET")
6259
.uri(APRESOLVE_ENDPOINT)
6360
.body(Body::empty())
6461
.unwrap();
6562

66-
let response = if let Some(url) = proxy {
67-
// Panic safety: all URLs are valid URIs
68-
let uri = url.to_string().parse().unwrap();
69-
let proxy = Proxy::new(Intercept::All, uri);
70-
let connector = HttpConnector::new();
71-
let proxy_connector = ProxyConnector::from_proxy_unsecured(connector, proxy);
72-
Client::builder()
73-
.build(proxy_connector)
74-
.request(req)
75-
.await?
76-
} else {
77-
Client::new().request(req).await?
78-
};
79-
80-
let body = hyper::body::to_bytes(response.into_body()).await?;
63+
let body = http_client.request_body(req).await?;
8164
let data: ApResolveData = serde_json::from_slice(body.as_ref())?;
8265

8366
Ok(data)
8467
}
8568

86-
pub async fn apresolve(proxy: Option<&Url>, ap_port: Option<u16>) -> AccessPoints {
87-
let data = try_apresolve(proxy).await.unwrap_or_else(|e| {
69+
pub async fn apresolve(http_client: &HttpClient, ap_port: Option<u16>) -> AccessPoints {
70+
let data = try_apresolve(http_client).await.unwrap_or_else(|e| {
8871
warn!("Failed to resolve access points: {}, using fallbacks.", e);
8972
ApResolveData::default()
9073
});
@@ -105,10 +88,12 @@ mod test {
10588
use std::net::ToSocketAddrs;
10689

10790
use super::apresolve;
91+
use crate::http_client::HttpClient;
10892

10993
#[tokio::test]
11094
async fn test_apresolve() {
111-
let aps = apresolve(None, None).await;
95+
let http_client = HttpClient::new(None);
96+
let aps = apresolve(&http_client, None).await;
11297

11398
// Assert that the result contains a valid host and port
11499
aps.accesspoint.to_socket_addrs().unwrap().next().unwrap();
@@ -118,7 +103,8 @@ mod test {
118103

119104
#[tokio::test]
120105
async fn test_apresolve_port_443() {
121-
let aps = apresolve(None, Some(443)).await;
106+
let http_client = HttpClient::new(None);
107+
let aps = apresolve(&http_client, Some(443)).await;
122108

123109
let port = aps
124110
.accesspoint

core/src/http_client.rs

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
use hyper::client::HttpConnector;
2+
use hyper::{Body, Client, Request, Response};
3+
use hyper_proxy::{Intercept, Proxy, ProxyConnector};
4+
use url::Url;
5+
6+
pub struct HttpClient {
7+
proxy: Option<Url>,
8+
}
9+
10+
impl HttpClient {
11+
pub fn new(proxy: Option<&Url>) -> Self {
12+
Self {
13+
proxy: proxy.cloned(),
14+
}
15+
}
16+
17+
pub async fn request(&self, req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
18+
if let Some(url) = &self.proxy {
19+
// Panic safety: all URLs are valid URIs
20+
let uri = url.to_string().parse().unwrap();
21+
let proxy = Proxy::new(Intercept::All, uri);
22+
let connector = HttpConnector::new();
23+
let proxy_connector = ProxyConnector::from_proxy_unsecured(connector, proxy);
24+
Client::builder().build(proxy_connector).request(req).await
25+
} else {
26+
Client::new().request(req).await
27+
}
28+
}
29+
30+
pub async fn request_body(&self, req: Request<Body>) -> Result<bytes::Bytes, hyper::Error> {
31+
let response = self.request(req).await?;
32+
hyper::body::to_bytes(response.into_body()).await
33+
}
34+
}

core/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,13 @@ mod connection;
1919
mod dealer;
2020
#[doc(hidden)]
2121
pub mod diffie_hellman;
22+
mod http_client;
2223
pub mod keymaster;
2324
pub mod mercury;
2425
mod proxytunnel;
2526
pub mod session;
2627
mod socket;
28+
mod spclient;
2729
pub mod spotify_id;
2830
mod token;
2931
#[doc(hidden)]

core/src/session.rs

+10-6
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use crate::cache::Cache;
2323
use crate::channel::ChannelManager;
2424
use crate::config::SessionConfig;
2525
use crate::connection::{self, AuthenticationError};
26+
use crate::http_client::HttpClient;
2627
use crate::mercury::MercuryManager;
2728
use crate::token::TokenProvider;
2829

@@ -45,6 +46,7 @@ struct SessionInternal {
4546
config: SessionConfig,
4647
data: RwLock<SessionData>,
4748

49+
http_client: HttpClient,
4850
tx_connection: mpsc::UnboundedSender<(u8, Vec<u8>)>,
4951

5052
audio_key: OnceCell<AudioKeyManager>,
@@ -69,22 +71,22 @@ impl Session {
6971
credentials: Credentials,
7072
cache: Option<Cache>,
7173
) -> Result<Session, SessionError> {
72-
let ap = apresolve(config.proxy.as_ref(), config.ap_port)
73-
.await
74-
.accesspoint;
74+
let http_client = HttpClient::new(config.proxy.as_ref());
75+
let ap = apresolve(&http_client, config.ap_port).await.accesspoint;
7576

7677
info!("Connecting to AP \"{}:{}\"", ap.0, ap.1);
77-
let mut conn = connection::connect(&ap.0, ap.1, config.proxy.as_ref()).await?;
78+
let mut transport = connection::connect(&ap.0, ap.1, config.proxy.as_ref()).await?;
7879

7980
let reusable_credentials =
80-
connection::authenticate(&mut conn, credentials, &config.device_id).await?;
81+
connection::authenticate(&mut transport, credentials, &config.device_id).await?;
8182
info!("Authenticated as \"{}\" !", reusable_credentials.username);
8283
if let Some(cache) = &cache {
8384
cache.save_credentials(&reusable_credentials);
8485
}
8586

8687
let session = Session::create(
87-
conn,
88+
transport,
89+
http_client,
8890
config,
8991
cache,
9092
reusable_credentials.username,
@@ -96,6 +98,7 @@ impl Session {
9698

9799
fn create(
98100
transport: connection::Transport,
101+
http_client: HttpClient,
99102
config: SessionConfig,
100103
cache: Option<Cache>,
101104
username: String,
@@ -116,6 +119,7 @@ impl Session {
116119
invalid: false,
117120
time_delta: 0,
118121
}),
122+
http_client,
119123
tx_connection: sender_tx,
120124
cache: cache.map(Arc::new),
121125
audio_key: OnceCell::new(),
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
11
// https://github.com/librespot-org/librespot-java/blob/27783e06f456f95228c5ac37acf2bff8c1a8a0c4/lib/src/main/java/xyz/gianlu/librespot/dealer/ApiClient.java
2-

0 commit comments

Comments
 (0)