feat(lib): report each finished upload's rate to the server
The speed other users see for us in `user` lookups and in the server's own ranking comes from SendUploadSpeed (code 121), which the client never sent, so it read zero however much we served. Each completed upload now reports its measured bytes per second, as Nicotine+ and slskd do.
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
use super::{
|
||||
ActiveUpload, Arc, Client, ClientContext, DownloadStatus, PeerMessage,
|
||||
RwLock, RwLockExt, collect_failed_tokens, error, next_upload_token, thread,
|
||||
RwLock, RwLockExt, ServerMessage, collect_failed_tokens, error,
|
||||
next_upload_token, thread,
|
||||
};
|
||||
use crate::message::server::MessageFactory;
|
||||
use crate::types::UploadStatus;
|
||||
use std::sync::atomic::{AtomicBool, AtomicU64};
|
||||
|
||||
@@ -156,6 +158,7 @@ impl Client {
|
||||
UploadStatus::Failed(e.to_string())
|
||||
}
|
||||
};
|
||||
let mut report = None;
|
||||
if let Ok(mut ctx) = context.write_safe()
|
||||
&& let Some(upload) = ctx.active_uploads.get_mut(&token)
|
||||
{
|
||||
@@ -164,9 +167,18 @@ impl Client {
|
||||
if let Some(bytes) = streamed
|
||||
&& secs > 0.0
|
||||
{
|
||||
ctx.last_upload_speed = (bytes as f64 / secs) as u32;
|
||||
let speed = (bytes as f64 / secs) as u32;
|
||||
ctx.last_upload_speed = speed;
|
||||
report = ctx.server_sender.clone().map(|s| (s, speed));
|
||||
}
|
||||
}
|
||||
// The server folds each finished upload's rate into the average
|
||||
// speed it shows other users for us.
|
||||
if let Some((server, speed)) = report {
|
||||
let _ = server.send(ServerMessage::SendMessage(
|
||||
MessageFactory::build_send_upload_speed(speed),
|
||||
));
|
||||
}
|
||||
// The slot this transfer held is free now, so whoever is next in
|
||||
// line gets it without waiting for another request to arrive.
|
||||
Self::pump_upload_queue(&context);
|
||||
|
||||
@@ -252,6 +252,7 @@ impl Message {
|
||||
100 => Ok("AcceptChildren"),
|
||||
102 => Ok("PossibleParents"),
|
||||
104 => Ok("WishlistInterval"),
|
||||
121 => Ok("SendUploadSpeed"),
|
||||
160 => Ok("ExcludedSearchPhrases"),
|
||||
1001 => Ok("CantConnectToPeer"),
|
||||
_ => Err(Error(format!("Unknown server message code: {code}"))),
|
||||
|
||||
@@ -143,6 +143,16 @@ impl MessageFactory {
|
||||
Message::new().write_int32(7).write_string(username).clone()
|
||||
}
|
||||
|
||||
/// Tell the server (code 121) what a finished upload averaged, in bytes
|
||||
/// per second; it folds that into the speed other users see for us.
|
||||
#[must_use]
|
||||
pub fn build_send_upload_speed(bytes_per_second: u32) -> Message {
|
||||
Message::new()
|
||||
.write_int32(121)
|
||||
.write_int32(bytes_per_second)
|
||||
.clone()
|
||||
}
|
||||
|
||||
/// Ask the server (code 36) for a user's share statistics.
|
||||
#[must_use]
|
||||
pub fn build_get_user_stats(username: &str) -> Message {
|
||||
@@ -474,3 +484,12 @@ fn test_build_unwatch_user() {
|
||||
let expect: Vec<u8> = [6, 0, 0, 0, 3, 0, 0, 0, b'b', b'o', b'b'].to_vec();
|
||||
assert_eq!(expect, message.get_data());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_upload_speed_report_carries_the_rate() {
|
||||
let message = MessageFactory::build_send_upload_speed(900);
|
||||
let mut decoded = Message::new_with_data(message.get_buffer());
|
||||
assert_eq!(decoded.get_message_code(), 121);
|
||||
decoded.set_pointer(8);
|
||||
assert_eq!(decoded.read_int32(), 900);
|
||||
}
|
||||
|
||||
@@ -1769,6 +1769,22 @@ fn two_real_clients_search_and_download() {
|
||||
"a finished upload sets the advertised speed"
|
||||
);
|
||||
|
||||
// The server hears about it too (SendUploadSpeed), which is where every
|
||||
// other client's `user` lookup and the server's own ranking read it.
|
||||
let deadline = Instant::now() + Duration::from_secs(10);
|
||||
let mut recorded = 0;
|
||||
while Instant::now() < deadline && recorded == 0 {
|
||||
leecher
|
||||
.request_user_info("e2e_sharer")
|
||||
.expect("ask about the sharer");
|
||||
std::thread::sleep(Duration::from_millis(250));
|
||||
recorded = leecher
|
||||
.user_info("e2e_sharer")
|
||||
.and_then(|info| info.stats)
|
||||
.map_or(0, |stats| stats.average_speed);
|
||||
}
|
||||
assert!(recorded > 0, "the server should record the upload's speed");
|
||||
|
||||
let _ = std::fs::remove_dir_all(share_dir);
|
||||
let _ = std::fs::remove_dir_all(download_dir);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user