mirror of
https://github.com/librespot-org/librespot.git
synced 2024-12-18 17:11:53 +00:00
Configure User-Agent once
This commit is contained in:
parent
3b07a6bcb9
commit
9d88ac59c6
4 changed files with 40 additions and 36 deletions
|
@ -363,6 +363,8 @@ impl AudioFileStreaming {
|
||||||
let mut cdn_url = CdnUrl::new(file_id).resolve_audio(&session).await?;
|
let mut cdn_url = CdnUrl::new(file_id).resolve_audio(&session).await?;
|
||||||
let url = cdn_url.get_url()?;
|
let url = cdn_url.get_url()?;
|
||||||
|
|
||||||
|
trace!("Streaming {:?}", url);
|
||||||
|
|
||||||
let mut streamer = session.spclient().stream_file(url, 0, download_size)?;
|
let mut streamer = session.spclient().stream_file(url, 0, download_size)?;
|
||||||
let request_time = Instant::now();
|
let request_time = Instant::now();
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,6 @@ use url::Url;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct SessionConfig {
|
pub struct SessionConfig {
|
||||||
pub user_agent: String,
|
|
||||||
pub device_id: String,
|
pub device_id: String,
|
||||||
pub proxy: Option<Url>,
|
pub proxy: Option<Url>,
|
||||||
pub ap_port: Option<u16>,
|
pub ap_port: Option<u16>,
|
||||||
|
@ -16,7 +15,6 @@ impl Default for SessionConfig {
|
||||||
fn default() -> SessionConfig {
|
fn default() -> SessionConfig {
|
||||||
let device_id = uuid::Uuid::new_v4().to_hyphenated().to_string();
|
let device_id = uuid::Uuid::new_v4().to_hyphenated().to_string();
|
||||||
SessionConfig {
|
SessionConfig {
|
||||||
user_agent: crate::version::VERSION_STRING.to_string(),
|
|
||||||
device_id,
|
device_id,
|
||||||
proxy: None,
|
proxy: None,
|
||||||
ap_port: None,
|
ap_port: None,
|
||||||
|
|
|
@ -4,18 +4,20 @@ use futures_util::FutureExt;
|
||||||
use http::header::HeaderValue;
|
use http::header::HeaderValue;
|
||||||
use http::uri::InvalidUri;
|
use http::uri::InvalidUri;
|
||||||
use hyper::client::{HttpConnector, ResponseFuture};
|
use hyper::client::{HttpConnector, ResponseFuture};
|
||||||
use hyper::header::{InvalidHeaderValue, USER_AGENT};
|
use hyper::header::USER_AGENT;
|
||||||
use hyper::{Body, Client, Request, Response, StatusCode};
|
use hyper::{Body, Client, Request, Response, StatusCode};
|
||||||
use hyper_proxy::{Intercept, Proxy, ProxyConnector};
|
use hyper_proxy::{Intercept, Proxy, ProxyConnector};
|
||||||
use hyper_rustls::HttpsConnector;
|
use hyper_rustls::HttpsConnector;
|
||||||
use rustls::ClientConfig;
|
use rustls::{ClientConfig, RootCertStore};
|
||||||
use std::env::consts::OS;
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
|
use std::env::consts::OS;
|
||||||
|
|
||||||
use crate::version::{SPOTIFY_MOBILE_VERSION, SPOTIFY_VERSION, VERSION_STRING};
|
use crate::version::{SPOTIFY_MOBILE_VERSION, SPOTIFY_VERSION, VERSION_STRING};
|
||||||
|
|
||||||
pub struct HttpClient {
|
pub struct HttpClient {
|
||||||
|
user_agent: HeaderValue,
|
||||||
proxy: Option<Url>,
|
proxy: Option<Url>,
|
||||||
tls_config: ClientConfig,
|
tls_config: ClientConfig,
|
||||||
}
|
}
|
||||||
|
@ -34,12 +36,6 @@ pub enum HttpClientError {
|
||||||
ProxyBuilder(#[from] std::io::Error),
|
ProxyBuilder(#[from] std::io::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<InvalidHeaderValue> for HttpClientError {
|
|
||||||
fn from(err: InvalidHeaderValue) -> Self {
|
|
||||||
Self::Parsing(err.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<InvalidUri> for HttpClientError {
|
impl From<InvalidUri> for HttpClientError {
|
||||||
fn from(err: InvalidUri) -> Self {
|
fn from(err: InvalidUri) -> Self {
|
||||||
Self::Parsing(err.into())
|
Self::Parsing(err.into())
|
||||||
|
@ -48,6 +44,30 @@ impl From<InvalidUri> for HttpClientError {
|
||||||
|
|
||||||
impl HttpClient {
|
impl HttpClient {
|
||||||
pub fn new(proxy: Option<&Url>) -> Self {
|
pub fn new(proxy: Option<&Url>) -> Self {
|
||||||
|
let spotify_version = match OS {
|
||||||
|
"android" | "ios" => SPOTIFY_MOBILE_VERSION.to_owned(),
|
||||||
|
_ => SPOTIFY_VERSION.to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let spotify_platform = match OS {
|
||||||
|
"android" => "Android/31",
|
||||||
|
"ios" => "iOS/15.1.1",
|
||||||
|
"macos" => "OSX/0",
|
||||||
|
"windows" => "Win32/0",
|
||||||
|
_ => "Linux/0",
|
||||||
|
};
|
||||||
|
|
||||||
|
let user_agent_str = &format!(
|
||||||
|
"Spotify/{} {} ({})",
|
||||||
|
spotify_version, spotify_platform, VERSION_STRING
|
||||||
|
);
|
||||||
|
|
||||||
|
let user_agent = HeaderValue::from_str(user_agent_str).unwrap_or_else(|err| {
|
||||||
|
error!("Invalid user agent <{}>: {}", user_agent_str, err);
|
||||||
|
error!("Parts of the API will probably not work. Please report this as a bug.");
|
||||||
|
HeaderValue::from_static("")
|
||||||
|
});
|
||||||
|
|
||||||
// configuring TLS is expensive and should be done once per process
|
// configuring TLS is expensive and should be done once per process
|
||||||
let root_store = match rustls_native_certs::load_native_certs() {
|
let root_store = match rustls_native_certs::load_native_certs() {
|
||||||
Ok(store) => store,
|
Ok(store) => store,
|
||||||
|
@ -55,7 +75,11 @@ impl HttpClient {
|
||||||
warn!("Could not load all certificates: {:?}", err);
|
warn!("Could not load all certificates: {:?}", err);
|
||||||
store
|
store
|
||||||
}
|
}
|
||||||
Err((None, err)) => Err(err).expect("cannot access native cert store"),
|
Err((None, err)) => {
|
||||||
|
error!("Cannot access native certificate store: {}", err);
|
||||||
|
error!("Continuing, but most requests will probably fail until you fix your system certificate store.");
|
||||||
|
RootCertStore::empty()
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut tls_config = ClientConfig::new();
|
let mut tls_config = ClientConfig::new();
|
||||||
|
@ -63,12 +87,15 @@ impl HttpClient {
|
||||||
tls_config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
|
tls_config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
user_agent,
|
||||||
proxy: proxy.cloned(),
|
proxy: proxy.cloned(),
|
||||||
tls_config,
|
tls_config,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn request(&self, req: Request<Body>) -> Result<Response<Body>, HttpClientError> {
|
pub async fn request(&self, req: Request<Body>) -> Result<Response<Body>, HttpClientError> {
|
||||||
|
debug!("Requesting {:?}", req.uri().to_string());
|
||||||
|
|
||||||
let request = self.request_fut(req)?;
|
let request = self.request_fut(req)?;
|
||||||
{
|
{
|
||||||
let response = request.await;
|
let response = request.await;
|
||||||
|
@ -97,34 +124,12 @@ impl HttpClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn request_fut(&self, mut req: Request<Body>) -> Result<ResponseFuture, HttpClientError> {
|
pub fn request_fut(&self, mut req: Request<Body>) -> Result<ResponseFuture, HttpClientError> {
|
||||||
trace!("Requesting {:?}", req.uri().to_string());
|
|
||||||
|
|
||||||
let mut http = HttpConnector::new();
|
let mut http = HttpConnector::new();
|
||||||
http.enforce_http(false);
|
http.enforce_http(false);
|
||||||
let connector = HttpsConnector::from((http, self.tls_config.clone()));
|
let connector = HttpsConnector::from((http, self.tls_config.clone()));
|
||||||
|
|
||||||
let spotify_version = match OS {
|
|
||||||
"android" | "ios" => SPOTIFY_MOBILE_VERSION.to_owned(),
|
|
||||||
_ => SPOTIFY_VERSION.to_string(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let spotify_platform = match OS {
|
|
||||||
"android" => "Android/31",
|
|
||||||
"ios" => "iOS/15.1.1",
|
|
||||||
"macos" => "OSX/0",
|
|
||||||
"windows" => "Win32/0",
|
|
||||||
_ => "Linux/0",
|
|
||||||
};
|
|
||||||
|
|
||||||
let headers_mut = req.headers_mut();
|
let headers_mut = req.headers_mut();
|
||||||
headers_mut.insert(
|
headers_mut.insert(USER_AGENT, self.user_agent.clone());
|
||||||
USER_AGENT,
|
|
||||||
// Some features like lyrics are version-gated and require an official version string.
|
|
||||||
HeaderValue::from_str(&format!(
|
|
||||||
"Spotify/{} {} ({})",
|
|
||||||
spotify_version, spotify_platform, VERSION_STRING
|
|
||||||
))?,
|
|
||||||
);
|
|
||||||
|
|
||||||
let request = if let Some(url) = &self.proxy {
|
let request = if let Some(url) = &self.proxy {
|
||||||
let proxy_uri = url.to_string().parse()?;
|
let proxy_uri = url.to_string().parse()?;
|
||||||
|
|
|
@ -1151,7 +1151,6 @@ fn get_setup() -> Setup {
|
||||||
};
|
};
|
||||||
|
|
||||||
let session_config = SessionConfig {
|
let session_config = SessionConfig {
|
||||||
user_agent: version::VERSION_STRING.to_string(),
|
|
||||||
device_id: device_id(&connect_config.name),
|
device_id: device_id(&connect_config.name),
|
||||||
proxy: opt_str(PROXY).or_else(|| std::env::var("http_proxy").ok()).map(
|
proxy: opt_str(PROXY).or_else(|| std::env::var("http_proxy").ok()).map(
|
||||||
|s| {
|
|s| {
|
||||||
|
|
Loading…
Reference in a new issue