conclave_room_net/
lib.rs

1/*----------------------------------------------------------------------------------------------------------
2 *  Copyright (c) Peter Bjorklund. All rights reserved. https://github.com/piot/conclave-room-net-rs
3 *  Licensed under the MIT License. See LICENSE in the project root for license information.
4 *--------------------------------------------------------------------------------------------------------*/
5//! The Conclave Net Layer
6//!
7//! Easier to handle incoming network commands and construct outgoing messages
8
9use std::io::Cursor;
10use std::time::Instant;
11
12use conclave_room;
13use conclave_room::{ConnectionIndex, Room};
14use conclave_room_serialize::{RoomInfoCommand, ServerReceiveCommand};
15
16pub struct NetworkConnection {
17    pub id: ConnectionIndex,
18    pub room: Room,
19}
20
21pub trait SendDatagram {
22    fn send(&self) -> Vec<u8>;
23}
24
25impl SendDatagram for Room {
26    fn send(&self) -> Vec<u8> {
27        let room_info_command = RoomInfoCommand {
28            term: self.term,
29            leader_index: self.leader_index,
30            client_infos: vec![],
31        };
32
33        room_info_command.to_octets()
34    }
35}
36
37pub trait ReceiveDatagram {
38    fn receive(&mut self, connection_id: ConnectionIndex, now: Instant, buffer: Cursor<&[u8]>) -> Result<(), String>;
39}
40
41impl ReceiveDatagram for Room {
42    fn receive(&mut self, connection_id: ConnectionIndex, now: Instant, reader: Cursor<&[u8]>) -> Result<(), String> {
43        if !self.connections.contains_key(&connection_id) {
44            return Err(format!("there is no connection {}", connection_id));
45        }
46        let connection = self.connections.get_mut(&connection_id).unwrap();
47        let command = ServerReceiveCommand::from_cursor(reader).unwrap();
48        match command {
49            ServerReceiveCommand::PingCommandType(ping_command) => {
50                connection.on_ping(ping_command.term, ping_command.has_connection_to_leader, ping_command.knowledge, now);
51            }
52        }
53        Ok(())
54    }
55}
56
57#[cfg(test)]
58mod tests {
59    use std::io::Cursor;
60    use std::time::Instant;
61
62    use conclave_room::Room;
63    use conclave_room_serialize::PING_COMMAND_TYPE_ID;
64
65    use crate::{ReceiveDatagram, SendDatagram};
66
67    #[test]
68    fn check_send() {
69        let room = Room::new();
70        let octets = room.send();
71
72        assert_eq!(vec![0x00, 0x00, 0x00, 0xff], octets);
73    }
74
75    #[test]
76    fn on_ping() {
77        const EXPECTED_KNOWLEDGE_VALUE: u64 = 17718865395771014920;
78        let octets = [
79            PING_COMMAND_TYPE_ID,
80            0x00, // Term
81            0x20,
82            0xF5,  // Knowledge
83            0xE6,
84            0x0E,
85            0x32,
86            0xE9,
87            0xE4,
88            0x7F,
89            0x08,
90            0x01, // Has Connection
91        ];
92        let receive_cursor = Cursor::new(octets.as_slice());
93
94        let mut room = Room::new();
95        let now = Instant::now();
96        let first_mutable_connection = room.create_connection(now);
97        let first_connection_id = first_mutable_connection.id;
98        let receive_result = room.receive(first_connection_id, now, receive_cursor);
99        assert_eq!(receive_result, Ok(()));
100
101        let connection_after_receive = room.connections.get(&first_connection_id).unwrap();
102        assert_eq!(connection_after_receive.knowledge, EXPECTED_KNOWLEDGE_VALUE);
103    }
104}