quic-socket 0.7.0

Socket-esc wrapper on the quiche QUIC implementation
Documentation
use anyhow::{anyhow, Result};
use futures_util::StreamExt;
use quinn::ServerConfig;
use std::{error::Error, fs, net::SocketAddr, net::ToSocketAddrs, sync::Arc};
use url::Url;

pub struct QuicListener {}

impl QuicListener {
    #[allow(clippy::field_reassign_with_default)] // https://github.com/rust-lang/rust-clippy/issues/6527
    pub async fn recv(listen: SocketAddr, path: String) -> Result<std::vec::Vec<u8>> {
        let (server_config, _) = configure_server(path).unwrap();
        let (_, mut incoming) = quinn::Endpoint::server(server_config, listen)?;
        let mut ret = None;
        while let Some(conn) = incoming.next().await {
            ret = Some(tokio::spawn(handle_connection(conn)).await);
            break;
        }
        Ok(ret.unwrap().unwrap().unwrap())
    }

    pub async fn send(
        ca: String,
        remote_url: Url,
        host: Option<String>,
        payload: &mut [u8],
    ) -> Result<()> {
        let remote = (
            remote_url.host_str().unwrap(),
            remote_url.port().unwrap_or(4433),
        )
            .to_socket_addrs()?
            .next()
            .ok_or_else(|| anyhow!("couldn't resolve to an address"))
            .unwrap();
        let mut roots = rustls::RootCertStore::empty();
        roots.add(&rustls::Certificate(fs::read(&ca)?)).unwrap();
        let mut client_crypto = rustls::ClientConfig::builder()
            .with_safe_defaults()
            .with_root_certificates(roots)
            .with_no_client_auth();
        client_crypto.alpn_protocols = ALPN_QUIC_HTTP.iter().map(|&x| x.into()).collect();
        let mut endpoint = quinn::Endpoint::client("[::]:0".parse().unwrap())?;
        endpoint.set_default_client_config(quinn::ClientConfig::new(Arc::new(client_crypto)));
        let host = host
            .as_ref()
            .map_or_else(|| remote_url.host_str(), |x| Some(x))
            .ok_or_else(|| anyhow!("no hostname specified"))?;
        let new_conn = endpoint
            .connect(remote, host)?
            .await
            .map_err(|e| anyhow!("failed to connect: {}", e))?;
        let quinn::NewConnection {
            connection: conn, ..
        } = new_conn;
        let (mut send, _) = conn
            .open_bi()
            .await
            .map_err(|e| anyhow!("failed to open stream: {}", e))?;
        send.write_all(payload)
            .await
            .map_err(|e| anyhow!("failed to send request: {}", e))?;
        send.finish()
            .await
            .map_err(|e| anyhow!("failed to shutdown stream: {}", e))?;
        conn.close(0u32.into(), b"done");
        endpoint.wait_idle().await;
        Ok(())
    }
}

#[allow(clippy::field_reassign_with_default)] // https://github.com/rust-lang/rust-clippy/issues/6527
fn configure_server(path: String) -> Result<(ServerConfig, Vec<u8>), Box<dyn Error>> {
    let cert = rcgen::generate_simple_self_signed(vec!["localhost".into()]).unwrap();
    let cert_der = cert.serialize_der().unwrap();
    let priv_key = cert.serialize_private_key_der();
    let priv_key = rustls::PrivateKey(priv_key);
    let cert_chain = vec![rustls::Certificate(cert_der.clone())];
    let mut server_crypto = rustls::ServerConfig::builder()
        .with_safe_defaults()
        .with_no_client_auth()
        .with_single_cert(cert_chain, priv_key)?;
    server_crypto.alpn_protocols = ALPN_QUIC_HTTP.iter().map(|&x| x.into()).collect();
    let mut server_config = quinn::ServerConfig::with_crypto(Arc::new(server_crypto));
    Arc::get_mut(&mut server_config.transport)
        .unwrap()
        .max_concurrent_uni_streams(0_u8.into());
    fs::write(path, &cert_der).unwrap();
    Ok((server_config, cert_der))
}

#[allow(unused)]
pub const ALPN_QUIC_HTTP: &[&[u8]] = &[b"hq-29"];

async fn handle_connection(conn: quinn::Connecting) -> Result<std::vec::Vec<u8>> {
    Ok(handle_spawn(conn).await.unwrap())
}

async fn handle_spawn(conn: quinn::Connecting) -> Result<std::vec::Vec<u8>> {
    let quinn::NewConnection {
        connection: _,
        mut bi_streams,
        ..
    } = conn.await?;
    let mut req = None;
    while let Some(stream) = bi_streams.next().await {
        let stream = match stream {
            Err(quinn::ConnectionError::ApplicationClosed { .. }) => {
                return Ok(vec![]);
            }
            Err(e) => {
                return Err(anyhow::Error::new(e));
            }
            Ok(s) => s,
        };
        req = Some(tokio::spawn(handle_request(stream)).await?);
        break;
    }
    Ok(req.unwrap())
}

async fn handle_request((_, recv): (quinn::SendStream, quinn::RecvStream)) -> std::vec::Vec<u8> {
    let req = recv
        .read_to_end(64 * 1024)
        .await
        .map_err(|e| anyhow!("failed reading request: {}", e))
        .unwrap();
    req
}