fix(properties): harden protocol-aware resume recovery

This commit is contained in:
NimBold
2026-08-04 23:37:56 +03:30
parent 135ba75a69
commit 48a727798c
18 changed files with 645 additions and 95 deletions
-3
View File
@@ -302,9 +302,6 @@ pub struct TorrentPeer {
#[serde(default, skip_serializing_if = "Option::is_none")]
#[ts(optional)]
pub port: Option<u16>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[ts(optional)]
pub peer_id: Option<String>,
#[ts(type = "number")]
pub download_speed: u64,
#[ts(type = "number")]
+1 -1
View File
@@ -14253,7 +14253,7 @@ pub fn run() {
total_bytes: (total > 0).then_some(total as f64),
total_is_estimate: Some(false),
active_connections: Some(active_connections),
requested_connections: Some(requested_connections),
requested_connections: (!is_torrent).then_some(requested_connections),
uploaded_bytes: torrent_telemetry
.map(|value| value.uploaded_bytes as f64)
.or_else(|| uploaded_bytes.map(|value| value as f64)),
+1
View File
@@ -255,6 +255,7 @@ fn is_properties_action(action: &str) -> bool {
matches!(
action,
"apply-properties"
| "set-torrent-file-selection"
| "pause-resume"
| "verify-torrent"
| "set-download-limit"
+45 -15
View File
@@ -1773,7 +1773,13 @@ impl<R: tauri::Runtime> QueueManager<R> {
.lock()
.await
.get(id)
.and_then(|payload| payload.connections)
.and_then(|payload| {
if payload.is_torrent {
None
} else {
payload.connections
}
})
.map(clamp_download_connections)
}
@@ -5114,6 +5120,10 @@ fn apply_aria2_connection_options(
);
}
fn should_apply_aria2_connection_options(payload: &SpawnPayload) -> bool {
!payload.is_torrent
}
fn apply_aria2_follow_options(
options: &mut serde_json::Map<String, serde_json::Value>,
payload: &SpawnPayload,
@@ -5286,14 +5296,6 @@ fn aria2_peer_port(value: Option<&serde_json::Value>) -> Option<u16> {
}
}
fn aria2_peer_id(value: Option<&serde_json::Value>) -> Option<String> {
let value = value?.as_str()?.trim();
if value.is_empty() || value.len() > 128 || value.chars().any(char::is_control) {
return None;
}
Some(value.to_string())
}
fn aria2_peer_bool(value: Option<&serde_json::Value>) -> bool {
match value {
Some(serde_json::Value::Bool(value)) => *value,
@@ -5465,7 +5467,6 @@ pub(crate) fn parse_torrent_peer_diagnostics(
sanitized.push(crate::ipc::TorrentPeer {
ip: aria2_peer_ip(peer.get("ip")),
port: aria2_peer_port(peer.get("port")),
peer_id: aria2_peer_id(peer.get("peerId")),
download_speed: aria2_peer_number(peer.get("downloadSpeed")),
upload_speed: aria2_peer_number(peer.get("uploadSpeed")),
seeder,
@@ -6118,8 +6119,10 @@ impl SidecarSpawner for ProductionSpawner {
if !payload.is_torrent {
options.insert("out".to_string(), serde_json::json!(safe_filename));
}
let conn = effective_aria2_connections(id, payload).await;
apply_aria2_connection_options(&mut options, conn);
if should_apply_aria2_connection_options(payload) {
let conn = effective_aria2_connections(id, payload).await;
apply_aria2_connection_options(&mut options, conn);
}
apply_aria2_follow_options(&mut options, payload);
apply_aria2_torrent_options(&mut options, payload)?;
let mt = aria2_attempt_limit(payload.max_tries);
@@ -6897,6 +6900,33 @@ mod tests {
);
}
#[test]
fn torrent_payloads_do_not_use_generic_connection_options() {
let torrent = SpawnPayload {
is_torrent: true,
connections: Some(16),
..Default::default()
};
let normal = SpawnPayload {
is_torrent: false,
connections: Some(16),
..Default::default()
};
let mut torrent_options = serde_json::Map::new();
if should_apply_aria2_connection_options(&torrent) {
apply_aria2_connection_options(&mut torrent_options, 16);
}
assert!(!torrent_options.contains_key("split"));
assert!(!torrent_options.contains_key("max-connection-per-server"));
let mut normal_options = serde_json::Map::new();
if should_apply_aria2_connection_options(&normal) {
apply_aria2_connection_options(&mut normal_options, 16);
}
assert_eq!(normal_options.get("split"), Some(&serde_json::json!("16")));
assert_eq!(normal_options.get("max-connection-per-server"), Some(&serde_json::json!("16")));
}
#[test]
fn torrent_network_and_storage_settings_are_normalized_at_the_boundary() {
assert_eq!(
@@ -7647,7 +7677,7 @@ mod tests {
}
#[test]
fn torrent_peer_diagnostics_are_bounded_and_omit_bitfields() {
fn torrent_peer_diagnostics_are_bounded_and_omit_identity_and_bitfields() {
let mut result = vec![serde_json::json!({
"peerId": "secret-peer-id",
"ip": "192.0.2.10",
@@ -7678,11 +7708,11 @@ mod tests {
assert!(diagnostics.truncated);
assert_eq!(diagnostics.peers[0].ip.as_deref(), Some("192.0.2.10"));
assert_eq!(diagnostics.peers[0].port, Some(6881));
assert_eq!(diagnostics.peers[0].peer_id.as_deref(), Some("secret-peer-id"));
let serialized = serde_json::to_string(&diagnostics).unwrap();
assert!(serialized.contains("peerId"));
assert!(serialized.contains("192.0.2."));
assert!(!serialized.contains("\"port\":null"));
assert!(!serialized.contains("peerId"));
assert!(!serialized.contains("secret-peer-id"));
assert!(!serialized.contains("bitfield"));
}