add detailed peer listing in webserver
This commit is contained in:
parent
e86e2c367f
commit
ad369d1d22
|
@ -210,11 +210,13 @@ impl UDPTracker {
|
||||||
let client_addr = SocketAddr::new(remote_addr.ip(), packet.port);
|
let client_addr = SocketAddr::new(remote_addr.ip(), packet.port);
|
||||||
let info_hash = packet.info_hash.into();
|
let info_hash = packet.info_hash.into();
|
||||||
|
|
||||||
|
let peer_id: &tracker::PeerId = tracker::PeerId::from_array(&packet.peer_id);
|
||||||
|
|
||||||
match self
|
match self
|
||||||
.tracker
|
.tracker
|
||||||
.update_torrent_and_get_stats(
|
.update_torrent_and_get_stats(
|
||||||
&info_hash,
|
&info_hash,
|
||||||
&packet.peer_id,
|
peer_id,
|
||||||
&client_addr,
|
&client_addr,
|
||||||
packet.uploaded,
|
packet.uploaded,
|
||||||
packet.downloaded,
|
packet.downloaded,
|
||||||
|
|
120
src/tracker.rs
120
src/tracker.rs
|
@ -25,16 +25,21 @@ pub enum TrackerMode {
|
||||||
PrivateMode,
|
PrivateMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Serialize)]
|
||||||
struct TorrentPeer {
|
pub struct TorrentPeer {
|
||||||
ip: std::net::SocketAddr,
|
ip: std::net::SocketAddr,
|
||||||
uploaded: u64,
|
uploaded: u64,
|
||||||
downloaded: u64,
|
downloaded: u64,
|
||||||
left: u64,
|
left: u64,
|
||||||
event: Events,
|
event: Events,
|
||||||
|
#[serde(serialize_with = "ser_instant")]
|
||||||
updated: std::time::Instant,
|
updated: std::time::Instant,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn ser_instant<S: serde::Serializer>(inst: &std::time::Instant, ser: S) -> Result<S::Ok, S::Error> {
|
||||||
|
ser.serialize_u64(inst.elapsed().as_millis() as u64)
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Ord, PartialEq, Eq, Clone)]
|
#[derive(Ord, PartialEq, Eq, Clone)]
|
||||||
pub struct InfoHash {
|
pub struct InfoHash {
|
||||||
info_hash: [u8; 20],
|
info_hash: [u8; 20],
|
||||||
|
@ -128,7 +133,112 @@ impl<'de> serde::de::Deserialize<'de> for InfoHash {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type PeerId = [u8; 20];
|
#[repr(transparent)]
|
||||||
|
#[derive(Copy, Clone, PartialOrd, Ord, Eq, PartialEq)]
|
||||||
|
pub struct PeerId([u8; 20]);
|
||||||
|
impl PeerId {
|
||||||
|
pub fn from_array(v: &[u8; 20]) -> &PeerId {
|
||||||
|
unsafe {
|
||||||
|
// This is safe since PeerId's repr is transparent and content's are identical. PeerId == [0u8; 20]
|
||||||
|
core::mem::transmute(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_client_name(&self) -> Option<&'static str> {
|
||||||
|
if self.0[0] == b'M' {
|
||||||
|
return Some("BitTorrent");
|
||||||
|
}
|
||||||
|
if self.0[0] == b'-' {
|
||||||
|
let name = match &self.0[1..3] {
|
||||||
|
b"AG" => "Ares",
|
||||||
|
b"A~" => "Ares",
|
||||||
|
b"AR" => "Arctic",
|
||||||
|
b"AV" => "Avicora",
|
||||||
|
b"AX" => "BitPump",
|
||||||
|
b"AZ" => "Azureus",
|
||||||
|
b"BB" => "BitBuddy",
|
||||||
|
b"BC" => "BitComet",
|
||||||
|
b"BF" => "Bitflu",
|
||||||
|
b"BG" => "BTG (uses Rasterbar libtorrent)",
|
||||||
|
b"BR" => "BitRocket",
|
||||||
|
b"BS" => "BTSlave",
|
||||||
|
b"BX" => "~Bittorrent X",
|
||||||
|
b"CD" => "Enhanced CTorrent",
|
||||||
|
b"CT" => "CTorrent",
|
||||||
|
b"DE" => "DelugeTorrent",
|
||||||
|
b"DP" => "Propagate Data Client",
|
||||||
|
b"EB" => "EBit",
|
||||||
|
b"ES" => "electric sheep",
|
||||||
|
b"FT" => "FoxTorrent",
|
||||||
|
b"FW" => "FrostWire",
|
||||||
|
b"FX" => "Freebox BitTorrent",
|
||||||
|
b"GS" => "GSTorrent",
|
||||||
|
b"HL" => "Halite",
|
||||||
|
b"HN" => "Hydranode",
|
||||||
|
b"KG" => "KGet",
|
||||||
|
b"KT" => "KTorrent",
|
||||||
|
b"LH" => "LH-ABC",
|
||||||
|
b"LP" => "Lphant",
|
||||||
|
b"LT" => "libtorrent",
|
||||||
|
b"lt" => "libTorrent",
|
||||||
|
b"LW" => "LimeWire",
|
||||||
|
b"MO" => "MonoTorrent",
|
||||||
|
b"MP" => "MooPolice",
|
||||||
|
b"MR" => "Miro",
|
||||||
|
b"MT" => "MoonlightTorrent",
|
||||||
|
b"NX" => "Net Transport",
|
||||||
|
b"PD" => "Pando",
|
||||||
|
b"qB" => "qBittorrent",
|
||||||
|
b"QD" => "QQDownload",
|
||||||
|
b"QT" => "Qt 4 Torrent example",
|
||||||
|
b"RT" => "Retriever",
|
||||||
|
b"S~" => "Shareaza alpha/beta",
|
||||||
|
b"SB" => "~Swiftbit",
|
||||||
|
b"SS" => "SwarmScope",
|
||||||
|
b"ST" => "SymTorrent",
|
||||||
|
b"st" => "sharktorrent",
|
||||||
|
b"SZ" => "Shareaza",
|
||||||
|
b"TN" => "TorrentDotNET",
|
||||||
|
b"TR" => "Transmission",
|
||||||
|
b"TS" => "Torrentstorm",
|
||||||
|
b"TT" => "TuoTu",
|
||||||
|
b"UL" => "uLeecher!",
|
||||||
|
b"UT" => "µTorrent",
|
||||||
|
b"UW" => "µTorrent Web",
|
||||||
|
b"VG" => "Vagaa",
|
||||||
|
b"WD" => "WebTorrent Desktop",
|
||||||
|
b"WT" => "BitLet",
|
||||||
|
b"WW" => "WebTorrent",
|
||||||
|
b"WY" => "FireTorrent",
|
||||||
|
b"XL" => "Xunlei",
|
||||||
|
b"XT" => "XanTorrent",
|
||||||
|
b"XX" => "Xtorrent",
|
||||||
|
b"ZT" => "ZipTorrent",
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
Some(name)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Serialize for PeerId {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where S: serde::Serializer {
|
||||||
|
let mut tmp = [0u8; 40];
|
||||||
|
binascii::bin2hex(&self.0, &mut tmp).unwrap();
|
||||||
|
let id = std::str::from_utf8(&tmp).ok();
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct PeerIdInfo<'a> {
|
||||||
|
id: Option<&'a str>,
|
||||||
|
client: Option<&'a str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
let obj = PeerIdInfo { id, client: self.get_client_name() };
|
||||||
|
obj.serialize(serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
pub struct TorrentEntry {
|
pub struct TorrentEntry {
|
||||||
|
@ -208,6 +318,10 @@ impl TorrentEntry {
|
||||||
list
|
list
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_peers_iter(&self) -> impl Iterator<Item=(&PeerId, &TorrentPeer)> {
|
||||||
|
self.peers.iter()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_stats(&self) -> (u32, u32, u32) {
|
pub fn get_stats(&self) -> (u32, u32, u32) {
|
||||||
let leechers = (self.peers.len() as u32) - self.seeders;
|
let leechers = (self.peers.len() as u32) - self.seeders;
|
||||||
(self.seeders, self.completed, leechers)
|
(self.seeders, self.completed, leechers)
|
||||||
|
|
|
@ -31,6 +31,11 @@ struct TorrentEntry<'a> {
|
||||||
info_hash: &'a InfoHash,
|
info_hash: &'a InfoHash,
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
data: &'a crate::tracker::TorrentEntry,
|
data: &'a crate::tracker::TorrentEntry,
|
||||||
|
seeders: u32,
|
||||||
|
leechers: u32,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if="Option::is_none")]
|
||||||
|
peers: Option<Vec<(crate::tracker::PeerId, crate::tracker::TorrentPeer)>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
|
@ -101,7 +106,10 @@ pub fn build_server(
|
||||||
let db = tracker.get_database().await;
|
let db = tracker.get_database().await;
|
||||||
let results: Vec<_> = db
|
let results: Vec<_> = db
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(k, v)| TorrentEntry { info_hash: k, data: v })
|
.map(|(k, v)| {
|
||||||
|
let (seeders, _, leechers) = v.get_stats();
|
||||||
|
TorrentEntry { info_hash: k, data: v, seeders, leechers, peers: None }
|
||||||
|
})
|
||||||
.skip(offset as usize)
|
.skip(offset as usize)
|
||||||
.take(limit as usize)
|
.take(limit as usize)
|
||||||
.collect();
|
.collect();
|
||||||
|
@ -113,7 +121,7 @@ pub fn build_server(
|
||||||
let t2 = tracker.clone();
|
let t2 = tracker.clone();
|
||||||
// view_torrent_info -> GET /t/:infohash HTTP/*
|
// view_torrent_info -> GET /t/:infohash HTTP/*
|
||||||
let view_torrent_info = filters::method::get()
|
let view_torrent_info = filters::method::get()
|
||||||
.and(filters::path::param())
|
.and(filters::path::param()).and(filters::path::end())
|
||||||
.map(move |info_hash: InfoHash| {
|
.map(move |info_hash: InfoHash| {
|
||||||
let tracker = t2.clone();
|
let tracker = t2.clone();
|
||||||
(info_hash, tracker)
|
(info_hash, tracker)
|
||||||
|
@ -125,10 +133,22 @@ pub fn build_server(
|
||||||
Some(v) => v,
|
Some(v) => v,
|
||||||
None => return Err(warp::reject::reject()),
|
None => return Err(warp::reject::reject()),
|
||||||
};
|
};
|
||||||
|
let (seeders, _, leechers) = info.get_stats();
|
||||||
|
|
||||||
|
let peers: Vec<_> = info
|
||||||
|
.get_peers_iter()
|
||||||
|
.take(1000)
|
||||||
|
.map(|(peer_id, peer_info)| {
|
||||||
|
(peer_id.clone(), peer_info.clone())
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
Ok(reply::json(&TorrentEntry {
|
Ok(reply::json(&TorrentEntry {
|
||||||
info_hash: &info_hash,
|
info_hash: &info_hash,
|
||||||
data: info,
|
data: info,
|
||||||
|
seeders,
|
||||||
|
leechers,
|
||||||
|
peers: Some(peers),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -136,7 +156,7 @@ pub fn build_server(
|
||||||
// DELETE /t/:info_hash
|
// DELETE /t/:info_hash
|
||||||
let t3 = tracker.clone();
|
let t3 = tracker.clone();
|
||||||
let delete_torrent = filters::method::delete()
|
let delete_torrent = filters::method::delete()
|
||||||
.and(filters::path::param())
|
.and(filters::path::param()).and(filters::path::end())
|
||||||
.map(move |info_hash: InfoHash| {
|
.map(move |info_hash: InfoHash| {
|
||||||
let tracker = t3.clone();
|
let tracker = t3.clone();
|
||||||
(info_hash, tracker)
|
(info_hash, tracker)
|
||||||
|
@ -160,7 +180,7 @@ pub fn build_server(
|
||||||
// add_torrent/alter: POST /t/:info_hash
|
// add_torrent/alter: POST /t/:info_hash
|
||||||
// (optional) BODY: json: {"is_flagged": boolean}
|
// (optional) BODY: json: {"is_flagged": boolean}
|
||||||
let change_torrent = filters::method::post()
|
let change_torrent = filters::method::post()
|
||||||
.and(filters::path::param())
|
.and(filters::path::param()).and(filters::path::end())
|
||||||
.and(filters::body::content_length_limit(4096))
|
.and(filters::body::content_length_limit(4096))
|
||||||
.and(filters::body::json())
|
.and(filters::body::json())
|
||||||
.map(move |info_hash: InfoHash, body: Option<TorrentFlag>| {
|
.map(move |info_hash: InfoHash, body: Option<TorrentFlag>| {
|
||||||
|
|
Loading…
Reference in a new issue