use mio::net::TcpStream;
use crate::{TCP_READ_BUFFER_CAPACITY, TCP_WRITE_BUFFER_CAPACITY, logging::*};
pub trait Transport: AsMut<TcpStream> + Sized + Send {
type Lines<'a>: Iterator<Item = bytes::Bytes>
where
Self: 'a;
fn connect(addr: std::net::SocketAddr) -> std::io::Result<Self>;
fn read_lines<'a>(&'a mut self) -> std::io::Result<Self::Lines<'a>>;
fn read_line_sync(&mut self) -> std::io::Result<String>;
fn write_with<R>(&mut self, write: impl FnOnce(&mut bytes::BytesMut) -> R) -> R;
fn write_line(&mut self, line: impl AsRef<[u8]>);
fn has_buffered_writes(&self) -> bool;
fn flush_writes(&mut self) -> std::io::Result<usize>;
}
pub struct SonicStream {
stream: TcpStream,
write_buf: bytes::BytesMut,
read_buf: bytes::BytesMut,
}
impl Transport for SonicStream {
type Lines<'a> = Lines<'a>;
fn connect(addr: std::net::SocketAddr) -> std::io::Result<Self> {
TcpStream::connect(addr).map(|stream| Self {
stream,
write_buf: bytes::BytesMut::with_capacity(TCP_WRITE_BUFFER_CAPACITY),
read_buf: bytes::BytesMut::with_capacity(TCP_READ_BUFFER_CAPACITY),
})
}
fn read_lines<'a>(&'a mut self) -> std::io::Result<Lines<'a>> {
use std::io::Read as _;
let len_before = self.read_buf.len();
'read_stream: loop {
let mut tmp = [0u8; 4096];
match self.stream.read(&mut tmp) {
Ok(0) => {
log_trace!("Read empty chunk");
break 'read_stream;
}
Ok(n) => {
log_trace!("Read {n} bytes chunk");
self.read_buf.extend_from_slice(&tmp[..n]);
}
Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => {
break 'read_stream;
}
Err(e) => return Err(e),
}
}
log_debug!("Read {n} bytes", n = self.read_buf.len() - len_before);
Ok(Lines::new(&mut self.read_buf))
}
fn read_line_sync(&mut self) -> std::io::Result<String> {
use std::io::{BufRead as _, BufReader};
let mut buf_reader = BufReader::new(&self.stream);
let mut response = String::new();
loop {
match buf_reader.read_line(&mut response) {
Ok(_read) => {
let new_len = response.trim_ascii_end().len();
response.truncate(new_len);
return Ok(response);
}
Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => {
continue;
}
Err(e) => return Err(e),
};
}
}
fn write_with<R>(&mut self, write: impl FnOnce(&mut bytes::BytesMut) -> R) -> R {
use bytes::BufMut as _;
let res = write(&mut self.write_buf);
self.write_buf.put_slice(b"\r\n");
res
}
fn write_line(&mut self, line: impl AsRef<[u8]>) {
use bytes::BufMut as _;
self.write_buf.put_slice(line.as_ref());
log_trace!("Wrote {} bytes line", line.as_ref().len());
self.write_buf.put_slice(b"\r\n");
log_trace!("Wrote \\r\\n");
}
#[inline]
fn has_buffered_writes(&self) -> bool {
!self.write_buf.is_empty()
}
fn flush_writes(&mut self) -> std::io::Result<usize> {
use std::io::Write as _;
if self.write_buf.is_empty() {
return Ok(0);
}
match self.stream.write_all(&self.write_buf) {
Ok(()) => {
let written = self.write_buf.len();
self.write_buf.clear();
Ok(written)
}
Err(err) => Err(err),
}
}
}
impl AsMut<TcpStream> for SonicStream {
#[inline]
fn as_mut(&mut self) -> &mut TcpStream {
&mut self.stream
}
}
pub struct Lines<'a> {
bytes: &'a mut bytes::BytesMut,
processed_lines: usize,
}
impl<'a> Lines<'a> {
fn new(buf: &'a mut bytes::BytesMut) -> Self {
Self {
bytes: buf,
processed_lines: 0,
}
}
}
impl<'a> Iterator for Lines<'a> {
type Item = bytes::Bytes;
fn next(&mut self) -> Option<Self::Item> {
use bytes::Buf as _;
if let Some(pos) = self.bytes.iter().position(|&b| b == b'\n') {
self.processed_lines += 1;
let mut line_bytes = self.bytes.split_to(pos);
self.bytes.advance(1);
if line_bytes.ends_with(b"\r") {
line_bytes.truncate(line_bytes.len() - 1);
};
Some(bytes::Bytes::from(line_bytes))
} else {
None
}
}
}
impl<'a> Drop for Lines<'a> {
fn drop(&mut self) {
if self.processed_lines > 1 {
log_debug!("Processed {} lines at once", self.processed_lines);
}
}
}