97 lines
2.7 KiB
Rust
97 lines
2.7 KiB
Rust
use std::io::Read;
|
|
use std::net::Ipv4Addr;
|
|
|
|
use hyper::Client;
|
|
use url::form_urlencoded::byte_serialize;
|
|
|
|
use bencode::{decode, Dict};
|
|
use metainfo::{Hash, Metainfo};
|
|
use tracker::{Peer, TrackerError, TrackerResponse, TrackerResult};
|
|
|
|
macro_rules! ts {
|
|
( $e:expr ) => {
|
|
match $e {
|
|
Some(x) => x,
|
|
None => return Err(TrackerError::InvalidResponse),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn get_peers(peer_id: Hash, port: u16, metainfo: &Metainfo, uploaded: u64, downloaded: u64, left: u64) -> TrackerResult {
|
|
let url = format!("{}?info_hash={}&peer_id={}&port={}&uploaded={}\
|
|
&downloaded={}&left={}&compact=1",
|
|
metainfo.announce,
|
|
urlencode(&metainfo.info_hash),
|
|
urlencode(&peer_id),
|
|
port,
|
|
uploaded,
|
|
downloaded,
|
|
left);
|
|
|
|
let client = Client::new();
|
|
let mut resp = client.get(&url).send()?;
|
|
let mut buff = Vec::new();
|
|
resp.read_to_end(&mut buff)?;
|
|
|
|
let obj = decode(&buff)?;
|
|
let info = ts!(obj.as_dict());
|
|
parse_object(info)
|
|
}
|
|
|
|
fn parse_object(info: &Dict) -> TrackerResult {
|
|
if let Some(reason) = info.get_bytes("failure reason") {
|
|
Err(TrackerError::TrackerFailure(ts!(reason.string())))
|
|
} else {
|
|
let interval = ts!(info.get_int("interval"));
|
|
let peerstring = ts!(info.get_bytes("peers"));
|
|
|
|
if peerstring.len() % 6 != 0 {
|
|
return Err(TrackerError::InvalidResponse)
|
|
}
|
|
|
|
let peers = peerstring.chunks(6).map(|chunk| {
|
|
Peer {
|
|
addr: Ipv4Addr::new(chunk[0], chunk[1], chunk[2], chunk[3]),
|
|
port: (chunk[4] as u16) << 8 | (chunk[5] as u16) << 0,
|
|
}
|
|
}).collect();
|
|
|
|
Ok(TrackerResponse {
|
|
interval: interval as u64,
|
|
peers: peers,
|
|
})
|
|
}
|
|
}
|
|
|
|
fn urlencode(bytes: &[u8]) -> String {
|
|
byte_serialize(bytes).collect::<Vec<_>>().join("")
|
|
}
|
|
|
|
#[test]
|
|
fn test_failure() {
|
|
let mut info = Dict::new();
|
|
info.insert("failure reason", "unknown hash");
|
|
assert!(parse_object(&info).is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn test_invalid_peer_list() {
|
|
let mut info = Dict::new();
|
|
info.insert("interval", 900);
|
|
info.insert("peers", vec![1u8, 1, 1, 1]);
|
|
assert!(parse_object(&info).is_err());
|
|
}
|
|
|
|
|
|
#[test]
|
|
fn test_success() {
|
|
let mut info = Dict::new();
|
|
info.insert("interval", 900);
|
|
info.insert("peers", vec![1u8, 1, 1, 1, 1, 1]);
|
|
let resp = parse_object(&info).unwrap();
|
|
assert_eq!(resp.interval, 900);
|
|
assert_eq!(resp.peers.len(), 1);
|
|
assert_eq!(resp.peers[0].addr.octets(), [1, 1, 1, 1]);
|
|
assert_eq!(resp.peers[0].port, 257);
|
|
}
|