From 1fe2c7998de1ffaa4f1b62bc302cd2efb1f170df Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Sat, 7 Mar 2026 16:55:43 +0300 Subject: [PATCH] pinakes-server: eliminate unwraps from response builders Signed-off-by: NotAShelf Change-Id: I6d80e505963dfa4d117f6b33d69fc1516a6a6964 --- crates/pinakes-server/src/app.rs | 32 +++---- crates/pinakes-server/src/routes/streaming.rs | 85 ++++++++++--------- crates/pinakes-server/src/routes/subtitles.rs | 14 +-- 3 files changed, 67 insertions(+), 64 deletions(-) diff --git a/crates/pinakes-server/src/app.rs b/crates/pinakes-server/src/app.rs index 58b9d3e..778a2fa 100644 --- a/crates/pinakes-server/src/app.rs +++ b/crates/pinakes-server/src/app.rs @@ -33,35 +33,35 @@ pub fn create_router_with_tls( .per_second(1) .burst_size(100) .finish() - .unwrap(), + .expect("valid global rate limit config"), ); // Strict rate limit for login: 5 requests/min per IP let login_governor = Arc::new( GovernorConfigBuilder::default() - .per_second(12) // replenish one every 12 seconds - .burst_size(5) - .finish() - .unwrap(), + .per_second(12) // replenish one every 12 seconds + .burst_size(5) + .finish() + .expect("valid login rate limit config"), ); // Rate limit for search: 10 requests/min per IP let search_governor = Arc::new( GovernorConfigBuilder::default() - .per_second(6) // replenish one every 6 seconds (10/min) - .burst_size(10) - .finish() - .unwrap(), + .per_second(6) // replenish one every 6 seconds (10/min) + .burst_size(10) + .finish() + .expect("valid search rate limit config"), ); // Rate limit for streaming: 5 requests per IP (very restrictive for // concurrent streams) let stream_governor = Arc::new( GovernorConfigBuilder::default() - .per_second(60) // replenish slowly (one per minute) - .burst_size(5) // max 5 concurrent connections - .finish() - .unwrap(), + .per_second(60) // replenish slowly (one per minute) + .burst_size(5) // max 5 concurrent connections + .finish() + .expect("valid stream rate limit config"), ); // Login route with strict rate limiting @@ -501,9 +501,9 @@ pub fn create_router_with_tls( // CORS: allow same-origin by default, plus the desktop UI origin let cors = CorsLayer::new() .allow_origin([ - "http://localhost:3000".parse::().unwrap(), - "http://127.0.0.1:3000".parse::().unwrap(), - "tauri://localhost".parse::().unwrap(), + HeaderValue::from_static("http://localhost:3000"), + HeaderValue::from_static("http://127.0.0.1:3000"), + HeaderValue::from_static("tauri://localhost"), ]) .allow_methods([ Method::GET, diff --git a/crates/pinakes-server/src/routes/streaming.rs b/crates/pinakes-server/src/routes/streaming.rs index 6735eca..e3d1aa0 100644 --- a/crates/pinakes-server/src/routes/streaming.rs +++ b/crates/pinakes-server/src/routes/streaming.rs @@ -11,6 +11,36 @@ use uuid::Uuid; use crate::{error::ApiError, state::AppState}; +fn build_response( + content_type: &str, + body: impl Into, +) -> Result { + axum::response::Response::builder() + .header("Content-Type", content_type) + .body(body.into()) + .map_err(|e| { + ApiError(pinakes_core::error::PinakesError::InvalidOperation( + format!("failed to build response: {e}"), + )) + }) +} + +fn build_response_with_status( + status: StatusCode, + headers: &[(&str, &str)], + body: impl Into, +) -> Result { + let mut builder = axum::response::Response::builder().status(status); + for (name, value) in headers { + builder = builder.header(*name, *value); + } + builder.body(body.into()).map_err(|e| { + ApiError(pinakes_core::error::PinakesError::InvalidOperation( + format!("failed to build response: {e}"), + )) + }) +} + fn escape_xml(s: &str) -> String { s.replace('&', "&") .replace('<', "<") @@ -42,12 +72,7 @@ pub async fn hls_master_playlist( )); } - Ok( - axum::response::Response::builder() - .header("Content-Type", "application/vnd.apple.mpegurl") - .body(axum::body::Body::from(playlist)) - .unwrap(), - ) + build_response("application/vnd.apple.mpegurl", playlist) } pub async fn hls_variant_playlist( @@ -84,12 +109,7 @@ pub async fn hls_variant_playlist( } playlist.push_str("#EXT-X-ENDLIST\n"); - Ok( - axum::response::Response::builder() - .header("Content-Type", "application/vnd.apple.mpegurl") - .body(axum::body::Body::from(playlist)) - .unwrap(), - ) + build_response("application/vnd.apple.mpegurl", playlist) } pub async fn hls_segment( @@ -127,21 +147,14 @@ pub async fn hls_segment( )) })?; - return Ok( - axum::response::Response::builder() - .header("Content-Type", "video/MP2T") - .body(axum::body::Body::from(data)) - .unwrap(), - ); + return build_response("video/MP2T", data); } // Session exists but segment not ready yet - return Ok( - axum::response::Response::builder() - .status(StatusCode::ACCEPTED) - .header("Retry-After", "2") - .body(axum::body::Body::from("segment not yet available")) - .unwrap(), + return build_response_with_status( + StatusCode::ACCEPTED, + &[("Retry-After", "2")], + "segment not yet available", ); } @@ -200,12 +213,7 @@ pub async fn dash_manifest( "# ); - Ok( - axum::response::Response::builder() - .header("Content-Type", "application/dash+xml") - .body(axum::body::Body::from(mpd)) - .unwrap(), - ) + build_response("application/dash+xml", mpd) } pub async fn dash_segment( @@ -242,20 +250,13 @@ pub async fn dash_segment( )) })?; - return Ok( - axum::response::Response::builder() - .header("Content-Type", "video/mp4") - .body(axum::body::Body::from(data)) - .unwrap(), - ); + return build_response("video/mp4", data); } - return Ok( - axum::response::Response::builder() - .status(StatusCode::ACCEPTED) - .header("Retry-After", "2") - .body(axum::body::Body::from("segment not yet available")) - .unwrap(), + return build_response_with_status( + StatusCode::ACCEPTED, + &[("Retry-After", "2")], + "segment not yet available", ); } diff --git a/crates/pinakes-server/src/routes/subtitles.rs b/crates/pinakes-server/src/routes/subtitles.rs index d4742ef..230dc4d 100644 --- a/crates/pinakes-server/src/routes/subtitles.rs +++ b/crates/pinakes-server/src/routes/subtitles.rs @@ -97,12 +97,14 @@ pub async fn get_subtitle_content( SubtitleFormat::Srt => "application/x-subrip", _ => "text/plain", }; - Ok( - axum::response::Response::builder() - .header("Content-Type", content_type) - .body(axum::body::Body::from(content)) - .unwrap(), - ) + axum::response::Response::builder() + .header("Content-Type", content_type) + .body(axum::body::Body::from(content)) + .map_err(|e| { + ApiError(pinakes_core::error::PinakesError::InvalidOperation( + format!("failed to build response: {e}"), + )) + }) } else { Err(ApiError( pinakes_core::error::PinakesError::InvalidOperation(