Skip to content

Commit

Permalink
Cycle through AP if blocked (#532)
Browse files Browse the repository at this point in the history
  • Loading branch information
jacksongoode authored Nov 12, 2024
1 parent a497d93 commit 69314f9
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 36 deletions.
67 changes: 47 additions & 20 deletions psst-core/src/connection/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,42 +99,69 @@ pub struct Transport {
}

impl Transport {
pub fn resolve_ap_with_fallback(proxy_url: Option<&str>) -> String {
pub fn resolve_ap_with_fallback(proxy_url: Option<&str>) -> Vec<String> {
match Self::resolve_ap(proxy_url) {
Ok(ap) => ap,
Ok(ap_list) => {
log::info!("successfully resolved {} access points", ap_list.len());
ap_list
}
Err(err) => {
log::error!("using AP fallback, error while resolving: {:?}", err);
AP_FALLBACK.into()
log::error!("error while resolving APs, using fallback: {:?}", err);
vec![AP_FALLBACK.into()]
}
}
}

pub fn resolve_ap(proxy_url: Option<&str>) -> Result<String, Error> {
pub fn resolve_ap(proxy_url: Option<&str>) -> Result<Vec<String>, Error> {
#[derive(Clone, Debug, Deserialize)]
struct APResolveData {
ap_list: Vec<String>,
}

let agent = default_ureq_agent_builder(proxy_url)?.build();
log::info!("requesting AP list from {}", AP_RESOLVE_ENDPOINT);
let data: APResolveData = agent.get(AP_RESOLVE_ENDPOINT).call()?.into_json()?;
data.ap_list
.into_iter()
.next()
.ok_or(Error::UnexpectedResponse)
if data.ap_list.is_empty() {
log::warn!("received empty AP list from server");
Err(Error::UnexpectedResponse)
} else {
log::info!("received {} APs from server", data.ap_list.len());
Ok(data.ap_list)
}
}

pub fn connect(ap: &str, proxy_url: Option<&str>) -> Result<Self, Error> {
log::trace!("connecting to: {:?} with proxy: {:?}", ap, proxy_url);
let stream = if let Some(url) = proxy_url {
Self::stream_through_proxy(ap, url)?
} else {
Self::stream_without_proxy(ap)?
};
if let Err(err) = stream.set_write_timeout(Some(NET_IO_TIMEOUT)) {
log::warn!("failed to set TCP write timeout: {:?}", err);
pub fn connect(ap_list: &[String], proxy_url: Option<&str>) -> Result<Self, Error> {
log::info!(
"attempting to connect using {} access points",
ap_list.len()
);
for (index, ap) in ap_list.iter().enumerate() {
log::info!("Trying AP {} of {}: {}", index + 1, ap_list.len(), ap);
let stream = if let Some(url) = proxy_url {
match Self::stream_through_proxy(ap, url) {
Ok(s) => s,
Err(e) => {
log::warn!("failed to connect to AP {} through proxy: {:?}", ap, e);
continue;
}
}
} else {
match Self::stream_without_proxy(ap) {
Ok(s) => s,
Err(e) => {
log::warn!("failed to connect to AP {} without proxy: {:?}", ap, e);
continue;
}
}
};
if let Err(err) = stream.set_write_timeout(Some(NET_IO_TIMEOUT)) {
log::warn!("failed to set TCP write timeout: {:?}", err);
}
log::info!("successfully connected to AP: {}", ap);
return Self::exchange_keys(stream);
}
log::trace!("connected");
Self::exchange_keys(stream)
log::error!("failed to connect to any access point");
Err(Error::ConnectionFailed)
}

fn stream_without_proxy(ap: &str) -> Result<TcpStream, io::Error> {
Expand Down
2 changes: 2 additions & 0 deletions psst-core/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub enum Error {
MediaFileNotFound,
ProxyUrlInvalid,
AuthFailed { code: i32 },
ConnectionFailed,
JsonError(Box<dyn error::Error + Send>),
AudioFetchingError(Box<dyn error::Error + Send>),
AudioDecodingError(Box<dyn error::Error + Send>),
Expand Down Expand Up @@ -40,6 +41,7 @@ impl fmt::Display for Error {
17 => write!(f, "Authentication failed: application banned"),
_ => write!(f, "Authentication failed with error code {}", code),
},
Self::ConnectionFailed => write!(f, "Failed to connect to any access point"),
Self::ResamplingError(code) => {
write!(f, "Resampling failed with error code {}", code)
}
Expand Down
4 changes: 2 additions & 2 deletions psst-core/src/oauth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ pub fn get_authcode_listener(
socket_address: SocketAddr,
timeout: Duration,
) -> Result<AuthorizationCode, String> {
log::info!("Starting OAuth listener on {:?}", socket_address);
log::info!("starting OAuth listener on {:?}", socket_address);
let listener = TcpListener::bind(socket_address)
.map_err(|e| format!("Failed to bind to address: {}", e))?;
log::info!("Listener bound successfully");
log::info!("listener bound successfully");

let (tx, rx) = mpsc::channel();

Expand Down
6 changes: 2 additions & 4 deletions psst-core/src/session/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,10 +133,8 @@ impl SessionConnection {
pub fn open(config: SessionConfig) -> Result<Self, Error> {
// Connect to the server and exchange keys.
let proxy_url = config.proxy_url.as_deref();
let ap_url = Transport::resolve_ap_with_fallback(proxy_url);
let mut transport = Transport::connect(&ap_url, proxy_url)?;
// Authenticate with provided credentials (either username/password, or saved,
// reusable credential blob from an earlier run).
let ap_list = Transport::resolve_ap_with_fallback(proxy_url);
let mut transport = Transport::connect(&ap_list, proxy_url)?;
let credentials = transport.authenticate(config.login_creds)?;
Ok(Self {
credentials,
Expand Down
2 changes: 1 addition & 1 deletion psst-gui/src/controller/playback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ impl PlaybackController {
fn send(&mut self, event: PlayerEvent) {
if let Some(s) = &self.sender {
s.send(event)
.map_err(|e| log::error!("Error sending message: {:?}", e))
.map_err(|e| log::error!("error sending message: {:?}", e))
.ok();
}
}
Expand Down
2 changes: 1 addition & 1 deletion psst-gui/src/data/album.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ impl Album {

pub fn release_year_int(&self) -> usize {
self.release_year().parse::<usize>().unwrap_or_else(|err| {
log::error!("Error parsing release year for {}: {}", self.name, err);
log::error!("error parsing release year for {}: {}", self.name, err);
usize::MAX
})
}
Expand Down
39 changes: 31 additions & 8 deletions psst-gui/src/ui/preferences.rs
Original file line number Diff line number Diff line change
Expand Up @@ -347,14 +347,37 @@ impl<W: Widget<AppState>> Controller<AppState, W> for Authenticate {
) {
Ok(code) => {
let token = oauth::exchange_code_for_token(8888, code, pkce_verifier);
let response =
Authentication::authenticate_and_get_credentials(SessionConfig {
login_creds: Credentials::from_access_token(token),
..config
});
event_sink
.submit_command(Self::RESPONSE, response, widget_id)
.unwrap();
let mut retries = 3;
while retries > 0 {
let response = Authentication::authenticate_and_get_credentials(
SessionConfig {
login_creds: Credentials::from_access_token(token.clone()),
..config.clone()
},
);
match response {
Ok(credentials) => {
event_sink
.submit_command(
Self::RESPONSE,
Ok(credentials),
widget_id,
)
.unwrap();
return;
}
Err(e) if retries > 1 => {
log::warn!("authentication failed, retrying: {:?}", e);
retries -= 1;
}
Err(e) => {
event_sink
.submit_command(Self::RESPONSE, Err(e), widget_id)
.unwrap();
return;
}
}
}
}
Err(e) => {
event_sink
Expand Down

0 comments on commit 69314f9

Please sign in to comment.