fix(backend): Support TCP SCGI sockets and remove verbose unstandardized tracker fields
All checks were successful
Build MIPS Binary / build (push) Successful in 2m4s

This commit is contained in:
spinline
2026-02-21 20:00:00 +03:00
parent 9cfea2aed5
commit 401ccb69b2
4 changed files with 128 additions and 12 deletions

View File

@@ -0,0 +1,83 @@
use shared::xmlrpc::{RtorrentClient, RpcParam, parse_multicall_response};
#[tokio::main]
async fn main() {
let mut client = None;
for path in ["127.0.0.1:8000", "0.0.0.0:8000", "localhost:8000"] {
let test_client = RtorrentClient::new(path);
match test_client.call("system.client_version", &[]).await {
Ok(res) => {
println!("SUCCESS: Connected to rTorrent at {} (Version: {})", path, res);
client = Some(test_client);
break;
}
Err(_) => {
// println!("Failed to connect to {}", path);
}
}
}
let client = match client {
Some(c) => c,
None => {
println!("Could not connect to rTorrent on port 8000.");
return;
}
};
let mut hash = String::new();
match client.call("d.multicall2", &[RpcParam::from(""), RpcParam::from("main"), RpcParam::from("d.hash=")]).await {
Ok(xml) => {
if let Ok(rows) = parse_multicall_response(&xml) {
if let Some(row) = rows.first() {
if let Some(h) = row.first() {
hash = h.clone();
println!("Using torrent hash: {}", hash);
}
}
}
},
Err(e) => {
println!("Error getting torrents: {:?}", e);
return;
},
}
if hash.is_empty() {
println!("No torrents found to test trackers.");
return;
}
// Now test Tracker fields one by one to see which one is failing
let fields = vec![
"t.url=",
"t.is_enabled=",
"t.group=",
"t.scrape_complete=",
"t.scrape_incomplete=",
"t.scrape_downloaded=",
"t.activity_date_last=",
"t.normal_interval=",
"t.message=",
];
for field in &fields {
let params = vec![
RpcParam::from(hash.as_str()),
RpcParam::from(""),
RpcParam::from(*field),
];
print!("Testing field {:<22} : ", field);
match client.call("t.multicall", &params).await {
Ok(xml) => {
if xml.contains("faultCode") {
println!("FAILED");
} else {
println!("SUCCESS");
}
},
Err(e) => println!("ERROR: {:?}", e),
}
}
}

View File

@@ -83,12 +83,19 @@ impl ScgiRequest {
pub async fn send_request(socket_path: &str, request: ScgiRequest) -> Result<Bytes, ScgiError> { pub async fn send_request(socket_path: &str, request: ScgiRequest) -> Result<Bytes, ScgiError> {
let perform_request = async { let perform_request = async {
let mut stream = UnixStream::connect(socket_path).await?;
let data = request.encode(); let data = request.encode();
stream.write_all(&data).await?;
let mut response = Vec::new(); let mut response = Vec::new();
stream.read_to_end(&mut response).await?;
if socket_path.contains(':') {
let mut stream = tokio::net::TcpStream::connect(socket_path).await?;
stream.write_all(&data).await?;
stream.read_to_end(&mut response).await?;
} else {
let mut stream = tokio::net::UnixStream::connect(socket_path).await?;
stream.write_all(&data).await?;
stream.read_to_end(&mut response).await?;
}
Ok::<Vec<u8>, std::io::Error>(response) Ok::<Vec<u8>, std::io::Error>(response)
}; };

View File

@@ -198,9 +198,7 @@ pub async fn get_trackers(hash: String) -> Result<Vec<TorrentTracker>, ServerFnE
RpcParam::from("t.scrape_complete="), RpcParam::from("t.scrape_complete="),
RpcParam::from("t.scrape_incomplete="), RpcParam::from("t.scrape_incomplete="),
RpcParam::from("t.scrape_downloaded="), RpcParam::from("t.scrape_downloaded="),
RpcParam::from("t.activity_date_last="),
RpcParam::from("t.normal_interval="), RpcParam::from("t.normal_interval="),
RpcParam::from("t.message="),
]; ];
let xml = client let xml = client
@@ -211,7 +209,7 @@ pub async fn get_trackers(hash: String) -> Result<Vec<TorrentTracker>, ServerFnE
let rows = parse_multicall_response(&xml) let rows = parse_multicall_response(&xml)
.map_err(|e| ServerFnError::new(format!("Parse error: {}", e)))?; .map_err(|e| ServerFnError::new(format!("Parse error: {}", e)))?;
Ok(rows let result: Vec<TorrentTracker> = rows
.into_iter() .into_iter()
.map(|row| TorrentTracker { .map(|row| TorrentTracker {
url: row.get(0).cloned().unwrap_or_default(), url: row.get(0).cloned().unwrap_or_default(),
@@ -220,12 +218,14 @@ pub async fn get_trackers(hash: String) -> Result<Vec<TorrentTracker>, ServerFnE
seeders: row.get(3).and_then(|s| s.parse().ok()).unwrap_or(0), seeders: row.get(3).and_then(|s| s.parse().ok()).unwrap_or(0),
peers: row.get(4).and_then(|s| s.parse().ok()).unwrap_or(0), peers: row.get(4).and_then(|s| s.parse().ok()).unwrap_or(0),
downloaded: row.get(5).and_then(|s| s.parse().ok()).unwrap_or(0), downloaded: row.get(5).and_then(|s| s.parse().ok()).unwrap_or(0),
last_updated: row.get(6).and_then(|s| s.parse().ok()).unwrap_or(0), interval: row.get(6).and_then(|s| s.parse().ok()).unwrap_or(0),
interval: row.get(7).and_then(|s| s.parse().ok()).unwrap_or(0), last_updated: 0,
status: "Unknown".to_string(), // Can derive from message or activity later, or keep unknown status: "Unknown".to_string(), // Can derive from activity later, or keep unknown
message: row.get(8).cloned().unwrap_or_default(), message: "".to_string(),
}) })
.collect()) .collect();
Ok(result)
} }
#[server(SetFilePriority, "/api/server_fns")] #[server(SetFilePriority, "/api/server_fns")]

26
test_rpc.rs Normal file
View File

@@ -0,0 +1,26 @@
use shared::xmlrpc::{RtorrentClient, RpcParam, parse_multicall_response};
#[tokio::main]
async fn main() {
let client = RtorrentClient::new("/tmp/rtorrent.sock");
// Hardcode a known hash from the UI, e.g. "C3315ABFAD70C54505813D1303C1457900C5B795" (from first image)
let hash = "C3315ABFAD70C54505813D1303C1457900C5B795";
let params = vec![
RpcParam::from(hash),
RpcParam::from(""),
RpcParam::from("t.url="),
];
match client.call("t.multicall", &params).await {
Ok(xml) => {
println!("Response XML:\n{}", xml);
match parse_multicall_response(&xml) {
Ok(rows) => println!("Rows ({})", rows.len()),
Err(e) => println!("Parse error: {:?}", e),
}
},
Err(e) => println!("Error: {:?}", e),
}
}