From 9d88ac59c63ec373345681f60fcf3eec6bdf369a Mon Sep 17 00:00:00 2001 From: Roderick van Domburg Date: Sat, 18 Dec 2021 12:39:16 +0100 Subject: [PATCH] Configure User-Agent once --- audio/src/fetch/mod.rs | 2 ++ core/src/config.rs | 2 -- core/src/http_client.rs | 71 ++++++++++++++++++++++------------------- src/main.rs | 1 - 4 files changed, 40 insertions(+), 36 deletions(-) diff --git a/audio/src/fetch/mod.rs b/audio/src/fetch/mod.rs index c4f6c72f..d60f5861 100644 --- a/audio/src/fetch/mod.rs +++ b/audio/src/fetch/mod.rs @@ -363,6 +363,8 @@ impl AudioFileStreaming { let mut cdn_url = CdnUrl::new(file_id).resolve_audio(&session).await?; let url = cdn_url.get_url()?; + trace!("Streaming {:?}", url); + let mut streamer = session.spclient().stream_file(url, 0, download_size)?; let request_time = Instant::now(); diff --git a/core/src/config.rs b/core/src/config.rs index 24b6a88e..c6b3d23c 100644 --- a/core/src/config.rs +++ b/core/src/config.rs @@ -5,7 +5,6 @@ use url::Url; #[derive(Clone, Debug)] pub struct SessionConfig { - pub user_agent: String, pub device_id: String, pub proxy: Option, pub ap_port: Option, @@ -16,7 +15,6 @@ impl Default for SessionConfig { fn default() -> SessionConfig { let device_id = uuid::Uuid::new_v4().to_hyphenated().to_string(); SessionConfig { - user_agent: crate::version::VERSION_STRING.to_string(), device_id, proxy: None, ap_port: None, diff --git a/core/src/http_client.rs b/core/src/http_client.rs index 21624e1a..ebd7aefd 100644 --- a/core/src/http_client.rs +++ b/core/src/http_client.rs @@ -4,18 +4,20 @@ use futures_util::FutureExt; use http::header::HeaderValue; use http::uri::InvalidUri; 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_proxy::{Intercept, Proxy, ProxyConnector}; use hyper_rustls::HttpsConnector; -use rustls::ClientConfig; -use std::env::consts::OS; +use rustls::{ClientConfig, RootCertStore}; use thiserror::Error; use url::Url; +use std::env::consts::OS; + use crate::version::{SPOTIFY_MOBILE_VERSION, SPOTIFY_VERSION, VERSION_STRING}; pub struct HttpClient { + user_agent: HeaderValue, proxy: Option, tls_config: ClientConfig, } @@ -34,12 +36,6 @@ pub enum HttpClientError { ProxyBuilder(#[from] std::io::Error), } -impl From for HttpClientError { - fn from(err: InvalidHeaderValue) -> Self { - Self::Parsing(err.into()) - } -} - impl From for HttpClientError { fn from(err: InvalidUri) -> Self { Self::Parsing(err.into()) @@ -48,6 +44,30 @@ impl From for HttpClientError { impl HttpClient { 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 let root_store = match rustls_native_certs::load_native_certs() { Ok(store) => store, @@ -55,7 +75,11 @@ impl HttpClient { warn!("Could not load all certificates: {:?}", err); 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(); @@ -63,12 +87,15 @@ impl HttpClient { tls_config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()]; Self { + user_agent, proxy: proxy.cloned(), tls_config, } } pub async fn request(&self, req: Request) -> Result, HttpClientError> { + debug!("Requesting {:?}", req.uri().to_string()); + let request = self.request_fut(req)?; { let response = request.await; @@ -97,34 +124,12 @@ impl HttpClient { } pub fn request_fut(&self, mut req: Request) -> Result { - trace!("Requesting {:?}", req.uri().to_string()); - let mut http = HttpConnector::new(); http.enforce_http(false); 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(); - headers_mut.insert( - 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 - ))?, - ); + headers_mut.insert(USER_AGENT, self.user_agent.clone()); let request = if let Some(url) = &self.proxy { let proxy_uri = url.to_string().parse()?; diff --git a/src/main.rs b/src/main.rs index 8ff9f8b6..6bfb027b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1151,7 +1151,6 @@ fn get_setup() -> Setup { }; let session_config = SessionConfig { - user_agent: version::VERSION_STRING.to_string(), device_id: device_id(&connect_config.name), proxy: opt_str(PROXY).or_else(|| std::env::var("http_proxy").ok()).map( |s| {