use std::io;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
use quinn::{ClientConfig, Endpoint, RecvStream, SendStream, ServerConfig};
use crate::traits::transport::Transport;
pub enum QuicEndpoint {
Client {
addr: SocketAddr,
server_name: String,
config: ClientConfig,
},
Server {
addr: SocketAddr,
config: ServerConfig,
},
}
pub struct QuicTransport;
impl QuicTransport {
pub async fn connect(
addr: SocketAddr,
server_name: &str,
config: ClientConfig,
) -> io::Result<(RecvStream, SendStream)> {
let bind_addr = match addr {
SocketAddr::V4(_) => SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 0),
SocketAddr::V6(_) => SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), 0),
};
let endpoint = Endpoint::client(bind_addr)?;
let conn = endpoint
.connect_with(config, addr, server_name)
.map_err(io::Error::other)?
.await
.map_err(io::Error::other)?;
let (send, recv) = conn.open_bi().await.map_err(io::Error::other)?;
tokio::spawn(async move {
let _endpoint = endpoint;
conn.closed().await;
});
Ok((recv, send))
}
pub fn bind(addr: SocketAddr, config: ServerConfig) -> io::Result<Endpoint> {
Endpoint::server(config, addr)
}
pub async fn accept(listener: &Endpoint) -> io::Result<(RecvStream, SendStream)> {
let incoming = listener.accept().await.ok_or_else(|| {
io::Error::new(io::ErrorKind::UnexpectedEof, "endpoint closed while accepting")
})?;
let conn = incoming.await.map_err(io::Error::other)?;
let (send, recv) = conn.accept_bi().await.map_err(io::Error::other)?;
tokio::spawn(async move {
conn.closed().await;
});
Ok((recv, send))
}
}
impl Transport for QuicTransport {
type Endpoint = QuicEndpoint;
type Listener = Endpoint;
type Read = RecvStream;
type Write = SendStream;
async fn connect(endpoint: &QuicEndpoint) -> io::Result<(RecvStream, SendStream)> {
match endpoint {
QuicEndpoint::Client {
addr,
server_name,
config,
} => QuicTransport::connect(*addr, server_name, config.clone()).await,
QuicEndpoint::Server { .. } => Err(io::Error::new(
io::ErrorKind::InvalidInput,
"connect requires the client endpoint form",
)),
}
}
async fn bind(endpoint: &QuicEndpoint) -> io::Result<Endpoint> {
match endpoint {
QuicEndpoint::Server { addr, config } => QuicTransport::bind(*addr, config.clone()),
QuicEndpoint::Client { .. } => Err(io::Error::new(
io::ErrorKind::InvalidInput,
"bind requires the server endpoint form",
)),
}
}
async fn accept(listener: &Endpoint) -> io::Result<(RecvStream, SendStream)> {
QuicTransport::accept(listener).await
}
}