connection-layer 0.0.2

Datagram Connection Hash Layer
Documentation
/*
 * Copyright (c) Peter Bjorklund. All rights reserved. https://github.com/nimble-rust/workspace
 * Licensed under the MIT License. See LICENSE in the project root for license information.
 */

use connection_layer::prelude::*;
use datagram::{DatagramDecoder, DatagramEncoder};
use flood_rs::prelude::*;
use secure_random::SecureRandom;
use std::io;

#[test_log::test]
fn test_header() {
    let connection = ConnectionLayerMode::Connection(ConnectionLayer {
        connection_id: ConnectionId { value: 42 },
        murmur3_hash: 0xfe334411,
    });

    let mut writer = OutOctetStream::new();
    connection.to_stream(&mut writer).expect("should work");

    let buf = writer.octets_ref();
    assert_eq!(buf[0], 42);
    assert_eq!(&buf[1..=4], &[0xfe, 0x33, 0x44, 0x11]);

    let mut reader = InOctetStream::new(buf);
    assert_eq!(
        ConnectionLayerMode::from_stream(&mut reader).expect("should work"),
        connection
    );
}

#[derive(Debug)]
pub struct FakeRandom {
    pub counter: u64,
}

impl SecureRandom for FakeRandom {
    fn get_random_u64(&mut self) -> u64 {
        self.counter += 1;
        self.counter
    }
}

#[test_log::test]
fn codec() -> io::Result<()> {
    // Setup
    let request_id: RequestId = 0x0001020304050607;
    let mut client_codec = ConnectionLayerClientCodec::new(request_id);

    let random2 = FakeRandom { counter: 0 };
    let boxed_random2 = Box::new(random2);
    let mut host_codec = ConnectionLayerHostCodec::new(boxed_random2);

    // First message Client -> Host
    let test_octets = &[b'h', b'e', b'l', b'l', b'o'];
    let data_to_send = client_codec.encode(test_octets)?;

    // Verify
    #[rustfmt::skip]
    let expected_test_octets = &[
        0, // Connection ID. Zero is OOB
        0x05, // Connect Request
        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // Request ID
        0x00, 0x02, // Connection Layer Version
        b'h', b'e', b'l', b'l', b'o'];
    hexify::assert_eq_slices(&data_to_send, expected_test_octets);
    let (connection_id, decoded) = host_codec.decode(data_to_send.as_slice())?;
    assert_eq!(decoded, test_octets);

    // Host -> Client
    const EXPECTED_CONNECTION_ID: u8 = 1;
    assert_eq!(connection_id, EXPECTED_CONNECTION_ID);

    let test_reply_octets = &[b'w', b'o', b'r', b'l', b'd', b'!'];

    let host_to_client_reply = host_codec.encode(connection_id, test_reply_octets)?;
    #[rustfmt::skip]
    let expected_host_to_client_reply = &[
        0, // Connection Id.
        0x06, // Connect Response
        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // Request ID
        EXPECTED_CONNECTION_ID, // Created connection id
        0x00, 0x00, 0x00, 0x01,  // Secret seed
        b'w', b'o', b'r', b'l', b'd', b'!'];
    hexify::assert_eq_slices(&host_to_client_reply, expected_host_to_client_reply);

    let client_received_reply = client_codec.decode(&host_to_client_reply)?;
    hexify::assert_eq_slices(&client_received_reply, test_reply_octets);

    // Client -> Host
    let test_after_connected_octets = &[b'l', b'o', b'v', b'e', b'l', b'y'];
    let to_host_after_connected = client_codec.encode(test_after_connected_octets)?;

    // verify
    #[rustfmt::skip]
    let expected_to_host_after_connected = &[
        EXPECTED_CONNECTION_ID, // client should be connected now, so start using the client connection ID
        19, 215, 173, 162,  // Hash for this content
        b'l', b'o', b'v', b'e', b'l', b'y'];
    hexify::assert_eq_slices(&to_host_after_connected, expected_to_host_after_connected);

    let (found_connection_id, from_client_after_connected) =
        host_codec.decode(&*to_host_after_connected)?;
    hexify::assert_eq_slices(&*from_client_after_connected, test_after_connected_octets);
    assert_eq!(found_connection_id, EXPECTED_CONNECTION_ID);

    let host_to_client_msg_after_connected = &[b'w', b'o', b'r', b'k', b's'];
    let host_to_client_after_connected =
        host_codec.encode(connection_id, host_to_client_msg_after_connected)?;

    // verify
    #[rustfmt::skip]
    let expected_to_client_after_connected = &[
        EXPECTED_CONNECTION_ID, // host should know that client is connected, so it will use connection_id without any command
        129, 15, 178, 151, // Hash for this content
        b'w', b'o', b'r', b'k', b's'];
    hexify::assert_eq_slices(
        &host_to_client_after_connected,
        expected_to_client_after_connected,
    );

    Ok(())
}