feat(download): resume interrupted transfers

This commit is contained in:
Michel de Graaf
2026-09-09 15:49:35 +02:00
parent 7f84998c2e
commit 926c1c6697
9 changed files with 1032 additions and 332 deletions
+4 -19
View File
@@ -1,8 +1,8 @@
use super::{
Arc, Client, ClientContext, ClientOperation, ConnectionType, DownloadPeer,
DownloadStatus, Listen, Peer, PeerRegistry, Receiver, Result, RwLock,
RwLockExt, Sender, ServerActor, ServerMessage, Shares, SoulseekRs,
TcpStream, error, info, mpsc, thread, trace, warn,
Listen, Peer, PeerRegistry, Receiver, Result, RwLock, RwLockExt, Sender,
ServerActor, ServerMessage, Shares, SoulseekRs, TcpStream, error, info,
mpsc, thread, trace, warn,
};
/// Ceiling on the wait for a login verdict. Generous enough for a slow server
@@ -282,27 +282,12 @@ impl Client {
own_username,
);
match download_peer.download_file(
client_context.clone(),
None,
None,
) {
match download_peer.download_file(client_context, None, None) {
Ok((download, filename)) => {
trace!(
"[client] downloaded {} bytes {:?} ",
filename, download.size
);
let _ = download.sender.send(DownloadStatus::Completed);
match client_context.write_safe() {
Ok(mut ctx) => ctx.update_download_with_status(
download.token,
DownloadStatus::Completed,
),
Err(e) => error!(
"[client] connect_to_peer F write: {}",
e
),
}
}
Err(e) => {
trace!("[client] failed to download: {}", e);
+17 -74
View File
@@ -135,52 +135,33 @@ impl Client {
}
}
ClientOperation::DownloadFromPeer(token, peer, allowed) => {
let maybe_download = match client_context.read_safe() {
Ok(ctx) => {
ctx.get_download_by_token(token).cloned()
}
let maybe_download = match client_context.write_safe() {
Ok(mut ctx) => ctx
.downloads
.claim_for_peer(token, &peer.username),
Err(e) => {
error!("[client] DownloadFromPeer read: {}", e);
error!(
"[client] DownloadFromPeer write: {}",
e
);
continue;
}
};
let own_username = own_username.clone();
let client_context_clone = client_context.clone();
trace!(
"[client] DownloadFromPeer token: {} peer: {:?}",
token, peer
);
let Some(download) = maybe_download else {
error!(
"Can't find download with token {:?}",
token
debug!(
"[client] transfer token {} is missing, belongs \
to another peer, or is already claimed; ignoring",
token,
);
continue;
};
if matches!(
download.status,
DownloadStatus::InProgress { .. }
| DownloadStatus::Completed
| DownloadStatus::Cancelled
) {
debug!(
"[client] transfer token {} already claimed; \
ignoring replayed TransferResponse",
token
);
continue;
}
if let Ok(mut ctx) = client_context.write_safe() {
ctx.update_download_with_status(
token,
DownloadStatus::InProgress {
bytes_downloaded: 0,
total_bytes: download.size,
speed_bytes_per_sec: 0.0,
},
);
}
let own_username = own_username.clone();
let client_context = client_context.clone();
thread::spawn(move || {
let download_peer = DownloadPeer::new(
@@ -191,35 +172,12 @@ impl Client {
allowed,
own_username,
);
let Some(filename) =
download.filename.split('\\').next_back()
else {
error!(
"Cant find filename to save download: {:?}",
download.filename
);
return;
};
match download_peer.download_file(
client_context_clone.clone(),
client_context,
Some(download.clone()),
None,
) {
Ok((download, filename)) => {
let _ = download
.sender
.send(DownloadStatus::Completed);
match client_context_clone.write_safe() {
Ok(mut ctx) => ctx
.update_download_with_status(
download.token,
DownloadStatus::Completed,
),
Err(e) => error!(
"[client] download complete write: {}",
e
),
}
info!(
"Successfully downloaded {} bytes to {}",
download.size, filename
@@ -227,24 +185,9 @@ impl Client {
}
Err(DownloadError::Cancelled) => {}
Err(e) => {
let reason = Some(e.to_string());
let _ = download.sender.send(
DownloadStatus::Failed(reason.clone()),
);
match client_context_clone.write_safe() {
Ok(mut ctx) => ctx
.update_download_with_status(
download.token,
DownloadStatus::Failed(reason),
),
Err(e) => error!(
"[client] download failed write: {}",
e
),
}
error!(
"Failed to download file '{}' from {}:{} (token: {}) - Error: {}",
filename,
download.filename,
peer.host,
peer.port,
download.token,
+9 -7
View File
@@ -567,16 +567,17 @@ fn a_replayed_transfer_response_does_not_start_a_second_transfer() {
let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
listener.set_nonblocking(true).unwrap();
let port = u32::from(listener.local_addr().unwrap().port());
let download_dir = std::env::temp_dir()
.join(format!("soulseek-replayed-transfer-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&download_dir);
std::fs::create_dir_all(&download_dir).unwrap();
let client = Client::new("u", "p");
let (sender, _receiver) = mpsc::channel();
client.context.write().unwrap().add_download(download(
"peer",
"f.mp3",
9,
DownloadStatus::Queued,
sender,
));
let mut queued =
download("peer", "f.mp3", 9, DownloadStatus::Queued, sender);
queued.download_directory = download_dir.display().to_string();
client.context.write().unwrap().add_download(queued);
let (ops_tx, ops_rx) = mpsc::channel();
Client::listen_to_client_operations(
@@ -625,6 +626,7 @@ fn a_replayed_transfer_response_does_not_start_a_second_transfer() {
connections, 1,
"a replayed TransferResponse must not dial the peer again"
);
let _ = std::fs::remove_dir_all(download_dir);
}
#[test]