feat(shares): serve queued files to peers over F connections

Implement the upload path so a peer can download our shared files:
- fix the mis-coded PlaceInQueueResponse (43 -> 44) and add a real QueueUpload
  handler (code 43) -> PeerMessage::IncomingQueueUpload;
- the peer actor forwards it as ClientOperation::QueueUpload; the client looks
  the file up in its shares, mints an upload token (high half of the token
  space, disjoint from md5 download tokens), and offers it via ServeUpload ->
  build_upload_transfer_request (peer code 40, direction=upload);
- a TransferResponse for one of our serving tokens is handled by a surgical
  early-return that emits StartUpload, leaving the download path untouched;
- StartUpload resolves the downloader's address (GetPeerAddress, cached) and
  streams the file via a new peer::upload_peer::serve_file over an F connection.

Tested: build_upload_transfer_request decodes via the production Transfer
parser; serve_file streams a file correctly over a loopback F connection.
This commit is contained in:
michel
2026-07-19 18:46:06 +02:00
parent 9b6e533bed
commit 393f69e478
8 changed files with 396 additions and 7 deletions
+2
View File
@@ -2,6 +2,7 @@ mod file_search_response;
mod get_share_file_list;
mod peer_init;
mod place_in_queue_response;
mod queue_upload;
mod transfer_request;
mod transfer_response;
mod upload_failed;
@@ -13,6 +14,7 @@ pub use file_search_response::{
pub use get_share_file_list::GetShareFileList;
pub use peer_init::PeerInit;
pub use place_in_queue_response::PlaceInQueueResponse;
pub use queue_upload::QueueUploadHandler;
pub use transfer_request::TransferRequest;
pub use transfer_response::TransferResponse;
pub use upload_failed::UploadFailedHandler;
@@ -8,7 +8,7 @@ pub struct PlaceInQueueResponse;
impl MessageHandler<PeerMessage> for PlaceInQueueResponse {
fn get_code(&self) -> u8 {
43
44
}
fn handle(&self, message: &mut Message, sender: Sender<PeerMessage>) {
@@ -0,0 +1,19 @@
use crate::{
message::{Message, MessageHandler},
peer::PeerMessage,
};
use std::sync::mpsc::Sender;
/// A peer asking to download one of our shared files (peer code 43).
pub struct QueueUploadHandler;
impl MessageHandler<PeerMessage> for QueueUploadHandler {
fn get_code(&self) -> u8 {
43
}
fn handle(&self, message: &mut Message, sender: Sender<PeerMessage>) {
let filename = message.read_string();
let _ = sender.send(PeerMessage::IncomingQueueUpload(filename));
}
}
@@ -142,6 +142,23 @@ impl MessageFactory {
.write_string(filename)
.clone()
}
/// A TransferRequest (peer code 40) initiating an *upload*: we offer a file
/// to a peer who queued it, quoting our transfer token and its size.
#[must_use]
pub fn build_upload_transfer_request(
filename: &str,
token: u32,
size: u64,
) -> Message {
Message::new()
.write_int32(40)
.write_int32(1) // direction: upload
.write_int32(token)
.write_string(filename)
.write_int64(size)
.clone()
}
#[must_use]
pub fn build_transfer_response_message(transfer: Transfer) -> Message {
Message::new()
@@ -199,6 +216,21 @@ fn test_build_login_message() {
assert_eq!(expect, message.get_data());
}
#[test]
fn test_build_upload_transfer_request() {
use crate::types::Transfer;
let message =
MessageFactory::build_upload_transfer_request("song.mp3", 555, 4096);
// Decode via the production Transfer parser (dispatcher starts at offset 8).
let mut decoded = Message::new_with_data(message.get_buffer());
decoded.set_pointer(8);
let transfer = Transfer::new_from_message(&mut decoded);
assert_eq!(transfer.direction, 1); // upload
assert_eq!(transfer.token, 555);
assert_eq!(transfer.filename, "song.mp3");
assert_eq!(transfer.size, 4096);
}
#[test]
fn test_build_peer_init_message() {
use crate::peer::ConnectionType;