fix(peer): reset cancelled outbound file streams
Cancelled outbound transfers previously returned from the streaming loop without terminating the QUIC send half. A whole-file receiver relies on the stream ending to distinguish EOF from an in-progress body, so cancellation could leave it waiting on a truncated transfer until its own timeout fired. Reset the send stream on every cancellation branch, including cancellation while waiting for the final close acknowledgement. A reset is deliberately used instead of a graceful close so truncated whole-file transfers cannot be misinterpreted as a valid EOF. Test Plan: - just test - just clippy - git diff --check Refs: Claude review finding #1
This commit is contained in:
@@ -4,6 +4,7 @@ use bytes::Bytes;
|
|||||||
use lanspread_db::db::GameFileDescription;
|
use lanspread_db::db::GameFileDescription;
|
||||||
use lanspread_utils::maybe_addr;
|
use lanspread_utils::maybe_addr;
|
||||||
use s2n_quic::{
|
use s2n_quic::{
|
||||||
|
application,
|
||||||
connection,
|
connection,
|
||||||
stream::{Error as StreamError, SendStream},
|
stream::{Error as StreamError, SendStream},
|
||||||
};
|
};
|
||||||
@@ -14,6 +15,16 @@ use tokio::{
|
|||||||
|
|
||||||
use crate::{config::FILE_TRANSFER_BUFFER_SIZE, path_validation::validate_game_file_path};
|
use crate::{config::FILE_TRANSFER_BUFFER_SIZE, path_validation::validate_game_file_path};
|
||||||
|
|
||||||
|
fn cancel_send_stream(tx: &mut SendStream, remote_addr: impl std::fmt::Display, path: &Path) {
|
||||||
|
// Reset instead of finishing so truncated whole-file transfers cannot look like EOF.
|
||||||
|
if let Err(err) = tx.reset(application::Error::UNKNOWN) {
|
||||||
|
log::debug!(
|
||||||
|
"{remote_addr} failed to reset cancelled transfer for {}: {err}",
|
||||||
|
path.display()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_lines)]
|
#[allow(clippy::too_many_lines)]
|
||||||
async fn stream_file_bytes(
|
async fn stream_file_bytes(
|
||||||
tx: &mut SendStream,
|
tx: &mut SendStream,
|
||||||
@@ -52,6 +63,7 @@ async fn stream_file_bytes(
|
|||||||
"{remote_addr} transfer cancelled for {}",
|
"{remote_addr} transfer cancelled for {}",
|
||||||
validated_path.display()
|
validated_path.display()
|
||||||
);
|
);
|
||||||
|
cancel_send_stream(tx, remote_addr, &validated_path);
|
||||||
return Err(eyre::eyre!("File transfer cancelled by user"));
|
return Err(eyre::eyre!("File transfer cancelled by user"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,6 +79,7 @@ async fn stream_file_bytes(
|
|||||||
"{remote_addr} transfer cancelled for {}",
|
"{remote_addr} transfer cancelled for {}",
|
||||||
validated_path.display()
|
validated_path.display()
|
||||||
);
|
);
|
||||||
|
cancel_send_stream(tx, remote_addr, &validated_path);
|
||||||
return Err(eyre::eyre!("File transfer cancelled by user"));
|
return Err(eyre::eyre!("File transfer cancelled by user"));
|
||||||
}
|
}
|
||||||
res = file.read(&mut buf[..read_len]) => {
|
res = file.read(&mut buf[..read_len]) => {
|
||||||
@@ -86,6 +99,7 @@ async fn stream_file_bytes(
|
|||||||
"{remote_addr} transfer cancelled for {}",
|
"{remote_addr} transfer cancelled for {}",
|
||||||
validated_path.display()
|
validated_path.display()
|
||||||
);
|
);
|
||||||
|
cancel_send_stream(tx, remote_addr, &validated_path);
|
||||||
return Err(eyre::eyre!("File transfer cancelled by user"));
|
return Err(eyre::eyre!("File transfer cancelled by user"));
|
||||||
}
|
}
|
||||||
res = tx.send(Bytes::copy_from_slice(&buf[..bytes_read])) => {
|
res = tx.send(Bytes::copy_from_slice(&buf[..bytes_read])) => {
|
||||||
@@ -132,6 +146,7 @@ async fn stream_file_bytes(
|
|||||||
tokio::select! {
|
tokio::select! {
|
||||||
() = cancel_token.cancelled() => {
|
() = cancel_token.cancelled() => {
|
||||||
log::info!("{remote_addr} transfer cancelled while closing stream");
|
log::info!("{remote_addr} transfer cancelled while closing stream");
|
||||||
|
cancel_send_stream(tx, remote_addr, &validated_path);
|
||||||
return Err(eyre::eyre!("File transfer cancelled by user"));
|
return Err(eyre::eyre!("File transfer cancelled by user"));
|
||||||
}
|
}
|
||||||
res = tx.close() => {
|
res = tx.close() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user