Files
bt/src/tracker/http.rs

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);
}