pub mod tcp {
use std::io::{self, Read, Write};
use std::net::{SocketAddr, TcpStream as StdTcpStream, ToSocketAddrs};
use std::time::{Duration, Instant};
#[derive(Debug)]
pub struct TcpStream {
inner: StdTcpStream,
}
pub fn connect(host: &str, port: u16) -> io::Result<TcpStream> {
StdTcpStream::connect((host, port)).map(|inner| TcpStream { inner })
}
impl TcpStream {
pub fn connect_to<A: ToSocketAddrs>(addr: A) -> io::Result<Self> {
StdTcpStream::connect(addr).map(|inner| TcpStream { inner })
}
pub fn connect_timeout(host: &str, port: u16, timeout: Duration) -> io::Result<TcpStream> {
let deadline = Instant::now() + timeout;
let mut last_err = None;
for addr in (host, port).to_socket_addrs()? {
let remaining = deadline.saturating_duration_since(Instant::now());
if remaining.is_zero() {
break;
}
match StdTcpStream::connect_timeout(&addr, remaining) {
Ok(inner) => return Ok(TcpStream { inner }),
Err(e) => last_err = Some(e),
}
}
Err(last_err.unwrap_or_else(|| {
io::Error::new(io::ErrorKind::InvalidInput, "no address resolved for host")
}))
}
pub fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
self.inner.set_read_timeout(dur)
}
pub fn set_write_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
self.inner.set_write_timeout(dur)
}
pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> {
self.inner.set_nodelay(nodelay)
}
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
self.inner.peer_addr()
}
pub fn local_addr(&self) -> io::Result<SocketAddr> {
self.inner.local_addr()
}
pub fn get_ref(&self) -> &StdTcpStream {
&self.inner
}
pub fn get_mut(&mut self) -> &mut StdTcpStream {
&mut self.inner
}
pub fn into_std(self) -> StdTcpStream {
self.inner
}
}
impl From<StdTcpStream> for TcpStream {
fn from(inner: StdTcpStream) -> Self {
TcpStream { inner }
}
}
impl Read for TcpStream {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.inner.read(buf)
}
}
impl Write for TcpStream {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.inner.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.inner.flush()
}
}
impl Read for &TcpStream {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
(&self.inner).read(buf)
}
}
impl Write for &TcpStream {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
(&self.inner).write(buf)
}
fn flush(&mut self) -> io::Result<()> {
(&self.inner).flush()
}
}
}
#[cfg(test)]
mod tests {
use super::tcp;
use std::io::{Read, Write};
use std::net::TcpListener;
#[test]
fn connect_read_write_roundtrip() {
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
let addr = listener.local_addr().unwrap();
let server = std::thread::spawn(move || {
let (mut s, _) = listener.accept().unwrap();
let mut buf = [0u8; 5];
s.read_exact(&mut buf).unwrap();
s.write_all(&buf).unwrap(); });
let mut conn = tcp::connect("127.0.0.1", addr.port()).expect("connect");
conn.write_all(b"hello").unwrap();
let mut back = [0u8; 5];
conn.read_exact(&mut back).unwrap();
assert_eq!(&back, b"hello");
assert_eq!(conn.peer_addr().unwrap().port(), addr.port());
server.join().unwrap();
}
#[test]
fn connect_refused_is_an_io_error() {
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
let port = listener.local_addr().unwrap().port();
drop(listener); assert!(tcp::connect("127.0.0.1", port).is_err());
}
}