compio-ws 0.3.1

WebSocket library for the compio runtime
Documentation
use compio_tls::TlsConnector;
use compio_ws::connect_async_tls_with_config;
use tungstenite::Message;

// Create a TLS connector that accepts self-signed certificates
// This is needed for testing with localhost self-signed certificates
// WARNING: This is insecure and should only be used for testing!
#[cfg(feature = "native-tls")]
async fn create_insecure_tls_connector() -> Result<TlsConnector, Box<dyn std::error::Error>> {
    let mut builder = compio_tls::native_tls::TlsConnector::builder();
    builder
        .danger_accept_invalid_certs(true)
        .danger_accept_invalid_hostnames(true);
    let connector = builder.build()?;
    Ok(TlsConnector::from(connector))
}

#[cfg(all(feature = "rustls", not(feature = "native-tls")))]
async fn create_insecure_tls_connector() -> Result<TlsConnector, Box<dyn std::error::Error>> {
    use std::sync::Arc;

    use compio_tls::rustls;
    use rustls::ClientConfig;

    #[derive(Debug)]
    struct AcceptAllVerifier;

    impl rustls::client::danger::ServerCertVerifier for AcceptAllVerifier {
        fn verify_server_cert(
            &self,
            _end_entity: &rustls::pki_types::CertificateDer<'_>,
            _intermediates: &[rustls::pki_types::CertificateDer<'_>],
            _server_name: &rustls::pki_types::ServerName<'_>,
            _ocsp_response: &[u8],
            _now: rustls::pki_types::UnixTime,
        ) -> Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
            Ok(rustls::client::danger::ServerCertVerified::assertion())
        }

        fn verify_tls12_signature(
            &self,
            _message: &[u8],
            _cert: &rustls::pki_types::CertificateDer<'_>,
            _dss: &rustls::DigitallySignedStruct,
        ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
            Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
        }

        fn verify_tls13_signature(
            &self,
            _message: &[u8],
            _cert: &rustls::pki_types::CertificateDer<'_>,
            _dss: &rustls::DigitallySignedStruct,
        ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
            Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
        }

        fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
            vec![
                rustls::SignatureScheme::RSA_PKCS1_SHA1,
                rustls::SignatureScheme::ECDSA_SHA1_Legacy,
                rustls::SignatureScheme::RSA_PKCS1_SHA256,
                rustls::SignatureScheme::ECDSA_NISTP256_SHA256,
                rustls::SignatureScheme::RSA_PKCS1_SHA384,
                rustls::SignatureScheme::ECDSA_NISTP384_SHA384,
                rustls::SignatureScheme::RSA_PKCS1_SHA512,
                rustls::SignatureScheme::ECDSA_NISTP521_SHA512,
                rustls::SignatureScheme::RSA_PSS_SHA256,
                rustls::SignatureScheme::RSA_PSS_SHA384,
                rustls::SignatureScheme::RSA_PSS_SHA512,
                rustls::SignatureScheme::ED25519,
                rustls::SignatureScheme::ED448,
            ]
        }
    }

    let config = ClientConfig::builder()
        .dangerous()
        .with_custom_certificate_verifier(Arc::new(AcceptAllVerifier))
        .with_no_client_auth();

    Ok(TlsConnector::from(Arc::new(config)))
}

#[compio_macros::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Create an insecure TLS connector for testing with self-signed certificates
    // This accepts the self-signed certificate generated by the server
    #[cfg(any(feature = "native-tls", feature = "rustls"))]
    let connector = Some(create_insecure_tls_connector().await?);
    #[cfg(not(any(feature = "native-tls", feature = "rustls")))]
    let connector = None;

    let (mut websocket, _response) =
        connect_async_tls_with_config("wss://127.0.0.1:9002", None, connector).await?;

    println!("Successfully connected to WebSocket TLS server!");
    println!();

    println!("Test 1: Sending text message");
    websocket
        .send(Message::Text("Hello, secure server!".into()))
        .await?;

    println!("Test 2: Sending binary message");
    websocket
        .send(Message::Binary(vec![1, 2, 3, 4, 5].into()))
        .await?;

    println!("Test 3: Sending ping");
    websocket.send(Message::Ping(vec![42].into())).await?;

    println!("Reading responses from server:");

    for i in 0..3 {
        match websocket.read().await? {
            Message::Text(text) => {
                println!("  Response {}: Text: {}", i + 1, text);
            }
            Message::Binary(data) => {
                let data_str = String::from_utf8_lossy(&data);
                println!("  Response {}: Binary: {}", i + 1, data_str);
            }
            Message::Pong(data) => {
                println!("  Response {}: Pong: {:?}", i + 1, data);
            }
            other => {
                println!("  Response {}: {:?}", i + 1, other);
            }
        }
    }

    println!("Closing TLS connection...");
    websocket.close(None).await?;
    println!("TLS connection closed successfully!");

    Ok(())
}