Files @ 9206016be13b
Branch filter:

Location: CSY/reowolf/src/runtime2/tests/internet.rs

9206016be13b 6.2 KiB application/rls-services+xml Show Annotation Show as Raw Download as Raw
Max Henger
Merge branch 'feat-tcp-listener' into 'master'

feat: tcp listener

See merge request nl-cwi-csy/reowolf!9
use super::*;

// silly test to make sure that the PDL will never be an issue when doing TCP
// stuff with the actual components
#[test]
fn test_stdlib_file() {
    compile_and_create_component("
    import std.internet as inet;

    comp fake_listener_once(out<inet::TcpConnection> tx) {
        channel cmd_tx -> cmd_rx;
        channel data_tx -> data_rx;
        new fake_socket(cmd_rx, data_tx);
        sync put(tx, inet::TcpConnection{
            tx: cmd_tx,
            rx: data_rx,
        });
    }

    comp fake_socket(in<inet::ClientCmd> cmds, out<u8[]> tx) {
        auto to_send = {};

        auto shutdown = false;
        while (!shutdown) {
            auto keep_going = true;
            sync {
                while (keep_going) {
                    auto cmd = get(cmds);
                    if (let inet::ClientCmd::Send(data) = cmd) {
                        to_send = data;
                        keep_going = false;
                    } else if (let inet::ClientCmd::Receive = cmd) {
                        put(tx, to_send);
                    } else if (let inet::ClientCmd::Finish = cmd) {
                        keep_going = false;
                    } else if (let inet::ClientCmd::Shutdown = cmd) {
                        keep_going = false;
                        shutdown = true;
                    }
                }
            }
        }
    }

    comp fake_client(inet::TcpConnection conn) {
        sync put(conn.tx, inet::ClientCmd::Send({1, 3, 3, 7}));
        sync {
            put(conn.tx, inet::ClientCmd::Receive);
            auto val = get(conn.rx);
            while (val[0] != 1 || val[1] != 3 || val[2] != 3 || val[3] != 7) {
                print(\"this is going very wrong\");
            }
            put(conn.tx, inet::ClientCmd::Finish);
        }
        sync put(conn.tx, inet::ClientCmd::Shutdown);
    }

    comp constructor() {
        channel conn_tx -> conn_rx;
        new fake_listener_once(conn_tx);

        // Same crap as before:
        channel cmd_tx -> unused_cmd_rx;
        channel unused_data_tx -> data_rx;
        auto connection = inet::TcpConnection{ tx: cmd_tx, rx: data_rx };

        sync {
            connection = get(conn_rx);
        }

        new fake_client(connection);
    }
    ", "constructor", no_args());
}

#[test]
fn test_tcp_listener_and_client() {
    compile_and_create_component("
    import std.internet::*;

    func listen_port() -> u16 {
        return 2392;
    }

    comp server(u32 num_connections, in<()> shutdown) {
        // Start tcp listener
        channel listen_cmd_tx -> listen_cmd_rx;
        channel listen_conn_tx -> listen_conn_rx;
        new tcp_listener({}, listen_port(), listen_cmd_rx, listen_conn_tx);

        // Fake channels such that we can create a dummy connection variable
        channel client_cmd_tx -> unused_client_cmd_rx;
        channel unused_client_data_tx -> client_data_rx;
        auto new_connection = TcpConnection{
            tx: client_cmd_tx,
            rx: client_data_rx,
        };

        auto connection_counter = 0;
        while (connection_counter < num_connections) {
            // Wait until we get a connection
            print(\"server: waiting for an accepted connection\");
            sync {
                put(listen_cmd_tx, ListenerCmd::Accept);
                new_connection = get(listen_conn_rx);
            }

            // We have a new connection, spawn an 'echoer' for it
            print(\"server: spawning an echo'ing component\");
            new echo_machine(new_connection);
            connection_counter += 1;
        }

        // Shut down the listener
        print(\"server: shutting down listener\");
        sync auto v = get(shutdown);
        sync put(listen_cmd_tx, ListenerCmd::Shutdown);
    }

    // Waits for a single TCP byte (to simplify potentially having to
    // concatenate requests) and echos it
    comp echo_machine(TcpConnection conn) {
        auto data_to_echo = {};

        // Wait for a message
        sync {
            print(\"echo: receiving data\");
            put(conn.tx, ClientCmd::Receive);
            data_to_echo = get(conn.rx);
            put(conn.tx, ClientCmd::Finish);
        }

        // Echo the message
        print(\"echo: sending back data\");
        sync put(conn.tx, ClientCmd::Send(data_to_echo));

        // Ask the tcp connection to shut down
        print(\"echo: shutting down\");
        sync put(conn.tx, ClientCmd::Shutdown);
    }

    comp echo_requester(u8 byte_to_send, out<()> done) {
        channel cmd_tx -> cmd_rx;
        channel data_tx -> data_rx;
        new tcp_client({127, 0, 0, 1}, listen_port(), cmd_rx, data_tx);

        // Send the message
        print(\"requester: sending bytes\");
        sync put(cmd_tx, ClientCmd::Send({ byte_to_send }));

        // Receive the echo'd byte
        auto received_byte = byte_to_send + 1;
        sync {
            print(\"requester: receiving echo response\");
            put(cmd_tx, ClientCmd::Receive);
            received_byte = get(data_rx)[0];
            put(cmd_tx, ClientCmd::Finish);
        }

        // Silly check, as always
        while (byte_to_send != received_byte) {
            print(\"requester: Oh no! The echo is an otherworldly distorter\");
        }

        // Shut down the TCP connection
        print(\"requester: shutting down TCP component\");
        sync put(cmd_tx, ClientCmd::Shutdown);
        sync put(done, ());
    }

    comp constructor() {
        auto num_connections = 1;
        channel shutdown_listener_tx -> shutdown_listener_rx;
        new server(num_connections, shutdown_listener_rx);

        auto connection_index = 0;
        auto all_done = {};
        while (connection_index < num_connections) {
            channel done_tx -> done_rx;
            new echo_requester(cast(connection_index), done_tx);
            connection_index += 1;
            all_done @= {done_rx};
        }

        auto counter = 0;
        while (counter < length(all_done)) {
            print(\"constructor: waiting for requester to exit\");
            sync auto v = get(all_done[counter]);
            counter += 1;
        }

        print(\"constructor: instructing listener to exit\");
        sync put(shutdown_listener_tx, ());
    }
    ", "constructor", no_args());
}