use crate::{
asynchronous::{session::style::SessionStyle, stream::Stream},
error::{Error, ProtocolError},
options::SessionOptions,
proto::session::SessionController,
};
use tokio::{
io::{AsyncReadExt, AsyncWriteExt, Interest},
net::TcpStream,
};
use tokio_util::compat::{TokioAsyncReadCompatExt, TokioAsyncWriteCompatExt};
pub mod style;
pub struct Session<S> {
controller: SessionController,
options: SessionOptions,
context: S,
}
impl<S: SessionStyle> Session<S> {
pub async fn new(options: SessionOptions) -> crate::Result<Self> {
let mut controller = SessionController::new(options.clone())?;
let mut context = S::new(options.clone()).await?;
let command = controller.handshake_session()?;
context.write_command(&command).await?;
let response = context.read_command().await?;
controller.handle_response(&response)?;
let command = controller.create_session(context.create_session())?;
context.write_command(&command).await?;
let response = context.read_command().await?;
controller.handle_response(&response)?;
Ok(Self {
controller,
options,
context,
})
}
pub fn destination(&self) -> &str {
self.controller.destination()
}
}
impl Session<style::Stream> {
pub async fn connect(&mut self, destination: &str) -> crate::Result<Stream> {
let mut stream =
TcpStream::connect(format!("127.0.0.1:{}", self.options.samv3_tcp_port)).await?;
let command = self.controller.handshake_stream()?;
stream.write_all(&command).await?;
let (mut stream, response) = read_response!(stream);
self.controller.handle_response(&response)?;
let command = self.controller.create_stream(&destination)?;
stream.write_all(&command).await?;
let (stream, response) = read_response!(stream);
self.controller.handle_response(&response)?;
let compat = TokioAsyncReadCompatExt::compat(stream).into_inner();
let stream = TokioAsyncWriteCompatExt::compat_write(compat);
Ok(Stream::from_stream(stream, destination.to_string()))
}
pub async fn accept(&mut self) -> crate::Result<Stream> {
let mut stream =
TcpStream::connect(format!("127.0.0.1:{}", self.options.samv3_tcp_port)).await?;
let command = self.controller.handshake_stream()?;
stream.write_all(&command).await?;
let (mut stream, response) = read_response!(stream);
self.controller.handle_response(&response)?;
let command = self.controller.accept_stream()?;
stream.write_all(&command).await?;
let (mut stream, response) = read_response!(stream);
self.controller.handle_response(&response)?;
let response = {
let mut response = [0u8; 1024];
let destination = loop {
let ready = stream.ready(Interest::READABLE).await?;
if ready.is_readable() {
let nread = stream.peek(&mut response).await?;
if let Some(newline) = response[..nread].iter().position(|c| c == &b'\n') {
let _ = stream.read_exact(&mut response[..newline + 1]).await?;
break std::str::from_utf8(&response[..newline])
.map_err(|_| Error::Protocol(ProtocolError::InvalidMessage))?
.to_string();
}
}
};
destination
};
let compat = TokioAsyncReadCompatExt::compat(stream).into_inner();
let stream = TokioAsyncWriteCompatExt::compat_write(compat);
Ok(Stream::from_stream(stream, response.to_string()))
}
pub async fn forward(&mut self, port: u16) -> crate::Result<()> {
let mut stream =
TcpStream::connect(format!("127.0.0.1:{}", self.options.samv3_tcp_port)).await?;
let command = self.controller.handshake_stream()?;
stream.write_all(&command).await?;
let (mut stream, response) = read_response!(stream);
self.controller.handle_response(&response)?;
let command = self.controller.forward_stream(port)?;
stream.write_all(&command).await?;
let (stream, response) = read_response!(stream);
self.controller.handle_response(&response)?;
style::Stream::store_forwarded(&mut self.context, stream);
Ok(())
}
}
impl Session<style::Repliable> {
pub async fn send_to(&mut self, buf: &[u8], destination: &str) -> crate::Result<()> {
style::Repliable::send_to(&mut self.context, buf, destination).await
}
pub async fn recv_from(&mut self, buf: &mut [u8]) -> crate::Result<(usize, String)> {
style::Repliable::recv_from(&mut self.context, buf).await
}
}
impl Session<style::Anonymous> {
pub async fn send_to(&mut self, buf: &[u8], destination: &str) -> crate::Result<()> {
style::Anonymous::send_to(&mut self.context, buf, destination).await
}
pub async fn recv(&mut self, buf: &mut [u8]) -> crate::Result<usize> {
style::Anonymous::recv(&mut self.context, buf).await
}
}