1
0

implement client-side connect and packet

This commit is contained in:
EAimTY 2023-01-26 19:05:16 +09:00
parent a4d178c95e
commit 101d4427eb
4 changed files with 184 additions and 12 deletions

View File

@ -3,6 +3,10 @@ name = "tuic-quinn"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
async-trait = { version = "0.1.62", default-features = false }
bytes = { version = "1.3.0", default-features = false, features = ["std"] }
futures-util = { version = "0.3.25", default-features = false, features = ["io", "std"] }
quinn = { version = "0.9.3", default-features = false, features = ["futures-io"] }
thiserror = { version = "1.0.38", default-features = false }
tuic = { path = "../tuic", default-features = false, features = ["model"] }

View File

@ -1,14 +1,159 @@
pub fn add(left: usize, right: usize) -> usize {
left + right
use self::{marshal::Marshal, side::Side};
use bytes::Bytes;
use futures_util::{io::Cursor, AsyncRead, AsyncWrite, AsyncWriteExt};
use quinn::{
Connection as QuinnConnection, ConnectionError, RecvStream, SendDatagramError, SendStream,
};
use std::{
io::Error as IoError,
pin::Pin,
task::{Context, Poll},
};
use thiserror::Error;
use tuic::{
model::{
side::{Rx, Tx},
Connect as ConnectModel, Connection as ConnectionModel,
},
protocol::{Address, Header},
};
mod marshal;
mod unmarshal;
pub mod side {
pub struct Client;
pub struct Server;
pub(super) enum Side<C, S> {
Client(C),
Server(S),
}
}
#[cfg(test)]
mod tests {
use super::*;
pub struct Connection<'conn, Side> {
conn: &'conn QuinnConnection,
model: ConnectionModel<Bytes>,
_marker: Side,
}
#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
impl<'conn> Connection<'conn, side::Client> {
pub fn new(conn: &'conn QuinnConnection) -> Self {
Self {
conn,
model: ConnectionModel::new(),
_marker: side::Client,
}
}
pub async fn connect(&self, addr: Address) -> Result<Connect, Error> {
let (mut send, recv) = self.conn.open_bi().await?;
let model = self.model.send_connect(addr);
model.header().marshal(&mut send).await?;
Ok(Connect::new(Side::Client(model), send, recv))
}
pub async fn packet_native(
&self,
pkt: impl AsRef<[u8]>,
assoc_id: u16,
addr: Address,
) -> Result<(), Error> {
let Some(max_pkt_size) = self.conn.max_datagram_size() else {
return Err(Error::SendDatagram(SendDatagramError::Disabled));
};
let model = self.model.send_packet(assoc_id, addr, max_pkt_size);
for (header, frag) in model.into_fragments(pkt) {
let mut buf = Cursor::new(vec![0; header.len() + frag.len()]);
header.marshal(&mut buf).await?;
buf.write_all(frag).await.unwrap();
self.conn.send_datagram(Bytes::from(buf.into_inner()))?;
}
Ok(())
}
pub async fn packet_quic(
&self,
pkt: impl AsRef<[u8]>,
assoc_id: u16,
addr: Address,
) -> Result<(), Error> {
let model = self.model.send_packet(assoc_id, addr, u16::MAX as usize);
let mut frags = model.into_fragments(pkt);
let (header, frag) = frags.next().unwrap();
assert!(frags.next().is_none());
let mut send = self.conn.open_uni().await?;
header.marshal(&mut send).await?;
AsyncWriteExt::write_all(&mut send, frag).await?;
Ok(())
}
}
pub struct Connect {
model: Side<ConnectModel<Tx>, ConnectModel<Rx>>,
send: SendStream,
recv: RecvStream,
}
impl Connect {
fn new(
model: Side<ConnectModel<Tx>, ConnectModel<Rx>>,
send: SendStream,
recv: RecvStream,
) -> Self {
Self { model, send, recv }
}
pub fn addr(&self) -> &Address {
match &self.model {
Side::Client(model) => {
let Header::Connect(connect) = model.header() else { unreachable!() };
&connect.addr()
}
Side::Server(model) => model.addr(),
}
}
}
impl AsyncRead for Connect {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut [u8],
) -> Poll<Result<usize, IoError>> {
AsyncRead::poll_read(Pin::new(&mut self.get_mut().recv), cx, buf)
}
}
impl AsyncWrite for Connect {
fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<Result<usize, IoError>> {
AsyncWrite::poll_write(Pin::new(&mut self.get_mut().send), cx, buf)
}
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), IoError>> {
AsyncWrite::poll_flush(Pin::new(&mut self.get_mut().send), cx)
}
fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), IoError>> {
AsyncWrite::poll_close(Pin::new(&mut self.get_mut().send), cx)
}
}
#[derive(Debug, Error)]
pub enum Error {
#[error(transparent)]
Io(#[from] IoError),
#[error(transparent)]
Connection(#[from] ConnectionError),
#[error(transparent)]
SendDatagram(#[from] SendDatagramError),
}

16
tuic-quinn/src/marshal.rs Normal file
View File

@ -0,0 +1,16 @@
use async_trait::async_trait;
use futures_util::AsyncWrite;
use std::io::Error as IoError;
use tuic::protocol::Header;
#[async_trait]
pub(super) trait Marshal {
async fn marshal(&self, s: &mut impl AsyncWrite) -> Result<(), IoError>;
}
#[async_trait]
impl Marshal for Header {
async fn marshal(&self, s: &mut impl AsyncWrite) -> Result<(), IoError> {
todo!()
}
}

View File

@ -0,0 +1,7 @@
use async_trait::async_trait;
use futures_util::AsyncRead;
#[async_trait]
trait Unmarshal {
fn unmarshal(&self, s: &mut impl AsyncRead) -> Result<(), ()>;
}