Merge pull request #77 from michel/lib/send-upload-speed
feat(lib): report each finished upload's rate to the server
This commit is contained in:
@@ -1,7 +1,9 @@
|
|||||||
use super::{
|
use super::{
|
||||||
ActiveUpload, Arc, Client, ClientContext, DownloadStatus, PeerMessage,
|
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 crate::types::UploadStatus;
|
||||||
use std::sync::atomic::{AtomicBool, AtomicU64};
|
use std::sync::atomic::{AtomicBool, AtomicU64};
|
||||||
|
|
||||||
@@ -156,6 +158,7 @@ impl Client {
|
|||||||
UploadStatus::Failed(e.to_string())
|
UploadStatus::Failed(e.to_string())
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
let mut report = None;
|
||||||
if let Ok(mut ctx) = context.write_safe()
|
if let Ok(mut ctx) = context.write_safe()
|
||||||
&& let Some(upload) = ctx.active_uploads.get_mut(&token)
|
&& let Some(upload) = ctx.active_uploads.get_mut(&token)
|
||||||
{
|
{
|
||||||
@@ -164,9 +167,18 @@ impl Client {
|
|||||||
if let Some(bytes) = streamed
|
if let Some(bytes) = streamed
|
||||||
&& secs > 0.0
|
&& 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
|
// The slot this transfer held is free now, so whoever is next in
|
||||||
// line gets it without waiting for another request to arrive.
|
// line gets it without waiting for another request to arrive.
|
||||||
Self::pump_upload_queue(&context);
|
Self::pump_upload_queue(&context);
|
||||||
|
|||||||
@@ -252,6 +252,7 @@ impl Message {
|
|||||||
100 => Ok("AcceptChildren"),
|
100 => Ok("AcceptChildren"),
|
||||||
102 => Ok("PossibleParents"),
|
102 => Ok("PossibleParents"),
|
||||||
104 => Ok("WishlistInterval"),
|
104 => Ok("WishlistInterval"),
|
||||||
|
121 => Ok("SendUploadSpeed"),
|
||||||
160 => Ok("ExcludedSearchPhrases"),
|
160 => Ok("ExcludedSearchPhrases"),
|
||||||
1001 => Ok("CantConnectToPeer"),
|
1001 => Ok("CantConnectToPeer"),
|
||||||
_ => Err(Error(format!("Unknown server message code: {code}"))),
|
_ => Err(Error(format!("Unknown server message code: {code}"))),
|
||||||
|
|||||||
@@ -143,6 +143,16 @@ impl MessageFactory {
|
|||||||
Message::new().write_int32(7).write_string(username).clone()
|
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.
|
/// Ask the server (code 36) for a user's share statistics.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn build_get_user_stats(username: &str) -> Message {
|
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();
|
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());
|
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"
|
"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(share_dir);
|
||||||
let _ = std::fs::remove_dir_all(download_dir);
|
let _ = std::fs::remove_dir_all(download_dir);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user