use std::pin::Pin;
use std::task::{Context, Poll};
use quinn::VarInt;
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
use weida_core::{Error, PeerIdentity};
use crate::conn::{conn_error, read_error, write_error};
use crate::inproc::{LocalConn, LocalRecv, LocalSend};
pub(crate) enum Link {
Quic(quinn::Connection),
Local(LocalConn),
#[cfg(unix)]
Unix(Box<crate::grouped::Grouped<crate::unix::UnixLocal>>),
#[cfg(windows)]
Pipe(Box<crate::grouped::Grouped<crate::pipe::PipeStream>>),
}
pub(crate) enum SendHalf {
Quic(quinn::SendStream),
Local(LocalSend),
#[cfg(unix)]
Unix(crate::grouped::LocalSend<crate::unix::UnixLocal>),
#[cfg(windows)]
Pipe(crate::grouped::LocalSend<crate::pipe::PipeStream>),
}
pub(crate) enum RecvHalf {
Quic(quinn::RecvStream),
Local(LocalRecv),
#[cfg(unix)]
Unix(crate::grouped::LocalRecv<crate::unix::UnixLocal>),
#[cfg(windows)]
Pipe(crate::grouped::LocalRecv<crate::pipe::PipeStream>),
}
impl Link {
pub(crate) fn peer(&self) -> Option<PeerIdentity> {
match self {
Link::Quic(conn) => crate::tls::peer_fingerprint(conn).map(PeerIdentity::Key),
Link::Local(_) => None,
#[cfg(unix)]
Link::Unix(conn) => conn.peer(),
#[cfg(windows)]
Link::Pipe(conn) => conn.peer(),
}
}
pub(crate) fn dispatch_by_path(&self) -> bool {
match self {
Link::Quic(_) | Link::Local(_) => false,
#[cfg(unix)]
Link::Unix(_) => true,
#[cfg(windows)]
Link::Pipe(_) => true,
}
}
pub(crate) fn streams_are_local(&self) -> bool {
match self {
Link::Quic(_) => false,
Link::Local(_) => true,
#[cfg(unix)]
Link::Unix(_) => true,
#[cfg(windows)]
Link::Pipe(_) => true,
}
}
pub(crate) fn local_slots_exhausted(&self) -> bool {
match self {
Link::Quic(_) => false,
Link::Local(conn) => conn.slots_exhausted(),
#[cfg(unix)]
Link::Unix(conn) => conn.slots_exhausted(),
#[cfg(windows)]
Link::Pipe(conn) => conn.slots_exhausted(),
}
}
pub(crate) fn stable_id(&self) -> usize {
match self {
Link::Quic(conn) => conn.stable_id(),
Link::Local(conn) => conn.stable_id(),
#[cfg(unix)]
Link::Unix(conn) => conn.stable_id(),
#[cfg(windows)]
Link::Pipe(conn) => conn.stable_id(),
}
}
pub(crate) fn close_reason(&self) -> Option<Error> {
match self {
Link::Quic(conn) => conn.close_reason().map(conn_error),
Link::Local(conn) => conn.close_reason(),
#[cfg(unix)]
Link::Unix(conn) => conn.close_reason(),
#[cfg(windows)]
Link::Pipe(conn) => conn.close_reason(),
}
}
pub(crate) fn close(&self, code: u64, reason: &str) {
match self {
Link::Quic(conn) => conn.close(
VarInt::from_u64(code).expect("application codes are small"),
reason.as_bytes(),
),
Link::Local(conn) => conn.close(code, reason),
#[cfg(unix)]
Link::Unix(conn) => conn.close(code, reason),
#[cfg(windows)]
Link::Pipe(conn) => conn.close(code, reason),
}
}
pub(crate) async fn closed(&self) -> Error {
match self {
Link::Quic(conn) => conn_error(conn.closed().await),
Link::Local(conn) => conn.closed().await,
#[cfg(unix)]
Link::Unix(conn) => conn.closed().await,
#[cfg(windows)]
Link::Pipe(conn) => conn.closed().await,
}
}
pub(crate) async fn open_uni(&self) -> Result<SendHalf, Error> {
match self {
Link::Quic(conn) => conn
.open_uni()
.await
.map(SendHalf::Quic)
.map_err(conn_error),
Link::Local(conn) => conn.open_uni().await.map(SendHalf::Local),
#[cfg(unix)]
Link::Unix(conn) => conn.open_uni().await.map(SendHalf::Unix),
#[cfg(windows)]
Link::Pipe(conn) => conn.open_uni().await.map(SendHalf::Pipe),
}
}
pub(crate) async fn open_control(&self) -> Result<SendHalf, Error> {
match self {
#[cfg(unix)]
Link::Unix(conn) => conn.open_control().map(SendHalf::Unix),
#[cfg(windows)]
Link::Pipe(conn) => conn.open_control().map(SendHalf::Pipe),
_ => self.open_uni().await,
}
}
pub(crate) fn needs_reverse_pool(&self) -> bool {
match self {
#[cfg(unix)]
Link::Unix(_) => true,
#[cfg(windows)]
Link::Pipe(_) => true,
_ => false,
}
}
pub(crate) async fn park_reverse(&self) -> Result<usize, Error> {
match self {
#[cfg(unix)]
Link::Unix(conn) => conn.park_reverse().await,
#[cfg(windows)]
Link::Pipe(conn) => conn.park_reverse().await,
_ => Ok(0),
}
}
pub(crate) async fn maintain_reverse(&self) {
match self {
#[cfg(unix)]
Link::Unix(conn) => conn.maintain_reverse().await,
#[cfg(windows)]
Link::Pipe(conn) => conn.maintain_reverse().await,
_ => {}
}
}
pub(crate) async fn open_bi(&self) -> Result<(SendHalf, RecvHalf), Error> {
match self {
Link::Quic(conn) => conn
.open_bi()
.await
.map(|(s, r)| (SendHalf::Quic(s), RecvHalf::Quic(r)))
.map_err(conn_error),
Link::Local(conn) => conn
.open_bi()
.await
.map(|(s, r)| (SendHalf::Local(s), RecvHalf::Local(r))),
#[cfg(unix)]
Link::Unix(conn) => conn
.open_bi()
.await
.map(|(s, r)| (SendHalf::Unix(s), RecvHalf::Unix(r))),
#[cfg(windows)]
Link::Pipe(conn) => conn
.open_bi()
.await
.map(|(s, r)| (SendHalf::Pipe(s), RecvHalf::Pipe(r))),
}
}
pub(crate) async fn accept_uni(&self) -> Result<RecvHalf, Error> {
match self {
Link::Quic(conn) => conn
.accept_uni()
.await
.map(RecvHalf::Quic)
.map_err(conn_error),
Link::Local(conn) => conn.accept_uni().await.map(RecvHalf::Local),
#[cfg(unix)]
Link::Unix(conn) => conn.accept_uni().await.map(RecvHalf::Unix),
#[cfg(windows)]
Link::Pipe(conn) => conn.accept_uni().await.map(RecvHalf::Pipe),
}
}
pub(crate) async fn accept_bi(&self) -> Result<(SendHalf, RecvHalf), Error> {
match self {
Link::Quic(conn) => conn
.accept_bi()
.await
.map(|(s, r)| (SendHalf::Quic(s), RecvHalf::Quic(r)))
.map_err(conn_error),
Link::Local(conn) => conn
.accept_bi()
.await
.map(|(s, r)| (SendHalf::Local(s), RecvHalf::Local(r))),
#[cfg(unix)]
Link::Unix(conn) => conn
.accept_bi()
.await
.map(|(s, r)| (SendHalf::Unix(s), RecvHalf::Unix(r))),
#[cfg(windows)]
Link::Pipe(conn) => conn
.accept_bi()
.await
.map(|(s, r)| (SendHalf::Pipe(s), RecvHalf::Pipe(r))),
}
}
}
impl SendHalf {
pub(crate) async fn write_all(&mut self, buf: &[u8]) -> Result<(), Error> {
match self {
SendHalf::Quic(s) => s.write_all(buf).await.map_err(write_error),
SendHalf::Local(s) => s.write_all(buf).await,
#[cfg(unix)]
SendHalf::Unix(s) => s.write_all(buf).await,
#[cfg(windows)]
SendHalf::Pipe(s) => s.write_all(buf).await,
}
}
pub(crate) fn finish(&mut self) -> Result<(), Error> {
match self {
SendHalf::Quic(s) => s
.finish()
.map_err(|_| Error::Transport("stream already closed".into())),
SendHalf::Local(s) => s.finish(),
#[cfg(unix)]
SendHalf::Unix(s) => s.finish(),
#[cfg(windows)]
SendHalf::Pipe(s) => s.finish(),
}
}
pub(crate) fn reset(&mut self, code: u64) {
match self {
SendHalf::Quic(s) => {
let _ = s.reset(VarInt::from_u64(code).expect("application codes are small"));
}
SendHalf::Local(s) => s.reset(code),
#[cfg(unix)]
SendHalf::Unix(s) => s.reset(code),
#[cfg(windows)]
SendHalf::Pipe(s) => s.reset(code),
}
}
pub(crate) fn stopped(
&self,
) -> Pin<Box<dyn Future<Output = Result<Option<u64>, Error>> + Send + Sync>> {
match self {
SendHalf::Quic(s) => {
let stopped = s.stopped();
Box::pin(async move {
match stopped.await {
Ok(code) => Ok(code.map(VarInt::into_inner)),
Err(quinn::StoppedError::ConnectionLost(_)) => Err(Error::Indeterminate),
Err(quinn::StoppedError::ZeroRttRejected) => {
Err(Error::Transport("0-RTT data rejected by the peer".into()))
}
}
})
}
SendHalf::Local(s) => Box::pin(s.stopped()),
#[cfg(unix)]
SendHalf::Unix(s) => Box::pin(s.stopped()),
#[cfg(windows)]
SendHalf::Pipe(s) => Box::pin(s.stopped()),
}
}
}
impl RecvHalf {
pub(crate) async fn read(&mut self, buf: &mut [u8]) -> Result<Option<usize>, Error> {
match self {
RecvHalf::Quic(r) => r.read(buf).await.map_err(read_error),
RecvHalf::Local(r) => r.read(buf).await,
#[cfg(unix)]
RecvHalf::Unix(r) => r.read(buf).await,
#[cfg(windows)]
RecvHalf::Pipe(r) => r.read(buf).await,
}
}
pub(crate) async fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Error> {
match self {
RecvHalf::Quic(r) => r
.read_exact(buf)
.await
.map_err(|e| Error::Protocol(format!("truncated header: {e}"))),
RecvHalf::Local(r) => r.read_exact(buf).await,
#[cfg(unix)]
RecvHalf::Unix(r) => r.read_exact(buf).await,
#[cfg(windows)]
RecvHalf::Pipe(r) => r.read_exact(buf).await,
}
}
pub(crate) fn stop(&mut self, code: u64) {
match self {
RecvHalf::Quic(r) => {
let _ = r.stop(VarInt::from_u64(code).expect("application codes are small"));
}
RecvHalf::Local(r) => r.stop(code),
#[cfg(unix)]
RecvHalf::Unix(r) => r.stop(code),
#[cfg(windows)]
RecvHalf::Pipe(r) => r.stop(code),
}
}
}
impl AsyncWrite for SendHalf {
fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<std::io::Result<usize>> {
match self.get_mut() {
SendHalf::Quic(s) => AsyncWrite::poll_write(Pin::new(s), cx, buf),
SendHalf::Local(s) => match s.io_mut() {
Some(io) => Pin::new(io).poll_write(cx, buf),
None => Poll::Ready(Err(closed_io())),
},
#[cfg(unix)]
SendHalf::Unix(s) => match s.io_mut() {
Some(io) => Pin::new(io).poll_write(cx, buf),
None => Poll::Ready(Err(closed_io())),
},
#[cfg(windows)]
SendHalf::Pipe(s) => match s.io_mut() {
Some(io) => Pin::new(io).poll_write(cx, buf),
None => Poll::Ready(Err(closed_io())),
},
}
}
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
match self.get_mut() {
SendHalf::Quic(s) => AsyncWrite::poll_flush(Pin::new(s), cx),
SendHalf::Local(s) => match s.io_mut() {
Some(io) => Pin::new(io).poll_flush(cx),
None => Poll::Ready(Ok(())),
},
#[cfg(unix)]
SendHalf::Unix(s) => match s.io_mut() {
Some(io) => Pin::new(io).poll_flush(cx),
None => Poll::Ready(Ok(())),
},
#[cfg(windows)]
SendHalf::Pipe(s) => match s.io_mut() {
Some(io) => Pin::new(io).poll_flush(cx),
None => Poll::Ready(Ok(())),
},
}
}
fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
match self.get_mut() {
SendHalf::Quic(s) => AsyncWrite::poll_shutdown(Pin::new(s), cx),
SendHalf::Local(s) => match s.io_mut() {
Some(io) => Pin::new(io).poll_shutdown(cx),
None => Poll::Ready(Ok(())),
},
#[cfg(unix)]
SendHalf::Unix(s) => match s.io_mut() {
Some(io) => Pin::new(io).poll_shutdown(cx),
None => Poll::Ready(Ok(())),
},
#[cfg(windows)]
SendHalf::Pipe(s) => match s.io_mut() {
Some(io) => Pin::new(io).poll_shutdown(cx),
None => Poll::Ready(Ok(())),
},
}
}
}
impl AsyncRead for RecvHalf {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut ReadBuf<'_>,
) -> Poll<std::io::Result<()>> {
match self.get_mut() {
RecvHalf::Quic(r) => AsyncRead::poll_read(Pin::new(r), cx, buf),
RecvHalf::Local(r) => {
let reset = r.reset_code();
match r.io_mut() {
Some(io) => match Pin::new(io).poll_read(cx, buf) {
Poll::Ready(Ok(())) if buf.filled().is_empty() && reset.is_some() => {
Poll::Ready(Err(std::io::Error::other("stream reset by the peer")))
}
other => other,
},
None => Poll::Ready(Ok(())),
}
}
#[cfg(unix)]
RecvHalf::Unix(r) => match r.io_mut() {
Some(io) => AsyncRead::poll_read(Pin::new(io), cx, buf),
None => Poll::Ready(Ok(())),
},
#[cfg(windows)]
RecvHalf::Pipe(r) => match r.io_mut() {
Some(io) => AsyncRead::poll_read(Pin::new(io), cx, buf),
None => Poll::Ready(Ok(())),
},
}
}
}
fn closed_io() -> std::io::Error {
std::io::Error::new(std::io::ErrorKind::BrokenPipe, "stream already closed")
}