use std::collections::{BTreeMap, BTreeSet, HashMap};
use time::{SteadyTime};
use crust::Endpoint;
use types::{Id, PublicId};
use NameType;
const MAX_RELAY : usize = 100;
pub struct RelayMap {
relay_map: BTreeMap<NameType, (PublicId, BTreeSet<Endpoint>)>,
lookup_map: HashMap<Endpoint, NameType>,
unknown_connections: HashMap<Endpoint, SteadyTime>,
our_name: NameType,
self_relocated: bool
}
impl RelayMap {
pub fn new(our_id: &Id) -> RelayMap {
RelayMap {
relay_map: BTreeMap::new(),
lookup_map: HashMap::new(),
unknown_connections: HashMap::new(),
our_name: our_id.get_name(),
self_relocated: our_id.is_self_relocated()
}
}
pub fn add_ip_node(&mut self, relay_info: PublicId, relay_endpoint: Endpoint) -> bool {
if self.our_name == relay_info.name() {
return false;
}
if !self.relay_map.contains_key(&relay_info.name())
&& self.relay_map.len() >= MAX_RELAY {
return false;
}
if self.lookup_map.contains_key(&relay_endpoint) {
return false; }
self.lookup_map.entry(relay_endpoint.clone())
.or_insert(relay_info.name());
let new_set = || { (relay_info.clone(), BTreeSet::<Endpoint>::new()) };
self.relay_map.entry(relay_info.name()).or_insert_with(new_set).1
.insert(relay_endpoint);
true
}
pub fn drop_ip_node(&mut self, ip_node_to_drop: &NameType) {
match self.relay_map.get(&ip_node_to_drop) {
Some(relay_entry) => {
for endpoint in relay_entry.1.iter() {
self.lookup_map.remove(endpoint);
}
},
None => return
};
self.relay_map.remove(ip_node_to_drop);
}
pub fn drop_endpoint(&mut self, endpoint_to_drop: &Endpoint) -> Option<NameType> {
let mut old_entry = match self.lookup_map.remove(endpoint_to_drop) {
Some(name) => {
match self.relay_map.remove(&name) {
Some(entry) => Some((name, entry)),
None => None
}
},
None => None
};
let new_entry = match old_entry {
Some((ref name, (ref public_id, ref mut endpoints))) => {
endpoints.remove(endpoint_to_drop);
Some((name, (public_id, endpoints)))
},
None => None
};
match new_entry {
Some((name, (public_id, endpoints))) => {
if endpoints.is_empty() {
println!("Connection {:?} lost for relayed node {:?}", endpoint_to_drop, name);
Some(name.clone())
} else {
self.relay_map.insert(name.clone(), (public_id.clone(), endpoints.clone()));
None
}
},
None => None
}
}
pub fn contains_relay_for(&self, relay_name: &NameType) -> bool {
self.relay_map.contains_key(relay_name)
}
pub fn contains_endpoint(&self, relay_endpoint: &Endpoint) -> bool {
self.lookup_map.contains_key(relay_endpoint)
}
pub fn lookup_endpoint(&self, relay_endpoint: &Endpoint) -> Option<NameType> {
match self.lookup_map.get(relay_endpoint) {
Some(name) => Some(name.clone()),
None => None
}
}
pub fn get_endpoints(&self, relay_name: &NameType) -> Option<&(PublicId, BTreeSet<Endpoint>)> {
self.relay_map.get(relay_name)
}
pub fn change_our_name(&mut self, new_name: &NameType) {
if self.relay_map.contains_key(new_name) {
self.drop_ip_node(new_name);
}
self.our_name = new_name.clone();
}
pub fn register_unknown_connection(&mut self, endpoint: Endpoint) {
self.unknown_connections.insert(endpoint, SteadyTime::now());
}
pub fn remove_unknown_connection(&mut self, endpoint: &Endpoint) -> Option<Endpoint> {
match self.unknown_connections.remove(endpoint) {
Some(addded_timestamp) => Some(endpoint.clone()), None => None
}
}
pub fn lookup_unknown_connection(&self, endpoint: &Endpoint) -> bool {
self.unknown_connections.contains_key(endpoint)
}
pub fn zero_node(&self) -> bool {
self.self_relocated
}
}
pub struct BootstrapEndpoints {
bootstrap_endpoints: Vec<Endpoint>,
}
#[cfg(test)]
mod test {
use super::*;
use crust::Endpoint;
use types::{Id, PublicId};
use std::net::SocketAddr;
use std::str::FromStr;
use rand::random;
fn generate_random_endpoint() -> Endpoint {
Endpoint::Tcp(SocketAddr::from_str(&format!("127.0.0.1:{}", random::<u16>())).unwrap())
}
#[test]
fn add() {
let our_id : Id = Id::new();
let our_public_id = PublicId::new(&our_id);
let our_name = our_id.get_name();
let mut relay_map = RelayMap::new(&our_id);
assert_eq!(false, relay_map.add_ip_node(our_public_id.clone(), generate_random_endpoint()));
assert_eq!(0, relay_map.relay_map.len());
assert_eq!(0, relay_map.lookup_map.len());
while relay_map.relay_map.len() < super::MAX_RELAY {
let new_endpoint = generate_random_endpoint();
if !relay_map.contains_endpoint(&new_endpoint) {
assert_eq!(true, relay_map.add_ip_node(PublicId::new(&Id::new()),
new_endpoint)); };
}
assert_eq!(false, relay_map.add_ip_node(PublicId::new(&Id::new()),
generate_random_endpoint()));
}
#[test]
fn drop() {
let our_id : Id = Id::new();
let our_name = our_id.get_name();
let mut relay_map = RelayMap::new(&our_id);
let test_public_id = PublicId::new(&Id::new());
let test_endpoint = generate_random_endpoint();
assert_eq!(true, relay_map.add_ip_node(test_public_id.clone(),
test_endpoint.clone()));
assert_eq!(true, relay_map.contains_relay_for(&test_public_id.name()));
assert_eq!(true, relay_map.contains_endpoint(&test_endpoint));
relay_map.drop_ip_node(&test_public_id.name());
assert_eq!(false, relay_map.contains_relay_for(&test_public_id.name()));
assert_eq!(false, relay_map.contains_endpoint(&test_endpoint));
assert_eq!(None, relay_map.get_endpoints(&test_public_id.name()));
}
#[test]
fn add_conflicting_endpoints() {
let our_id : Id = Id::new();
let our_name = our_id.get_name();
let mut relay_map = RelayMap::new(&our_id);
let test_public_id = PublicId::new(&Id::new());
let test_endpoint = generate_random_endpoint();
let test_conflicting_public_id = PublicId::new(&Id::new());
assert_eq!(true, relay_map.add_ip_node(test_public_id.clone(),
test_endpoint.clone()));
assert_eq!(true, relay_map.contains_relay_for(&test_public_id.name()));
assert_eq!(true, relay_map.contains_endpoint(&test_endpoint));
assert_eq!(false, relay_map.add_ip_node(test_conflicting_public_id.clone(),
test_endpoint.clone()));
assert_eq!(false, relay_map.contains_relay_for(&test_conflicting_public_id.name()))
}
#[test]
fn add_multiple_endpoints() {
let our_id : Id = Id::new();
let our_name = our_id.get_name();
let mut relay_map = RelayMap::new(&our_id);
assert!(super::MAX_RELAY - 1 > 0);
while relay_map.relay_map.len() < super::MAX_RELAY - 1 {
let new_endpoint = generate_random_endpoint();
if !relay_map.contains_endpoint(&new_endpoint) {
assert_eq!(true, relay_map.add_ip_node(PublicId::new(&Id::new()),
new_endpoint)); };
}
let test_public_id = PublicId::new(&Id::new());
let mut test_endpoint_1 = generate_random_endpoint();
let mut test_endpoint_2 = generate_random_endpoint();
loop {
if !relay_map.contains_endpoint(&test_endpoint_1) { break; }
test_endpoint_1 = generate_random_endpoint(); };
loop {
if !relay_map.contains_endpoint(&test_endpoint_2) { break; }
test_endpoint_2 = generate_random_endpoint(); };
assert_eq!(true, relay_map.add_ip_node(test_public_id.clone(),
test_endpoint_1.clone()));
assert_eq!(true, relay_map.contains_relay_for(&test_public_id.name()));
assert_eq!(true, relay_map.contains_endpoint(&test_endpoint_1));
assert_eq!(false, relay_map.add_ip_node(test_public_id.clone(),
test_endpoint_1.clone()));
assert_eq!(true, relay_map.add_ip_node(test_public_id.clone(),
test_endpoint_2.clone()));
assert!(relay_map.get_endpoints(&test_public_id.name()).unwrap().1
.contains(&test_endpoint_1));
assert!(relay_map.get_endpoints(&test_public_id.name()).unwrap().1
.contains(&test_endpoint_2));
}
}