Traits for defining I/O objects which automatically reconnect upon failure.
This project is similar in scope to
stubborn-io, but aims to
leverage the recently stabilized async fn
in traits, to make the
implementation of reconnecting simpler for the end user.
To get started, add io-tether
to your list of dependencies
io-tether = { version = "0.1.3" }
Then in most cases, it is expected that the consumer of this library will want
to implement TetherResolver
on their own types. This allows them to inject
arbitrary asynchronous code just before the I/O attempts to reconnect.
use io_tether::{TetherResolver, Context, State, Tether};
use tokio::{net::TcpStream, io::{AsyncReadExt, AsyncWriteExt}, sync::mpsc};
/// Custom resolver
pub struct CallbackResolver {
channel: mpsc::Sender<String>,
}
impl TetherResolver for CallbackResolver {
type Error = std::io::Error;
async fn disconnected(
&mut self,
context: &Context,
state: &State<Self::Error>,
) -> bool {
match state {
State::Eof => false, // No reconnection attempt will be made
State::Err(error) => {
let error = error.to_string();
self.channel.send(error).await.unwrap();
true
}
}
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut buf = Vec::new();
let (channel, rx) = mpsc::channel(10);
let listener = tokio::net::TcpListener::bind("localhost:8080").await?;
tokio::spawn(async move {
loop {
let (mut stream, _addr) = listener.accept().await.unwrap();
stream.write_all(b"foo-bar").await.unwrap();
stream.shutdown().await.unwrap();
}
});
let resolver = CallbackResolver {
channel,
};
let mut tether = Tether::<_, TcpStream, _>::connect("localhost:8080", resolver)
.await?;
tether.read_to_end(&mut buf).await?;
assert_eq!(&buf, b"foo-bar");
Ok(())
}
This project is still very much a work in progress. Expect fairly common breaking changes in the short term.