diff --git a/CHANGELOG.md b/CHANGELOG.md index e0c418b6c..7a4768e1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [main] Fixed `--volume-ctrl fixed` not disabling volume control - [core] Fix default permissions on credentials file and warn user if file is world readable - [core] Try all resolved addresses for the dealer connection instead of failing after the first one. +- [discovery] Return an HTTP error response instead of panicking on malformed discovery login requests ## [0.8.0] - 2025-11-10 diff --git a/discovery/src/server.rs b/discovery/src/server.rs index f4ce6152e..9ed83cc10 100644 --- a/discovery/src/server.rs +++ b/discovery/src/server.rs @@ -9,7 +9,6 @@ use aes::cipher::{KeyIvInit, StreamCipher}; use base64::engine::Engine as _; use base64::engine::general_purpose::STANDARD as BASE64; use bytes::Bytes; -use futures_util::{FutureExt, TryFutureExt}; use hmac::{Hmac, Mac}; use http_body_util::{BodyExt, Full}; use hyper::{Method, Request, Response, StatusCode, body::Incoming}; @@ -24,7 +23,7 @@ use super::{DiscoveryError, DiscoveryEvent}; use crate::{ core::config::DeviceType, - core::{Error, authentication::Credentials, diffie_hellman::DhLocalKeys}, + core::{Error, authentication::Credentials, diffie_hellman::DhLocalKeys, error::ErrorKind}, }; type Aes128Ctr = ctr::Ctr128BE; @@ -234,10 +233,28 @@ impl RequestHandler { res } + fn error_response(&self, err: &Error) -> Response> { + let status = match err.kind { + ErrorKind::InvalidArgument | ErrorKind::FailedPrecondition => StatusCode::BAD_REQUEST, + _ => StatusCode::SERVICE_UNAVAILABLE, + }; + + let body = json!({ + "status": 102, + "spotifyError": 0, + "statusString": status.canonical_reason().unwrap_or("ERROR"), + }) + .to_string(); + + let mut res = Response::new(Full::new(Bytes::from(body))); + *res.status_mut() = status; + res + } + async fn handle( self: Arc, request: Request, - ) -> Result>>, Error> { + ) -> hyper::Result>> { let mut params = Params::new(); let (parts, body) = request.into_parts(); @@ -257,11 +274,17 @@ impl RequestHandler { let action = params.get("action").map(Cow::as_ref); - Ok(Ok(match (parts.method, action) { + Ok(match (parts.method, action) { (Method::GET, Some("getInfo")) => self.handle_get_info(), - (Method::POST, Some("addUser")) => self.handle_add_user(¶ms)?, + (Method::POST, Some("addUser")) => match self.handle_add_user(¶ms) { + Ok(response) => response, + Err(err) => { + error!("could not handle discovery request: {err}"); + self.error_response(&err) + } + }, _ => self.not_found(), - })) + }) } } @@ -325,12 +348,7 @@ impl DiscoveryServer { let discovery = discovery.clone(); let svc = hyper::service::service_fn(move |request| { - discovery - .clone() - .handle(request) - .inspect_err(|e| error!("could not handle discovery request: {e}")) - .and_then(|x| async move { Ok(x) }) - .map(Result::unwrap) // guaranteed by `and_then` above + discovery.clone().handle(request) }); let conn = server.serve_connection(io, svc);