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_utils::maybe_addr;
|
||||
use s2n_quic::{
|
||||
application,
|
||||
connection,
|
||||
stream::{Error as StreamError, SendStream},
|
||||
};
|
||||
@@ -14,6 +15,16 @@ use tokio::{
|
||||
|
||||
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)]
|
||||
async fn stream_file_bytes(
|
||||
tx: &mut SendStream,
|
||||
@@ -52,6 +63,7 @@ async fn stream_file_bytes(
|
||||
"{remote_addr} transfer cancelled for {}",
|
||||
validated_path.display()
|
||||
);
|
||||
cancel_send_stream(tx, remote_addr, &validated_path);
|
||||
return Err(eyre::eyre!("File transfer cancelled by user"));
|
||||
}
|
||||
|
||||
@@ -67,6 +79,7 @@ async fn stream_file_bytes(
|
||||
"{remote_addr} transfer cancelled for {}",
|
||||
validated_path.display()
|
||||
);
|
||||
cancel_send_stream(tx, remote_addr, &validated_path);
|
||||
return Err(eyre::eyre!("File transfer cancelled by user"));
|
||||
}
|
||||
res = file.read(&mut buf[..read_len]) => {
|
||||
@@ -86,6 +99,7 @@ async fn stream_file_bytes(
|
||||
"{remote_addr} transfer cancelled for {}",
|
||||
validated_path.display()
|
||||
);
|
||||
cancel_send_stream(tx, remote_addr, &validated_path);
|
||||
return Err(eyre::eyre!("File transfer cancelled by user"));
|
||||
}
|
||||
res = tx.send(Bytes::copy_from_slice(&buf[..bytes_read])) => {
|
||||
@@ -132,6 +146,7 @@ async fn stream_file_bytes(
|
||||
tokio::select! {
|
||||
() = cancel_token.cancelled() => {
|
||||
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"));
|
||||
}
|
||||
res = tx.close() => {
|
||||
|
||||
Reference in New Issue
Block a user