use crate::io;
use crate::url::Url;
use anyhow::bail;
use async_std::io::prelude::ReadExt;
use async_std::io::prelude::WriteExt;
use async_std::io::Read;
use async_std::io::Write;
use async_std::net::TcpStream;
use async_tls::TlsConnector;
pub struct RawConnection {
wbuf: Vec<u8>,
rbuf: Vec<u8>,
stream: Box<dyn ReadWrite + Unpin>,
}
trait ReadWrite: Read + Write + Send {}
impl<T: Read + Write + Send> ReadWrite for T {}
impl RawConnection {
pub async fn open(url: &str) -> crate::Result<Self> {
let url = Url::parse(url)?;
let tcp_stream = TcpStream::connect((url.host(), url.port())).await?;
let stream = if url.scheme() == "rediss" || url.scheme() == "redis+tls" {
let connector = TlsConnector::default();
let handshake = connector.connect(url.host(), tcp_stream)?;
let enc_stream = handshake.await?;
Box::new(enc_stream) as Box<dyn ReadWrite + Unpin>
} else {
Box::new(tcp_stream) as Box<dyn ReadWrite + Unpin>
};
let mut conn = Self {
stream,
wbuf: Vec::with_capacity(4096),
rbuf: vec![0; 4096],
};
if url.database() != "0" {
conn.send(&["SELECT", url.database()]).await?;
conn.receive().await?;
}
if let Some(password) = url.password() {
conn.send(&["AUTH", password]).await?;
conn.receive().await?;
}
Ok(conn)
}
pub(crate) async fn send(&mut self, command: &[&str]) -> crate::Result<()> {
self.wbuf.clear();
log::trace!("send: {:?}", command);
io::write_array_start(&mut self.wbuf, command.len());
for arg in command {
io::write_str(&mut self.wbuf, arg);
}
self.stream.write_all(&self.wbuf).await?;
self.stream.flush().await?;
Ok(())
}
pub(crate) async fn receive(&mut self) -> crate::Result<&[u8]> {
let n = self.stream.read(&mut self.rbuf).await?;
if n == 0 {
bail!("server closed the connection (EOF)");
}
log::trace!("recv: {:?}", ::std::str::from_utf8(&self.rbuf[..n])?);
Ok(&self.rbuf[..n])
}
}