You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
398 lines
12 KiB
Rust
398 lines
12 KiB
Rust
use crate::tracker_manager::{TrackerId, TrackerManager};
|
|
use crate::MAX_OPEN_REQUESTS;
|
|
use bytes::Bytes;
|
|
use ring::digest::{digest, SHA1_FOR_LEGACY_USE_ONLY};
|
|
use std::cmp::max;
|
|
use std::collections::{HashMap, HashSet, VecDeque};
|
|
use std::net::SocketAddr;
|
|
use std::ops::Add;
|
|
use std::option::Option::Some;
|
|
use std::path::PathBuf;
|
|
use std::time::{Duration, Instant};
|
|
use torment_core::infohash::v1::U160;
|
|
use torment_core::metainfo::{MetaInfo, Torrent};
|
|
use torment_core::utils::EphemeralSet;
|
|
use torment_core::{Bitfield, REQUEST_SIZE};
|
|
use torment_peer::message::{Handshake, Message, PieceMessage};
|
|
use torment_peer::{Peer, PeerProtocol};
|
|
use torment_storage::{StorageMap, ToStorageMap};
|
|
|
|
#[derive(Debug)]
|
|
pub enum Source {
|
|
Torrent(TorrentSource),
|
|
MetaInfo(MetaInfoSource),
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct MetaInfoSource {
|
|
info_hash: U160,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct TorrentSource {
|
|
file: Option<PathBuf>,
|
|
torrent: Option<Torrent>,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct TorrentTarget {
|
|
pub path: PathBuf,
|
|
pub is_base_path: bool,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct TorrentManager {
|
|
info_hash: U160,
|
|
bitfield: Bitfield,
|
|
trackers: Vec<Vec<TrackerId>>,
|
|
source: Source,
|
|
target: TorrentTarget,
|
|
peers: HashMap<U160, Peer>,
|
|
queue: VecDeque<(U160, Message)>,
|
|
storage_map: StorageMap,
|
|
requests_queue: VecDeque<u32>,
|
|
open_requests: HashMap<u32, EphemeralSet<u32>>,
|
|
uploaded: usize,
|
|
downloaded: usize,
|
|
}
|
|
|
|
impl TorrentManager {
|
|
pub fn peer_count(&self) -> usize {
|
|
self.peers.len()
|
|
}
|
|
|
|
pub fn trackers(&self) -> &Vec<Vec<TrackerId>> {
|
|
&self.trackers
|
|
}
|
|
|
|
pub fn info_hash(&self) -> U160 {
|
|
self.info_hash
|
|
}
|
|
|
|
pub fn is_done(&self) -> bool {
|
|
self.bitfield.all()
|
|
}
|
|
|
|
pub fn uploaded(&self) -> usize {
|
|
self.uploaded
|
|
}
|
|
|
|
pub fn downloaded(&self) -> usize {
|
|
self.downloaded
|
|
}
|
|
|
|
pub fn bytes_left(&self) -> i64 {
|
|
let mut size = self.storage_map.size() as i64;
|
|
let piece_length = self.storage_map.get_piece_length(0) as i64;
|
|
for i in 0..self.bitfield.size() as u32 {
|
|
if self.bitfield.get(i) {
|
|
size -= piece_length;
|
|
}
|
|
}
|
|
|
|
max(0, size)
|
|
}
|
|
|
|
pub fn dump_queue(&mut self) -> VecDeque<(U160, Message)> {
|
|
std::mem::replace(&mut self.queue, VecDeque::new())
|
|
}
|
|
|
|
pub fn from_torrent(
|
|
torrent: Torrent,
|
|
target: TorrentTarget,
|
|
tracker_manager: Option<&mut TrackerManager>,
|
|
) -> TorrentManager {
|
|
let trackers = tracker_manager
|
|
.map(|manager| {
|
|
torrent
|
|
.announce_list()
|
|
.iter()
|
|
.map(|tier_list| {
|
|
tier_list
|
|
.iter()
|
|
.filter_map(|tracker| manager.get_tracker_id(tracker))
|
|
.collect::<Vec<_>>()
|
|
})
|
|
.collect::<Vec<_>>()
|
|
})
|
|
.unwrap_or(vec![]);
|
|
|
|
TorrentManager {
|
|
info_hash: torrent.info_hash(),
|
|
bitfield: Bitfield::with_size(torrent.meta_info().pieces()),
|
|
trackers,
|
|
source: Source::Torrent(TorrentSource {
|
|
file: None,
|
|
torrent: Some(torrent.clone()),
|
|
}),
|
|
storage_map: torrent.to_storage_map(&target.path, target.is_base_path),
|
|
requests_queue: Default::default(),
|
|
target,
|
|
peers: HashMap::new(),
|
|
queue: Default::default(),
|
|
open_requests: Default::default(),
|
|
uploaded: 0,
|
|
downloaded: 0,
|
|
}
|
|
}
|
|
|
|
pub fn handshake(
|
|
&mut self,
|
|
handshake: Handshake,
|
|
addr: SocketAddr,
|
|
protocol: PeerProtocol,
|
|
) -> bool {
|
|
if handshake.info_hash() != self.info_hash {
|
|
return false;
|
|
}
|
|
|
|
if let Some(peer) = self.peers.get(&handshake.peer_id()) {
|
|
if peer.addr() != addr {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
let meta_info = self.meta_info();
|
|
let mut peer = Peer::new(addr, protocol, handshake, meta_info);
|
|
peer.send_bitfield(&self.bitfield);
|
|
self.peers.insert(peer.id(), peer);
|
|
|
|
true
|
|
}
|
|
|
|
fn meta_info(&mut self) -> &MetaInfo {
|
|
if let Source::Torrent(torrent) = &self.source {
|
|
return torrent
|
|
.torrent
|
|
.as_ref()
|
|
.expect("No torrent loaded uh oh missing functionality")
|
|
.meta_info();
|
|
}
|
|
|
|
panic!("Can't resolve MetaInfo for torrent")
|
|
}
|
|
|
|
pub fn process(&mut self, peer_id: U160, message: Message) -> bool {
|
|
let mut queue = vec![];
|
|
if let Message::Piece(piece) = &message {
|
|
self.downloaded += piece.piece.len()
|
|
}
|
|
|
|
let ok = if let Some(peer) = self.peers.get_mut(&peer_id) {
|
|
if peer.process(message) {
|
|
while let Some(piece) = peer.next_piece() {
|
|
if self.storage_map.has_piece(piece.index() as usize) {
|
|
continue;
|
|
}
|
|
|
|
if self
|
|
.storage_map
|
|
.write(
|
|
piece.index() as usize,
|
|
piece.offset() as usize,
|
|
piece.piece(),
|
|
)
|
|
.unwrap()
|
|
{
|
|
queue.push(piece.index() as usize);
|
|
}
|
|
}
|
|
|
|
while let Some(_) = peer.next_have() {
|
|
// Something i guess
|
|
}
|
|
|
|
while let Some(piece_request) = peer.next_request() {
|
|
if !self.bitfield.get(piece_request.index()) {
|
|
continue;
|
|
}
|
|
|
|
let mut buffer = vec![0u8; piece_request.length() as usize];
|
|
self.storage_map
|
|
.read(
|
|
piece_request.index() as usize,
|
|
piece_request.offset() as usize,
|
|
&mut buffer,
|
|
)
|
|
.unwrap();
|
|
|
|
self.queue.push_back((
|
|
peer_id,
|
|
Message::Piece(PieceMessage {
|
|
index: piece_request.index(),
|
|
offset: piece_request.offset(),
|
|
piece: Bytes::from(buffer),
|
|
}),
|
|
))
|
|
}
|
|
|
|
self.queue_requests(peer_id);
|
|
let msgs = self.peers.get_mut(&peer_id).unwrap().dump_queue();
|
|
self.queue_messages(peer_id, msgs);
|
|
true
|
|
} else {
|
|
false
|
|
}
|
|
} else {
|
|
false
|
|
};
|
|
|
|
// offload to io thread?
|
|
for have in queue {
|
|
let piece_hash = self.meta_info().hash(have);
|
|
let piece_data = self.storage_map.read_piece(have).unwrap();
|
|
let res = digest(&SHA1_FOR_LEGACY_USE_ONLY, &piece_data);
|
|
if piece_hash != res.as_ref() {
|
|
println!("=> Piece#{} failed verification", have);
|
|
self.storage_map.wipe_piece(have);
|
|
continue;
|
|
} else {
|
|
println!("=> Piece#{} verified", have);
|
|
}
|
|
|
|
self.bitfield.set(have as u32);
|
|
|
|
let keys = self.peers.keys().copied().collect::<Vec<_>>();
|
|
for key in keys {
|
|
self.queue.push_back((key, Message::Have(have as u32)));
|
|
|
|
if !self.peers[&key].has_piece(have as u32) && self.peers[&key].we_choked() {
|
|
self.peers.get_mut(&key).unwrap().set_we_choked(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
ok
|
|
}
|
|
|
|
fn queue_messages(&mut self, peer_id: U160, messages: VecDeque<Message>) {
|
|
for msg in messages {
|
|
if let Message::Piece(piece) = &msg {
|
|
self.uploaded += piece.piece.len();
|
|
}
|
|
|
|
self.queue.push_back((peer_id, msg));
|
|
}
|
|
}
|
|
|
|
pub fn next(&mut self) -> Option<(U160, Message)> {
|
|
self.queue.pop_front()
|
|
}
|
|
|
|
fn get_next_request_piece_for_peer(&mut self, peer: &U160) -> Option<(u32, u32, u32)> {
|
|
let mut queue_index = 0;
|
|
while let Some(index) = self.requests_queue.get(queue_index).copied() {
|
|
if !self.peers[peer].has_piece(index) {
|
|
queue_index += 1;
|
|
continue;
|
|
}
|
|
|
|
let bits = self.storage_map.get_bits_in_pieces(index as usize);
|
|
if self.open_requests.get(&index).map_or(0, |set| set.len()) >= bits {
|
|
self.requests_queue.remove(0);
|
|
}
|
|
|
|
let set = self
|
|
.open_requests
|
|
.entry(index)
|
|
.or_insert(EphemeralSet::new());
|
|
for i in 0..bits as u32 {
|
|
let bit_offset = i * REQUEST_SIZE as u32;
|
|
if set.contains(&bit_offset) {
|
|
continue;
|
|
}
|
|
|
|
set.insert(bit_offset, Instant::now().add(Duration::from_secs(10)));
|
|
return Some((
|
|
index,
|
|
bit_offset,
|
|
self.storage_map
|
|
.get_bit_length(index as usize, bit_offset as usize),
|
|
));
|
|
}
|
|
}
|
|
|
|
None
|
|
}
|
|
|
|
pub fn house_keeping(&mut self) {
|
|
let mut map: HashMap<u32, usize> = HashMap::new();
|
|
let mut interested_peers = HashSet::new();
|
|
if self.peers.len() <= 0 {
|
|
return;
|
|
}
|
|
|
|
let mut done = 0;
|
|
|
|
for i in 0..self.meta_info().pieces() as u32 {
|
|
if self.bitfield.get(i) {
|
|
done += 1;
|
|
// Don't need to queue anything
|
|
continue;
|
|
}
|
|
|
|
let mut entries = 0;
|
|
for (_, peer) in &mut self.peers {
|
|
if peer.has_piece(i) {
|
|
entries += 1;
|
|
interested_peers.insert(peer.id());
|
|
}
|
|
}
|
|
|
|
if entries == 0 {
|
|
// Can't queue entries without peers
|
|
continue;
|
|
}
|
|
|
|
map.insert(i, entries);
|
|
}
|
|
|
|
println!(
|
|
"{}%",
|
|
(done as f64 / self.meta_info().pieces() as f64) * 100f64
|
|
);
|
|
|
|
let mut pieces: Vec<_> = map.keys().copied().collect();
|
|
pieces.sort_by_key(|piece| map[piece]);
|
|
self.requests_queue = VecDeque::from(pieces);
|
|
|
|
for (_, peer) in &mut self.peers {
|
|
if !interested_peers.contains(&peer.id()) || self.requests_queue.len() == 0 {
|
|
peer.lost_interest();
|
|
continue;
|
|
}
|
|
|
|
peer.interested();
|
|
if peer.is_choked() {
|
|
// Don't send requests if they're choked
|
|
continue;
|
|
}
|
|
}
|
|
|
|
let peer_ids = self.peers.keys().copied().collect::<Vec<_>>();
|
|
for peer_id in peer_ids {
|
|
self.queue_requests(peer_id);
|
|
let message_queue = self.peers.get_mut(&peer_id).unwrap().dump_queue();
|
|
self.queue_messages(peer_id, message_queue);
|
|
}
|
|
}
|
|
|
|
fn queue_requests(&mut self, peer_id: U160) {
|
|
if self.peers[&peer_id].is_choked() {
|
|
return;
|
|
}
|
|
|
|
let amount = MAX_OPEN_REQUESTS - self.peers[&peer_id].count_open_requests();
|
|
for _ in 0..amount {
|
|
if let Some((index, offset, length)) = self.get_next_request_piece_for_peer(&peer_id) {
|
|
self.peers
|
|
.get_mut(&peer_id)
|
|
.unwrap()
|
|
.request(index, offset, length);
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|