liminal-server 0.25.4

Standalone server for the liminal messaging bus
Documentation
//! SRV-044 R1: a WebSocket client that connects and sends its upgrade
//! between the bind and the accept waits in the listen backlog and is
//! upgraded once accepting starts; an address in use refuses at the bind.
use std::net::{SocketAddr, TcpListener, TcpStream};

use super::super::WebSocketListener;
use super::BoundWebSocket;
use crate::ServerError;
use crate::config::types::WebSocketConfig;
use crate::server::connection::ConnectionSupervisor;

type TestResult = Result<(), Box<dyn std::error::Error>>;

fn config(listen_address: SocketAddr) -> WebSocketConfig {
    WebSocketConfig {
        listen_address,
        path: "/liminal".to_owned(),
        allowed_origins: Vec::new(),
        allowed_origin_ports: Vec::new(),
        ping_interval_ms: None,
    }
}

/// Connects to `address` and runs the upgrade on its own thread, which
/// blocks reading the response until the server accepts and answers.
pub(in crate::server) fn upgrade_on_a_thread(
    address: SocketAddr,
) -> Result<std::thread::JoinHandle<Result<u16, String>>, std::io::Error> {
    let stream = TcpStream::connect(address)?;
    Ok(std::thread::spawn(move || {
        let (socket, response) =
            tungstenite::client::client(format!("ws://{address}/liminal"), stream)
                .map_err(|error| error.to_string())?;
        drop(socket);
        Ok(response.status().as_u16())
    }))
}

#[test]
fn a_client_upgrading_before_accepting_is_upgraded_once_it_starts() -> TestResult {
    let bound = BoundWebSocket::bind(&config("127.0.0.1:0".parse()?))?;
    let client = upgrade_on_a_thread(bound.local_addr())?;
    let supervisor = ConnectionSupervisor::new()?;

    let listener = WebSocketListener::accept_on(bound, supervisor.clone())?;
    let status = client
        .join()
        .map_err(|_| "the upgrading client panicked")??;
    assert_eq!(status, 101, "the early client is upgraded");
    listener.shutdown()?;
    supervisor.shutdown();
    Ok(())
}

#[test]
fn an_address_in_use_refuses_at_the_bind() -> TestResult {
    let taken = TcpListener::bind("127.0.0.1:0")?;
    let address = taken.local_addr()?;
    let refused = BoundWebSocket::bind(&config(address));
    assert!(
        matches!(refused, Err(ServerError::ListenerBind { address: failed, .. }) if failed == address),
        "{refused:?}"
    );
    Ok(())
}

#[test]
fn the_bound_address_resolves_an_ephemeral_port() -> TestResult {
    let bound = BoundWebSocket::bind(&config("127.0.0.1:0".parse()?))?;
    assert_ne!(bound.local_addr().port(), 0, "{bound:?}");
    Ok(())
}