use tokio_proto::multiplex::RequestId;
use std::collections::VecDeque;
use std::ops::Deref;
use std::time::{Duration, Instant};
use toxcore::crypto_core::*;
pub const PING_TIMEOUT: u64 = 5;
pub const RESPONSE_CHECK: u64 = 60;
pub const BAD_TIME: u64 = RESPONSE_CHECK * 2 + 2;
pub const UNRESPONSIVE_TIME: u64 = BAD_TIME + RESPONSE_CHECK;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum NodeState {
Good,
Bad,
Unresponsive,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct NodeTimeout {
time: Instant,
id: RequestId,
pk: PublicKey,
}
impl NodeTimeout {
pub fn new(pk: &PublicKey, id: RequestId) -> Self {
NodeTimeout { time: Instant::now(), id: id, pk: *pk }
}
pub fn id(&self) -> RequestId {
self.id
}
pub fn pk(&self) -> &PublicKey {
&self.pk
}
pub fn is_timed_out(&self, secs: u64) -> bool {
self.time.elapsed() > Duration::from_secs(secs)
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct TimeoutQueue {
vec: VecDeque<NodeTimeout>,
}
impl TimeoutQueue {
#[cfg(test)] pub fn len(&self) -> usize {
self.vec.len()
}
pub fn push(&mut self, nt: NodeTimeout) {
self.vec.push_back(nt)
}
pub fn add(&mut self, pk: &PublicKey, id: RequestId) {
self.push(NodeTimeout::new(pk, id));
}
pub fn remove(&mut self, id: RequestId) -> bool {
match self.vec.iter().position(|nt| nt.id() == id) {
Some(pos) => {
if let Some(rm) = self.vec.remove(pos) {
self.vec.retain(|nt| nt.pk() != rm.pk());
}
true
},
None => false
}
}
pub fn get_timed_out(&mut self, secs: u64) -> Vec<PublicKey> {
let mut ret = Vec::new();
loop {
match self.vec.front() {
Some(node) if node.is_timed_out(secs) => {},
_ => break,
}
if let Some(node) = self.vec.pop_front() {
ret.push(*node.pk());
}
}
ret
}
}
impl Deref for TimeoutQueue {
type Target = VecDeque<NodeTimeout>;
fn deref(&self) -> &Self::Target {
&self.vec
}
}
#[cfg(test)]
mod test {
use quickcheck::{Arbitrary, Gen, TestResult};
use std::time::Duration;
use toxcore::crypto_core::*;
use toxcore::timeout::*;
use std::thread;
impl Arbitrary for NodeTimeout {
fn arbitrary<G: Gen>(g: &mut G) -> Self {
let mut bytes = [0; PUBLICKEYBYTES];
g.fill_bytes(&mut bytes);
NodeTimeout::new(&PublicKey(bytes), g.gen())
}
}
quickcheck! {
fn node_timeout_new_test(nt: NodeTimeout, id: u64) -> () {
assert!(Duration::from_secs(1) > nt.time.elapsed());
let pk = PublicKey([0; PUBLICKEYBYTES]);
let nt2 = NodeTimeout::new(&pk, id);
assert_eq!(nt2.pk(), &pk);
assert_eq!(id, nt2.id);
assert_ne!(nt.pk(), nt2.pk());
assert!(nt.time < nt2.time);
}
}
quickcheck! {
fn node_timeout_id_test(nt: NodeTimeout, id: u64) -> () {
let mut nt = nt;
nt.id = id;
assert_eq!(id, nt.id());
}
}
quickcheck! {
fn node_timeout_pk_test(nt: NodeTimeout) -> () {
assert_eq!(nt.pk(), &nt.pk);
}
}
#[test]
fn node_timeout_is_timed_out_test() {
let nt = NodeTimeout::new(&PublicKey([0; PUBLICKEYBYTES]), 0);
assert!(nt.is_timed_out(0));
assert!(!nt.is_timed_out(1));
thread::sleep(Duration::from_secs(1));
assert!(nt.is_timed_out(1));
thread::sleep(Duration::from_secs(1));
assert!(nt.is_timed_out(2));
}
quickcheck! {
fn timeout_queue_push_test(nts: Vec<NodeTimeout>) -> TestResult {
if nts.is_empty() { return TestResult::discard() }
let mut tq = TimeoutQueue::default();
for (n, nt) in nts.iter().enumerate() {
assert_eq!(n, tq.vec.len());
tq.push(*nt);
assert_eq!(nt, &tq.vec[n]);
}
TestResult::passed()
}
}
quickcheck! {
fn timeout_queue_add_test(id: u64) -> () {
let mut tq = TimeoutQueue::default();
let (pk, _) = gen_keypair();
tq.add(&pk, id);
assert_eq!(&pk, tq.vec[0].pk());
assert_eq!(id, tq.vec[0].id());
}
}
quickcheck! {
fn timeout_queue_remove_test(nts: Vec<NodeTimeout>, id: u64)
-> TestResult
{
if nts.is_empty() { return TestResult::discard() }
let mut tq = TimeoutQueue::default();
for nt in &nts {
tq.push(*nt);
}
for (num, nt) in nts.iter().enumerate() {
assert!(tq.remove(nt.id()));
assert!(!tq.remove(nt.id()));
assert_eq!(nts.len() - (num + 1), tq.vec.len());
assert_eq!(&nts[num + 1..],
tq.vec.iter().cloned()
.collect::<Vec<_>>().as_slice());
}
assert!(tq.vec.is_empty());
let ntout = NodeTimeout::new(nts[0].pk(), id);
tq.push(nts[0]);
tq.push(ntout);
tq.remove(nts[0].id());
assert!(tq.vec.is_empty());
TestResult::passed()
}
}
quickcheck! {
fn timeout_queue_get_timed_out_test(nts: Vec<NodeTimeout>)
-> TestResult
{
if nts.is_empty() { return TestResult::discard() }
let mut tq = TimeoutQueue::default();
assert!(tq.get_timed_out(0).is_empty());
for (n, nt) in nts.iter().enumerate() {
tq.push(*nt);
assert!(tq.get_timed_out(1).is_empty());
assert_eq!(n + 1, tq.vec.len());
}
assert_eq!(nts.iter().map(|nt| *nt.pk()).collect::<Vec<_>>(),
tq.get_timed_out(0));
assert!(tq.vec.is_empty());
let nodetimeout = {
let mut nt = nts[0];
nt.time -= Duration::from_secs(1);
nt
};
tq.push(nodetimeout);
for nt in &nts[1..] {
tq.push(*nt);
}
assert_eq!(vec![*nodetimeout.pk()], tq.get_timed_out(1));
TestResult::passed()
}
}
#[test]
fn timeout_queue_deref_test() {
let tq = TimeoutQueue::default();
assert_eq!(&tq.vec, tq.deref());
}
}