A small refactor
This commit is contained in:
parent
2eabebb5c3
commit
6eef3b9b66
25 changed files with 111 additions and 189 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -692,6 +692,7 @@ dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"clone_to_owned",
|
"clone_to_owned",
|
||||||
"crypto-hash",
|
"crypto-hash",
|
||||||
|
"dht",
|
||||||
"futures",
|
"futures",
|
||||||
"hex 0.4.3",
|
"hex 0.4.3",
|
||||||
"librqbit_core",
|
"librqbit_core",
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ use crate::{
|
||||||
self, CompactNodeInfo, FindNodeRequest, GetPeersRequest, Message, MessageKind, Node,
|
self, CompactNodeInfo, FindNodeRequest, GetPeersRequest, Message, MessageKind, Node,
|
||||||
},
|
},
|
||||||
routing_table::{InsertResult, RoutingTable},
|
routing_table::{InsertResult, RoutingTable},
|
||||||
|
DHT_BOOTSTRAP,
|
||||||
};
|
};
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use bencode::ByteString;
|
use bencode::ByteString;
|
||||||
|
|
@ -495,12 +496,15 @@ impl DhtWorker {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Dht {
|
impl Dht {
|
||||||
pub async fn new(bootstrap_addrs: &[&str]) -> anyhow::Result<Self> {
|
pub async fn new() -> anyhow::Result<Self> {
|
||||||
|
Self::with_bootstrap_addrs(DHT_BOOTSTRAP).await
|
||||||
|
}
|
||||||
|
pub async fn with_bootstrap_addrs(bootstrap_addrs: &[&str]) -> anyhow::Result<Self> {
|
||||||
let (request_tx, request_rx) = channel(1);
|
let (request_tx, request_rx) = channel(1);
|
||||||
let socket = UdpSocket::bind("0.0.0.0:0")
|
let socket = UdpSocket::bind("0.0.0.0:0")
|
||||||
.await
|
.await
|
||||||
.context("error binding socket")?;
|
.context("error binding socket")?;
|
||||||
let peer_id = Id20(generate_peer_id());
|
let peer_id = generate_peer_id();
|
||||||
info!("starting up DHT with peer id {:?}", peer_id);
|
info!("starting up DHT with peer id {:?}", peer_id);
|
||||||
let bootstrap_addrs = bootstrap_addrs
|
let bootstrap_addrs = bootstrap_addrs
|
||||||
.iter()
|
.iter()
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
|
|
||||||
|
|
@ -4,3 +4,5 @@ mod routing_table;
|
||||||
|
|
||||||
pub use dht::Dht;
|
pub use dht::Dht;
|
||||||
pub use librqbit_core::id20::Id20;
|
pub use librqbit_core::id20::Id20;
|
||||||
|
|
||||||
|
pub static DHT_BOOTSTRAP: &[&str] = &["dht.transmissionbt.com:6881", "dht.libtorrent.org:25401"];
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,7 @@ async fn main() -> anyhow::Result<()> {
|
||||||
pretty_env_logger::init();
|
pretty_env_logger::init();
|
||||||
|
|
||||||
let info_hash = Id20::from_str("64a980abe6e448226bb930ba061592e44c3781a1").unwrap();
|
let info_hash = Id20::from_str("64a980abe6e448226bb930ba061592e44c3781a1").unwrap();
|
||||||
let dht = Dht::new(&["dht.transmissionbt.com:6881", "dht.libtorrent.org:25401"])
|
let dht = Dht::new().await.context("error initializing DHT")?;
|
||||||
.await
|
|
||||||
.context("error initializing dht")?;
|
|
||||||
let mut stream = dht.get_peers(info_hash).await;
|
let mut stream = dht.get_peers(info_hash).await;
|
||||||
let mut seen = HashSet::new();
|
let mut seen = HashSet::new();
|
||||||
while let Some(peer) = stream.next().await {
|
while let Some(peer) = stream.next().await {
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ librqbit_core = {path = "../librqbit_core"}
|
||||||
clone_to_owned = {path = "../clone_to_owned"}
|
clone_to_owned = {path = "../clone_to_owned"}
|
||||||
peer_binary_protocol = {path = "../peer_binary_protocol"}
|
peer_binary_protocol = {path = "../peer_binary_protocol"}
|
||||||
sha1w = {path = "../sha1w"}
|
sha1w = {path = "../sha1w"}
|
||||||
|
dht = {path = "../dht"}
|
||||||
|
|
||||||
tokio = {version = "1", features = ["macros", "rt-multi-thread"]}
|
tokio = {version = "1", features = ["macros", "rt-multi-thread"]}
|
||||||
tokio-stream = "0.1"
|
tokio-stream = "0.1"
|
||||||
|
|
|
||||||
|
|
@ -1,72 +0,0 @@
|
||||||
use std::{io::BufRead, io::BufReader, net::SocketAddr, process::Stdio, str::FromStr};
|
|
||||||
|
|
||||||
use log::info;
|
|
||||||
use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};
|
|
||||||
|
|
||||||
// Collects seen peers for torrent
|
|
||||||
// Knows if they work or not.
|
|
||||||
// Informs subscribers of new peers discovered.
|
|
||||||
//
|
|
||||||
// Can discover metainfo quickly (limiting concurrency).
|
|
||||||
|
|
||||||
pub struct JsDht {
|
|
||||||
info_hash: [u8; 20],
|
|
||||||
}
|
|
||||||
|
|
||||||
static NODEJS_DISCOVER_SCRIPT: &str = r#"
|
|
||||||
const DHT = require('bittorrent-dht')
|
|
||||||
|
|
||||||
let dht = new DHT();
|
|
||||||
let infoHash = process.env["INFOHASH"];
|
|
||||||
|
|
||||||
dht.on('peer', function (peer, infoHash, from) {
|
|
||||||
console.log(peer.host + ':' + peer.port)
|
|
||||||
})
|
|
||||||
|
|
||||||
dht.lookup(infoHash)
|
|
||||||
"#;
|
|
||||||
|
|
||||||
fn infohash_hex(info_hash: [u8; 20]) -> String {
|
|
||||||
hex::encode(info_hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl JsDht {
|
|
||||||
pub fn new(info_hash: [u8; 20]) -> Self {
|
|
||||||
Self { info_hash }
|
|
||||||
}
|
|
||||||
pub fn start_peer_discovery(self) -> anyhow::Result<UnboundedReceiver<SocketAddr>> {
|
|
||||||
let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
|
|
||||||
std::thread::spawn(move || self.discover_peers_and_send(tx).unwrap());
|
|
||||||
Ok(rx)
|
|
||||||
}
|
|
||||||
fn discover_peers_and_send(self, tx: UnboundedSender<SocketAddr>) -> anyhow::Result<()> {
|
|
||||||
let mut cmd = std::process::Command::new("node");
|
|
||||||
cmd.arg("-e")
|
|
||||||
.arg(NODEJS_DISCOVER_SCRIPT)
|
|
||||||
.env("NODE_PATH", "/opt/homebrew/lib/node_modules")
|
|
||||||
.env("INFOHASH", infohash_hex(self.info_hash))
|
|
||||||
.stdout(Stdio::piped());
|
|
||||||
|
|
||||||
info!("Executing {:?}", &cmd);
|
|
||||||
|
|
||||||
let mut child = cmd.spawn()?;
|
|
||||||
|
|
||||||
let stdout = child.stdout.take().unwrap();
|
|
||||||
let mut stdout = BufReader::new(stdout);
|
|
||||||
let mut line = String::new();
|
|
||||||
loop {
|
|
||||||
line.clear();
|
|
||||||
let size = stdout.read_line(&mut line)?;
|
|
||||||
if size == 0 {
|
|
||||||
anyhow::bail!("node discover process was not supposed to close")
|
|
||||||
}
|
|
||||||
// Remove newline character;
|
|
||||||
line.pop();
|
|
||||||
|
|
||||||
let ipaddr = SocketAddr::from_str(&line)?;
|
|
||||||
if tx.send(ipaddr).is_err() {
|
|
||||||
anyhow::bail!("receiver closed")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
pub mod inforead;
|
|
||||||
pub mod jsdht;
|
|
||||||
|
|
@ -6,6 +6,7 @@ use librqbit_core::torrent_metainfo::TorrentMetaV1Info;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
|
|
||||||
use crate::peer_info_reader;
|
use crate::peer_info_reader;
|
||||||
|
use librqbit_core::id20::Id20;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ReadMetainfoResult<Rx> {
|
pub enum ReadMetainfoResult<Rx> {
|
||||||
|
|
@ -20,8 +21,8 @@ pub enum ReadMetainfoResult<Rx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn read_metainfo_from_peer_receiver<A: StreamExt<Item = SocketAddr> + Unpin>(
|
pub async fn read_metainfo_from_peer_receiver<A: StreamExt<Item = SocketAddr> + Unpin>(
|
||||||
peer_id: [u8; 20],
|
peer_id: Id20,
|
||||||
info_hash: [u8; 20],
|
info_hash: Id20,
|
||||||
mut addrs: A,
|
mut addrs: A,
|
||||||
) -> ReadMetainfoResult<A> {
|
) -> ReadMetainfoResult<A> {
|
||||||
let mut seen = HashSet::<SocketAddr>::new();
|
let mut seen = HashSet::<SocketAddr>::new();
|
||||||
|
|
@ -63,13 +64,11 @@ pub async fn read_metainfo_from_peer_receiver<A: StreamExt<Item = SocketAddr> +
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use librqbit_core::{info_hash::decode_info_hash, peer_id::generate_peer_id};
|
use dht::{Dht, Id20};
|
||||||
use tokio_stream::wrappers::UnboundedReceiverStream;
|
use librqbit_core::peer_id::generate_peer_id;
|
||||||
|
|
||||||
use crate::dht::jsdht::JsDht;
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use std::sync::Once;
|
use std::{str::FromStr, sync::Once};
|
||||||
|
|
||||||
static LOG_INIT: Once = Once::new();
|
static LOG_INIT: Once = Once::new();
|
||||||
|
|
||||||
|
|
@ -81,16 +80,13 @@ mod tests {
|
||||||
async fn read_metainfo_from_dht() {
|
async fn read_metainfo_from_dht() {
|
||||||
init_logging();
|
init_logging();
|
||||||
|
|
||||||
let info_hash = decode_info_hash("9905f844e5d8787ecd5e08fb46b2eb0a42c131d7").unwrap();
|
let info_hash = Id20::from_str("9905f844e5d8787ecd5e08fb46b2eb0a42c131d7").unwrap();
|
||||||
let peer_rx = JsDht::new(info_hash).start_peer_discovery().unwrap();
|
let dht = Dht::new().await.unwrap();
|
||||||
|
let peer_rx = dht.get_peers(info_hash).await;
|
||||||
let peer_id = generate_peer_id();
|
let peer_id = generate_peer_id();
|
||||||
dbg!(
|
match read_metainfo_from_peer_receiver(peer_id, info_hash, peer_rx).await {
|
||||||
read_metainfo_from_peer_receiver(
|
ReadMetainfoResult::Found { info, rx, seen } => dbg!(info),
|
||||||
peer_id,
|
ReadMetainfoResult::ChannelClosed { seen } => todo!("should not have happened"),
|
||||||
info_hash,
|
};
|
||||||
UnboundedReceiverStream::new(peer_rx)
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -90,7 +90,7 @@ impl Inner {
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(id, mgr)| TorrentListResponseItem {
|
.map(|(id, mgr)| TorrentListResponseItem {
|
||||||
id,
|
id,
|
||||||
info_hash: hex::encode(mgr.torrent_state().info_hash()),
|
info_hash: hex::encode(mgr.torrent_state().info_hash().0),
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
}
|
}
|
||||||
|
|
@ -98,7 +98,7 @@ impl Inner {
|
||||||
|
|
||||||
fn api_torrent_details(&self, idx: usize) -> Option<TorrentDetailsResponse> {
|
fn api_torrent_details(&self, idx: usize) -> Option<TorrentDetailsResponse> {
|
||||||
let handle = self.mgr_handle(idx)?;
|
let handle = self.mgr_handle(idx)?;
|
||||||
let info_hash = hex::encode(handle.torrent_state().info_hash());
|
let info_hash = hex::encode(handle.torrent_state().info_hash().0);
|
||||||
let files = handle
|
let files = handle
|
||||||
.torrent_state()
|
.torrent_state()
|
||||||
.info()
|
.info()
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
pub mod chunk_tracker;
|
pub mod chunk_tracker;
|
||||||
pub mod dht;
|
pub mod dht_utils;
|
||||||
pub mod file_ops;
|
pub mod file_ops;
|
||||||
pub mod http_api;
|
pub mod http_api;
|
||||||
pub mod peer_connection;
|
pub mod peer_connection;
|
||||||
|
|
@ -14,7 +14,6 @@ pub mod type_aliases;
|
||||||
|
|
||||||
pub use buffers::*;
|
pub use buffers::*;
|
||||||
pub use clone_to_owned::CloneToOwned;
|
pub use clone_to_owned::CloneToOwned;
|
||||||
pub use librqbit_core::info_hash::*;
|
|
||||||
pub use librqbit_core::magnet::*;
|
pub use librqbit_core::magnet::*;
|
||||||
pub use librqbit_core::peer_id::*;
|
pub use librqbit_core::peer_id::*;
|
||||||
pub use librqbit_core::torrent_metainfo::*;
|
pub use librqbit_core::torrent_metainfo::*;
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use std::{net::SocketAddr, time::Duration};
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use buffers::{ByteBuf, ByteString};
|
use buffers::{ByteBuf, ByteString};
|
||||||
use clone_to_owned::CloneToOwned;
|
use clone_to_owned::CloneToOwned;
|
||||||
use librqbit_core::{lengths::ChunkInfo, peer_id::try_decode_peer_id};
|
use librqbit_core::{id20::Id20, lengths::ChunkInfo, peer_id::try_decode_peer_id};
|
||||||
use log::{debug, trace};
|
use log::{debug, trace};
|
||||||
use peer_binary_protocol::{
|
use peer_binary_protocol::{
|
||||||
extended::{handshake::ExtendedHandshake, ExtendedMessage},
|
extended::{handshake::ExtendedHandshake, ExtendedMessage},
|
||||||
|
|
@ -34,8 +34,8 @@ pub enum WriterRequest {
|
||||||
pub struct PeerConnection<H> {
|
pub struct PeerConnection<H> {
|
||||||
handler: H,
|
handler: H,
|
||||||
addr: SocketAddr,
|
addr: SocketAddr,
|
||||||
info_hash: [u8; 20],
|
info_hash: Id20,
|
||||||
peer_id: [u8; 20],
|
peer_id: Id20,
|
||||||
}
|
}
|
||||||
|
|
||||||
// async fn read_one<'a, R: AsyncReadExt + Unpin>(
|
// async fn read_one<'a, R: AsyncReadExt + Unpin>(
|
||||||
|
|
@ -94,7 +94,7 @@ macro_rules! read_one {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<H: PeerConnectionHandler> PeerConnection<H> {
|
impl<H: PeerConnectionHandler> PeerConnection<H> {
|
||||||
pub fn new(addr: SocketAddr, info_hash: [u8; 20], peer_id: [u8; 20], handler: H) -> Self {
|
pub fn new(addr: SocketAddr, info_hash: Id20, peer_id: Id20, handler: H) -> Self {
|
||||||
PeerConnection {
|
PeerConnection {
|
||||||
handler,
|
handler,
|
||||||
addr,
|
addr,
|
||||||
|
|
@ -112,7 +112,7 @@ impl<H: PeerConnectionHandler> PeerConnection<H> {
|
||||||
use tokio::io::AsyncReadExt;
|
use tokio::io::AsyncReadExt;
|
||||||
use tokio::io::AsyncWriteExt;
|
use tokio::io::AsyncWriteExt;
|
||||||
let mut conn = match timeout(
|
let mut conn = match timeout(
|
||||||
Duration::from_secs(10),
|
Duration::from_secs(2),
|
||||||
tokio::net::TcpStream::connect(self.addr),
|
tokio::net::TcpStream::connect(self.addr),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ use bencode::from_bytes;
|
||||||
use buffers::{ByteBuf, ByteString};
|
use buffers::{ByteBuf, ByteString};
|
||||||
use librqbit_core::{
|
use librqbit_core::{
|
||||||
constants::CHUNK_SIZE,
|
constants::CHUNK_SIZE,
|
||||||
|
id20::Id20,
|
||||||
lengths::{ceil_div_u64, last_element_size_u64, ChunkInfo},
|
lengths::{ceil_div_u64, last_element_size_u64, ChunkInfo},
|
||||||
torrent_metainfo::TorrentMetaV1Info,
|
torrent_metainfo::TorrentMetaV1Info,
|
||||||
};
|
};
|
||||||
|
|
@ -20,8 +21,8 @@ use crate::peer_connection::{PeerConnection, PeerConnectionHandler, WriterReques
|
||||||
|
|
||||||
pub async fn read_metainfo_from_peer(
|
pub async fn read_metainfo_from_peer(
|
||||||
addr: SocketAddr,
|
addr: SocketAddr,
|
||||||
peer_id: [u8; 20],
|
peer_id: Id20,
|
||||||
info_hash: [u8; 20],
|
info_hash: Id20,
|
||||||
) -> anyhow::Result<TorrentMetaV1Info<ByteString>> {
|
) -> anyhow::Result<TorrentMetaV1Info<ByteString>> {
|
||||||
let (result_tx, result_rx) =
|
let (result_tx, result_rx) =
|
||||||
tokio::sync::oneshot::channel::<anyhow::Result<TorrentMetaV1Info<ByteString>>>();
|
tokio::sync::oneshot::channel::<anyhow::Result<TorrentMetaV1Info<ByteString>>>();
|
||||||
|
|
@ -77,12 +78,7 @@ impl HandlerLocked {
|
||||||
CHUNK_SIZE as usize
|
CHUNK_SIZE as usize
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn record_piece(
|
fn record_piece(&mut self, index: u32, data: &[u8], info_hash: Id20) -> anyhow::Result<bool> {
|
||||||
&mut self,
|
|
||||||
index: u32,
|
|
||||||
data: &[u8],
|
|
||||||
info_hash: [u8; 20],
|
|
||||||
) -> anyhow::Result<bool> {
|
|
||||||
if index as usize >= self.total_pieces {
|
if index as usize >= self.total_pieces {
|
||||||
anyhow::bail!("wrong index");
|
anyhow::bail!("wrong index");
|
||||||
}
|
}
|
||||||
|
|
@ -107,7 +103,7 @@ impl HandlerLocked {
|
||||||
// check metadata
|
// check metadata
|
||||||
let mut hash = Sha1::new();
|
let mut hash = Sha1::new();
|
||||||
hash.update(&self.buffer);
|
hash.update(&self.buffer);
|
||||||
if hash.finish() != info_hash {
|
if hash.finish() != info_hash.0 {
|
||||||
anyhow::bail!("info checksum invalid");
|
anyhow::bail!("info checksum invalid");
|
||||||
}
|
}
|
||||||
Ok(true)
|
Ok(true)
|
||||||
|
|
@ -119,7 +115,7 @@ impl HandlerLocked {
|
||||||
|
|
||||||
struct Handler {
|
struct Handler {
|
||||||
addr: SocketAddr,
|
addr: SocketAddr,
|
||||||
info_hash: [u8; 20],
|
info_hash: Id20,
|
||||||
writer_tx: UnboundedSender<WriterRequest>,
|
writer_tx: UnboundedSender<WriterRequest>,
|
||||||
result_tx:
|
result_tx:
|
||||||
Mutex<Option<tokio::sync::oneshot::Sender<anyhow::Result<TorrentMetaV1Info<ByteString>>>>>,
|
Mutex<Option<tokio::sync::oneshot::Sender<anyhow::Result<TorrentMetaV1Info<ByteString>>>>>,
|
||||||
|
|
@ -216,6 +212,7 @@ impl PeerConnectionHandler for Handler {
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::{net::SocketAddr, str::FromStr, sync::Once};
|
use std::{net::SocketAddr, str::FromStr, sync::Once};
|
||||||
|
|
||||||
|
use librqbit_core::id20::Id20;
|
||||||
use librqbit_core::peer_id::generate_peer_id;
|
use librqbit_core::peer_id::generate_peer_id;
|
||||||
|
|
||||||
use super::read_metainfo_from_peer;
|
use super::read_metainfo_from_peer;
|
||||||
|
|
@ -226,19 +223,13 @@ mod tests {
|
||||||
LOG_INIT.call_once(pretty_env_logger::init)
|
LOG_INIT.call_once(pretty_env_logger::init)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode_info_hash(hash_str: &str) -> [u8; 20] {
|
|
||||||
let mut hash_arr = [0u8; 20];
|
|
||||||
hex::decode_to_slice(hash_str, &mut hash_arr).unwrap();
|
|
||||||
hash_arr
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_get_torrent_metadata_from_localhost_bittorrent_client() {
|
async fn test_get_torrent_metadata_from_localhost_bittorrent_client() {
|
||||||
init_logging();
|
init_logging();
|
||||||
|
|
||||||
let addr = SocketAddr::from_str("127.0.0.1:27311").unwrap();
|
let addr = SocketAddr::from_str("127.0.0.1:27311").unwrap();
|
||||||
let peer_id = generate_peer_id();
|
let peer_id = generate_peer_id();
|
||||||
let info_hash = decode_info_hash("9905f844e5d8787ecd5e08fb46b2eb0a42c131d7");
|
let info_hash = Id20::from_str("9905f844e5d8787ecd5e08fb46b2eb0a42c131d7").unwrap();
|
||||||
dbg!(read_metainfo_from_peer(addr, peer_id, info_hash)
|
dbg!(read_metainfo_from_peer(addr, peer_id, info_hash)
|
||||||
.await
|
.await
|
||||||
.unwrap());
|
.unwrap());
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use std::{collections::HashSet, sync::Arc};
|
use std::{collections::HashSet, sync::Arc};
|
||||||
|
|
||||||
|
use librqbit_core::id20::Id20;
|
||||||
use librqbit_core::lengths::{ChunkInfo, ValidPieceIndex};
|
use librqbit_core::lengths::{ChunkInfo, ValidPieceIndex};
|
||||||
use tokio::sync::{Notify, Semaphore};
|
use tokio::sync::{Notify, Semaphore};
|
||||||
|
|
||||||
|
|
@ -29,7 +30,7 @@ pub enum PeerState {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct LivePeerState {
|
pub struct LivePeerState {
|
||||||
pub peer_id: [u8; 20],
|
pub peer_id: Id20,
|
||||||
pub i_am_choked: bool,
|
pub i_am_choked: bool,
|
||||||
pub peer_interested: bool,
|
pub peer_interested: bool,
|
||||||
pub requests_sem: Arc<Semaphore>,
|
pub requests_sem: Arc<Semaphore>,
|
||||||
|
|
@ -39,7 +40,7 @@ pub struct LivePeerState {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LivePeerState {
|
impl LivePeerState {
|
||||||
pub fn new(peer_id: [u8; 20]) -> Self {
|
pub fn new(peer_id: Id20) -> Self {
|
||||||
LivePeerState {
|
LivePeerState {
|
||||||
peer_id,
|
peer_id,
|
||||||
i_am_choked: true,
|
i_am_choked: true,
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ use anyhow::Context;
|
||||||
use bencode::from_bytes;
|
use bencode::from_bytes;
|
||||||
use buffers::ByteString;
|
use buffers::ByteString;
|
||||||
use librqbit_core::{
|
use librqbit_core::{
|
||||||
lengths::Lengths, peer_id::generate_peer_id, speed_estimator::SpeedEstimator,
|
id20::Id20, lengths::Lengths, peer_id::generate_peer_id, speed_estimator::SpeedEstimator,
|
||||||
torrent_metainfo::TorrentMetaV1Info,
|
torrent_metainfo::TorrentMetaV1Info,
|
||||||
};
|
};
|
||||||
use log::{debug, info};
|
use log::{debug, info};
|
||||||
|
|
@ -29,11 +29,11 @@ use crate::{
|
||||||
};
|
};
|
||||||
pub struct TorrentManagerBuilder {
|
pub struct TorrentManagerBuilder {
|
||||||
info: TorrentMetaV1Info<ByteString>,
|
info: TorrentMetaV1Info<ByteString>,
|
||||||
info_hash: [u8; 20],
|
info_hash: Id20,
|
||||||
overwrite: bool,
|
overwrite: bool,
|
||||||
output_folder: PathBuf,
|
output_folder: PathBuf,
|
||||||
only_files: Option<Vec<usize>>,
|
only_files: Option<Vec<usize>>,
|
||||||
peer_id: Option<[u8; 20]>,
|
peer_id: Option<Id20>,
|
||||||
force_tracker_interval: Option<Duration>,
|
force_tracker_interval: Option<Duration>,
|
||||||
spawner: Option<BlockingSpawner>,
|
spawner: Option<BlockingSpawner>,
|
||||||
}
|
}
|
||||||
|
|
@ -41,7 +41,7 @@ pub struct TorrentManagerBuilder {
|
||||||
impl TorrentManagerBuilder {
|
impl TorrentManagerBuilder {
|
||||||
pub fn new<P: AsRef<Path>>(
|
pub fn new<P: AsRef<Path>>(
|
||||||
info: TorrentMetaV1Info<ByteString>,
|
info: TorrentMetaV1Info<ByteString>,
|
||||||
info_hash: [u8; 20],
|
info_hash: Id20,
|
||||||
output_folder: P,
|
output_folder: P,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|
@ -76,7 +76,7 @@ impl TorrentManagerBuilder {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn peer_id(&mut self, peer_id: [u8; 20]) -> &mut Self {
|
pub fn peer_id(&mut self, peer_id: Id20) -> &mut Self {
|
||||||
self.peer_id = Some(peer_id);
|
self.peer_id = Some(peer_id);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
@ -150,12 +150,12 @@ impl TorrentManager {
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn start<P: AsRef<Path>>(
|
fn start<P: AsRef<Path>>(
|
||||||
info: TorrentMetaV1Info<ByteString>,
|
info: TorrentMetaV1Info<ByteString>,
|
||||||
info_hash: [u8; 20],
|
info_hash: Id20,
|
||||||
out: P,
|
out: P,
|
||||||
overwrite: bool,
|
overwrite: bool,
|
||||||
only_files: Option<Vec<usize>>,
|
only_files: Option<Vec<usize>>,
|
||||||
force_tracker_interval: Option<Duration>,
|
force_tracker_interval: Option<Duration>,
|
||||||
peer_id: Option<[u8; 20]>,
|
peer_id: Option<Id20>,
|
||||||
spawner: BlockingSpawner,
|
spawner: BlockingSpawner,
|
||||||
) -> anyhow::Result<TorrentManagerHandle> {
|
) -> anyhow::Result<TorrentManagerHandle> {
|
||||||
let files = {
|
let files = {
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ use buffers::{ByteBuf, ByteString};
|
||||||
use clone_to_owned::CloneToOwned;
|
use clone_to_owned::CloneToOwned;
|
||||||
use futures::{stream::FuturesUnordered, StreamExt};
|
use futures::{stream::FuturesUnordered, StreamExt};
|
||||||
use librqbit_core::{
|
use librqbit_core::{
|
||||||
info_hash::InfoHash,
|
id20::Id20,
|
||||||
lengths::{ChunkInfo, Lengths, ValidPieceIndex},
|
lengths::{ChunkInfo, Lengths, ValidPieceIndex},
|
||||||
torrent_metainfo::TorrentMetaV1Info,
|
torrent_metainfo::TorrentMetaV1Info,
|
||||||
};
|
};
|
||||||
|
|
@ -219,8 +219,8 @@ pub struct TorrentState {
|
||||||
info: TorrentMetaV1Info<ByteString>,
|
info: TorrentMetaV1Info<ByteString>,
|
||||||
locked: Arc<RwLock<TorrentStateLocked>>,
|
locked: Arc<RwLock<TorrentStateLocked>>,
|
||||||
files: Vec<Arc<Mutex<File>>>,
|
files: Vec<Arc<Mutex<File>>>,
|
||||||
info_hash: [u8; 20],
|
info_hash: Id20,
|
||||||
peer_id: [u8; 20],
|
peer_id: Id20,
|
||||||
lengths: Lengths,
|
lengths: Lengths,
|
||||||
needed: u64,
|
needed: u64,
|
||||||
stats: AtomicStats,
|
stats: AtomicStats,
|
||||||
|
|
@ -234,8 +234,8 @@ impl TorrentState {
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
info: TorrentMetaV1Info<ByteString>,
|
info: TorrentMetaV1Info<ByteString>,
|
||||||
info_hash: [u8; 20],
|
info_hash: Id20,
|
||||||
peer_id: [u8; 20],
|
peer_id: Id20,
|
||||||
files: Vec<Arc<Mutex<File>>>,
|
files: Vec<Arc<Mutex<File>>>,
|
||||||
chunk_tracker: ChunkTracker,
|
chunk_tracker: ChunkTracker,
|
||||||
lengths: Lengths,
|
lengths: Lengths,
|
||||||
|
|
@ -304,10 +304,10 @@ impl TorrentState {
|
||||||
pub fn info(&self) -> &TorrentMetaV1Info<ByteString> {
|
pub fn info(&self) -> &TorrentMetaV1Info<ByteString> {
|
||||||
&self.info
|
&self.info
|
||||||
}
|
}
|
||||||
pub fn info_hash(&self) -> InfoHash {
|
pub fn info_hash(&self) -> Id20 {
|
||||||
self.info_hash
|
self.info_hash
|
||||||
}
|
}
|
||||||
pub fn peer_id(&self) -> [u8; 20] {
|
pub fn peer_id(&self) -> Id20 {
|
||||||
self.peer_id
|
self.peer_id
|
||||||
}
|
}
|
||||||
pub fn file_ops(&self) -> FileOps<'_, Sha1> {
|
pub fn file_ops(&self) -> FileOps<'_, Sha1> {
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,8 @@ use std::{
|
||||||
str::FromStr,
|
str::FromStr,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use librqbit_core::id20::Id20;
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub enum TrackerRequestEvent {
|
pub enum TrackerRequestEvent {
|
||||||
Started,
|
Started,
|
||||||
|
|
@ -16,8 +18,8 @@ pub enum TrackerRequestEvent {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TrackerRequest {
|
pub struct TrackerRequest {
|
||||||
pub info_hash: [u8; 20],
|
pub info_hash: Id20,
|
||||||
pub peer_id: [u8; 20],
|
pub peer_id: Id20,
|
||||||
pub event: Option<TrackerRequestEvent>,
|
pub event: Option<TrackerRequestEvent>,
|
||||||
pub port: u16,
|
pub port: u16,
|
||||||
pub uploaded: u64,
|
pub uploaded: u64,
|
||||||
|
|
@ -159,9 +161,9 @@ impl TrackerRequest {
|
||||||
use urlencoding as u;
|
use urlencoding as u;
|
||||||
let mut s = String::new();
|
let mut s = String::new();
|
||||||
s.push_str("info_hash=");
|
s.push_str("info_hash=");
|
||||||
s.push_str(u::encode_binary(&self.info_hash).as_ref());
|
s.push_str(u::encode_binary(&self.info_hash.0).as_ref());
|
||||||
s.push_str("&peer_id=");
|
s.push_str("&peer_id=");
|
||||||
s.push_str(u::encode_binary(&self.peer_id).as_ref());
|
s.push_str(u::encode_binary(&self.peer_id.0).as_ref());
|
||||||
if let Some(event) = self.event {
|
if let Some(event) = self.event {
|
||||||
write!(
|
write!(
|
||||||
s,
|
s,
|
||||||
|
|
@ -201,12 +203,12 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
#[test]
|
#[test]
|
||||||
fn test_serialize() {
|
fn test_serialize() {
|
||||||
let info_hash = [
|
let info_hash = Id20([
|
||||||
1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
|
1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
|
||||||
];
|
]);
|
||||||
let peer_id = [
|
let peer_id = Id20([
|
||||||
1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
|
1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
|
||||||
];
|
]);
|
||||||
let request = TrackerRequest {
|
let request = TrackerRequest {
|
||||||
info_hash,
|
info_hash,
|
||||||
peer_id,
|
peer_id,
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use std::{cmp::Ordering, str::FromStr};
|
||||||
|
|
||||||
use serde::{Deserialize, Deserializer, Serialize};
|
use serde::{Deserialize, Deserializer, Serialize};
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Clone, Copy, PartialEq, Eq, Hash, Default)]
|
||||||
pub struct Id20(pub [u8; 20]);
|
pub struct Id20(pub [u8; 20]);
|
||||||
|
|
||||||
impl FromStr for Id20 {
|
impl FromStr for Id20 {
|
||||||
|
|
@ -67,6 +67,9 @@ impl<'de> Deserialize<'de> for Id20 {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Id20 {
|
impl Id20 {
|
||||||
|
pub fn to_string(&self) -> String {
|
||||||
|
hex::encode(self.0)
|
||||||
|
}
|
||||||
pub fn distance(&self, other: &Id20) -> Id20 {
|
pub fn distance(&self, other: &Id20) -> Id20 {
|
||||||
let mut xor = [0u8; 20];
|
let mut xor = [0u8; 20];
|
||||||
for (idx, (s, o)) in self
|
for (idx, (s, o)) in self
|
||||||
|
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
pub type InfoHash = [u8; 20];
|
|
||||||
|
|
||||||
pub fn decode_info_hash(hash_str: &str) -> anyhow::Result<InfoHash> {
|
|
||||||
let mut hash_arr = [0u8; 20];
|
|
||||||
hex::decode_to_slice(hash_str, &mut hash_arr)?;
|
|
||||||
Ok(hash_arr)
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
pub mod constants;
|
pub mod constants;
|
||||||
pub mod id20;
|
pub mod id20;
|
||||||
pub mod info_hash;
|
|
||||||
pub mod lengths;
|
pub mod lengths;
|
||||||
pub mod magnet;
|
pub mod magnet;
|
||||||
pub mod peer_id;
|
pub mod peer_id;
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
use crate::info_hash::{decode_info_hash, InfoHash};
|
use std::str::FromStr;
|
||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
|
|
||||||
|
use crate::id20::Id20;
|
||||||
|
|
||||||
pub struct Magnet {
|
pub struct Magnet {
|
||||||
pub info_hash: InfoHash,
|
pub info_hash: Id20,
|
||||||
pub trackers: Vec<String>,
|
pub trackers: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -12,13 +15,13 @@ impl Magnet {
|
||||||
if url.scheme() != "magnet" {
|
if url.scheme() != "magnet" {
|
||||||
anyhow::bail!("expected scheme magnet");
|
anyhow::bail!("expected scheme magnet");
|
||||||
}
|
}
|
||||||
let mut info_hash: Option<InfoHash> = None;
|
let mut info_hash: Option<Id20> = None;
|
||||||
let mut trackers = Vec::<String>::new();
|
let mut trackers = Vec::<String>::new();
|
||||||
for (key, value) in url.query_pairs() {
|
for (key, value) in url.query_pairs() {
|
||||||
match key.as_ref() {
|
match key.as_ref() {
|
||||||
"xt" => match value.as_ref().strip_prefix("urn:btih:") {
|
"xt" => match value.as_ref().strip_prefix("urn:btih:") {
|
||||||
Some(infohash) => {
|
Some(infohash) => {
|
||||||
info_hash.replace(decode_info_hash(infohash)?);
|
info_hash.replace(Id20::from_str(infohash)?);
|
||||||
}
|
}
|
||||||
None => anyhow::bail!("expected xt to start with urn:btih:"),
|
None => anyhow::bail!("expected xt to start with urn:btih:"),
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
use crate::id20::Id20;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum AzureusStyleKind {
|
pub enum AzureusStyleKind {
|
||||||
Deluge,
|
Deluge,
|
||||||
|
|
@ -23,7 +25,8 @@ impl AzureusStyleKind {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_decode_azureus_style(p: &[u8; 20]) -> Option<AzureusStyle> {
|
fn try_decode_azureus_style(p: &Id20) -> Option<AzureusStyle> {
|
||||||
|
let p = p.0;
|
||||||
if !(p[0] == b'-' && p[7] == b'-') {
|
if !(p[0] == b'-' && p[7] == b'-') {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
@ -40,11 +43,11 @@ pub enum PeerId {
|
||||||
AzureusStyle(AzureusStyle),
|
AzureusStyle(AzureusStyle),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn try_decode_peer_id(p: [u8; 20]) -> Option<PeerId> {
|
pub fn try_decode_peer_id(p: Id20) -> Option<PeerId> {
|
||||||
Some(PeerId::AzureusStyle(try_decode_azureus_style(&p)?))
|
Some(PeerId::AzureusStyle(try_decode_azureus_style(&p)?))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_peer_id() -> [u8; 20] {
|
pub fn generate_peer_id() -> Id20 {
|
||||||
let mut peer_id = [0u8; 20];
|
let mut peer_id = [0u8; 20];
|
||||||
|
|
||||||
let u = uuid::Uuid::new_v4();
|
let u = uuid::Uuid::new_v4();
|
||||||
|
|
@ -52,5 +55,5 @@ pub fn generate_peer_id() -> [u8; 20] {
|
||||||
|
|
||||||
(&mut peer_id[..8]).copy_from_slice(b"-rQ0001-");
|
(&mut peer_id[..8]).copy_from_slice(b"-rQ0001-");
|
||||||
|
|
||||||
peer_id
|
Id20(peer_id)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ use buffers::{ByteBuf, ByteString};
|
||||||
use clone_to_owned::CloneToOwned;
|
use clone_to_owned::CloneToOwned;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
use crate::id20::Id20;
|
||||||
|
|
||||||
pub type TorrentMetaV1Borrowed<'a> = TorrentMetaV1<ByteBuf<'a>>;
|
pub type TorrentMetaV1Borrowed<'a> = TorrentMetaV1<ByteBuf<'a>>;
|
||||||
pub type TorrentMetaV1Owned = TorrentMetaV1<ByteString>;
|
pub type TorrentMetaV1Owned = TorrentMetaV1<ByteString>;
|
||||||
|
|
||||||
|
|
@ -14,7 +16,10 @@ pub fn torrent_from_bytes<'de, ByteBuf: Deserialize<'de>>(
|
||||||
let mut de = BencodeDeserializer::new_from_buf(buf);
|
let mut de = BencodeDeserializer::new_from_buf(buf);
|
||||||
de.is_torrent_info = true;
|
de.is_torrent_info = true;
|
||||||
let mut t = TorrentMetaV1::deserialize(&mut de)?;
|
let mut t = TorrentMetaV1::deserialize(&mut de)?;
|
||||||
t.info_hash = de.torrent_info_digest.unwrap();
|
t.info_hash = Id20(
|
||||||
|
de.torrent_info_digest
|
||||||
|
.ok_or_else(|| anyhow::anyhow!("programming error"))?,
|
||||||
|
);
|
||||||
Ok(t)
|
Ok(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -35,7 +40,7 @@ pub struct TorrentMetaV1<BufType> {
|
||||||
pub creation_date: Option<usize>,
|
pub creation_date: Option<usize>,
|
||||||
|
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub info_hash: [u8; 20],
|
pub info_hash: Id20,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<BufType> TorrentMetaV1<BufType> {
|
impl<BufType> TorrentMetaV1<BufType> {
|
||||||
|
|
@ -292,8 +297,8 @@ mod tests {
|
||||||
|
|
||||||
let torrent: TorrentMetaV1Borrowed = torrent_from_bytes(&buf).unwrap();
|
let torrent: TorrentMetaV1Borrowed = torrent_from_bytes(&buf).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
torrent.info_hash,
|
torrent.info_hash.to_string(),
|
||||||
*b"\x64\xa9\x80\xab\xe6\xe4\x48\x22\x6b\xb9\x30\xba\x06\x15\x92\xe4\x4c\x37\x81\xa1"
|
"64a980abe6e448226bb930ba061592e44c3781a1"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use bincode::Options;
|
||||||
use buffers::{ByteBuf, ByteString};
|
use buffers::{ByteBuf, ByteString};
|
||||||
use byteorder::{ByteOrder, BE};
|
use byteorder::{ByteOrder, BE};
|
||||||
use clone_to_owned::CloneToOwned;
|
use clone_to_owned::CloneToOwned;
|
||||||
use librqbit_core::{constants::CHUNK_SIZE, lengths::ChunkInfo};
|
use librqbit_core::{constants::CHUNK_SIZE, id20::Id20, lengths::ChunkInfo};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use self::extended::{handshake::ExtendedHandshake, ExtendedMessage};
|
use self::extended::{handshake::ExtendedHandshake, ExtendedMessage};
|
||||||
|
|
@ -474,8 +474,8 @@ where
|
||||||
pub struct Handshake<'a> {
|
pub struct Handshake<'a> {
|
||||||
pub pstr: &'a str,
|
pub pstr: &'a str,
|
||||||
pub reserved: [u8; 8],
|
pub reserved: [u8; 8],
|
||||||
pub info_hash: [u8; 20],
|
pub info_hash: Id20,
|
||||||
pub peer_id: [u8; 20],
|
pub peer_id: Id20,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bopts() -> impl bincode::Options {
|
fn bopts() -> impl bincode::Options {
|
||||||
|
|
@ -485,7 +485,7 @@ fn bopts() -> impl bincode::Options {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Handshake<'a> {
|
impl<'a> Handshake<'a> {
|
||||||
pub fn new(info_hash: [u8; 20], peer_id: [u8; 20]) -> Handshake<'static> {
|
pub fn new(info_hash: Id20, peer_id: Id20) -> Handshake<'static> {
|
||||||
debug_assert_eq!(PSTR_BT1.len(), 19);
|
debug_assert_eq!(PSTR_BT1.len(), 19);
|
||||||
|
|
||||||
let mut reserved: u64 = 0;
|
let mut reserved: u64 = 0;
|
||||||
|
|
@ -552,12 +552,12 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
#[test]
|
#[test]
|
||||||
fn test_handshake_serialize() {
|
fn test_handshake_serialize() {
|
||||||
let info_hash = [
|
let info_hash = Id20([
|
||||||
1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
|
1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
|
||||||
];
|
]);
|
||||||
let peer_id = [
|
let peer_id = Id20([
|
||||||
1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
|
1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
|
||||||
];
|
]);
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
Handshake::new(info_hash, peer_id).serialize(&mut buf);
|
Handshake::new(info_hash, peer_id).serialize(&mut buf);
|
||||||
assert_eq!(buf.len(), 20 + 20 + 8 + 19 + 1);
|
assert_eq!(buf.len(), 20 + 20 + 8 + 19 + 1);
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,12 @@ use clap::Clap;
|
||||||
use dht::{Dht, Id20};
|
use dht::{Dht, Id20};
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use librqbit::{
|
use librqbit::{
|
||||||
dht::inforead::read_metainfo_from_peer_receiver,
|
dht_utils::{read_metainfo_from_peer_receiver, ReadMetainfoResult},
|
||||||
generate_peer_id,
|
generate_peer_id,
|
||||||
spawn_utils::{spawn, BlockingSpawner},
|
spawn_utils::{spawn, BlockingSpawner},
|
||||||
torrent_from_bytes,
|
torrent_from_bytes,
|
||||||
torrent_manager::TorrentManagerBuilder,
|
torrent_manager::TorrentManagerBuilder,
|
||||||
ByteString, InfoHash, Magnet, TorrentMetaV1Info, TorrentMetaV1Owned,
|
ByteString, Magnet, TorrentMetaV1Info, TorrentMetaV1Owned,
|
||||||
};
|
};
|
||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
use reqwest::Url;
|
use reqwest::Url;
|
||||||
|
|
@ -169,22 +169,18 @@ fn main() -> anyhow::Result<()> {
|
||||||
|
|
||||||
async fn async_main(opts: Opts, spawner: BlockingSpawner) -> anyhow::Result<()> {
|
async fn async_main(opts: Opts, spawner: BlockingSpawner) -> anyhow::Result<()> {
|
||||||
let peer_id = generate_peer_id();
|
let peer_id = generate_peer_id();
|
||||||
let dht = Dht::new(&["dht.transmissionbt.com:6881", "dht.libtorrent.org:25401"])
|
let dht = Dht::new().await.context("error initializing DHT")?;
|
||||||
.await
|
|
||||||
.context("error initializing DHT")?;
|
|
||||||
|
|
||||||
if opts.torrent_path.starts_with("magnet:") {
|
if opts.torrent_path.starts_with("magnet:") {
|
||||||
let Magnet {
|
let Magnet {
|
||||||
info_hash,
|
info_hash,
|
||||||
trackers,
|
trackers,
|
||||||
} = Magnet::parse(&opts.torrent_path).context("provided path is not a valid magnet URL")?;
|
} = Magnet::parse(&opts.torrent_path).context("provided path is not a valid magnet URL")?;
|
||||||
let dht_rx = dht.get_peers(Id20(info_hash)).await;
|
let dht_rx = dht.get_peers(info_hash).await;
|
||||||
let (info, dht_rx, initial_peers) =
|
let (info, dht_rx, initial_peers) =
|
||||||
match read_metainfo_from_peer_receiver(peer_id, info_hash, dht_rx).await {
|
match read_metainfo_from_peer_receiver(peer_id, info_hash, dht_rx).await {
|
||||||
librqbit::dht::inforead::ReadMetainfoResult::Found { info, rx, seen } => {
|
ReadMetainfoResult::Found { info, rx, seen } => (info, rx, seen),
|
||||||
(info, rx, seen)
|
ReadMetainfoResult::ChannelClosed { .. } => {
|
||||||
}
|
|
||||||
librqbit::dht::inforead::ReadMetainfoResult::ChannelClosed { .. } => {
|
|
||||||
anyhow::bail!("DHT died, no way to discover torrent metainfo")
|
anyhow::bail!("DHT died, no way to discover torrent metainfo")
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -216,7 +212,7 @@ async fn async_main(opts: Opts, spawner: BlockingSpawner) -> anyhow::Result<()>
|
||||||
} else {
|
} else {
|
||||||
torrent_from_file(&opts.torrent_path)?
|
torrent_from_file(&opts.torrent_path)?
|
||||||
};
|
};
|
||||||
let dht_rx = dht.get_peers(Id20(torrent.info_hash)).await;
|
let dht_rx = dht.get_peers(torrent.info_hash).await;
|
||||||
let trackers = torrent
|
let trackers = torrent
|
||||||
.iter_announce()
|
.iter_announce()
|
||||||
.filter_map(|tracker| {
|
.filter_map(|tracker| {
|
||||||
|
|
@ -253,9 +249,9 @@ async fn async_main(opts: Opts, spawner: BlockingSpawner) -> anyhow::Result<()>
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
async fn main_info(
|
async fn main_info(
|
||||||
opts: Opts,
|
opts: Opts,
|
||||||
info_hash: InfoHash,
|
info_hash: Id20,
|
||||||
info: TorrentMetaV1Info<ByteString>,
|
info: TorrentMetaV1Info<ByteString>,
|
||||||
peer_id: [u8; 20],
|
peer_id: Id20,
|
||||||
mut dht_peer_rx: impl StreamExt<Item = SocketAddr> + Unpin + Send + Sync + 'static,
|
mut dht_peer_rx: impl StreamExt<Item = SocketAddr> + Unpin + Send + Sync + 'static,
|
||||||
initial_peers: Vec<SocketAddr>,
|
initial_peers: Vec<SocketAddr>,
|
||||||
trackers: Vec<reqwest::Url>,
|
trackers: Vec<reqwest::Url>,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue