Skip to content

Commit c7fb1ce

Browse files
committed
Add doccumentation for auto_reconnect, lazy_auto_reconnect and SetTarget
1 parent 51a38a6 commit c7fb1ce

File tree

1 file changed

+104
-0
lines changed

1 file changed

+104
-0
lines changed

capnp-rpc/src/reconnect.rs

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,46 @@ use capnp::capability::{FromClientHook, Promise};
66
use capnp::private::capability::{ClientHook, RequestHook};
77
use futures::TryFutureExt;
88

9+
/// Trait implemented by the reconnecting client to set new connection out-of-band.
10+
///
11+
/// When using [`auto_reconnect`] or [`lazy_auto_reconnect`] it is not always optimal
12+
/// to wait for a call to fail with [`Disconnected`](capnp::ErrorKind::Disconnected)
13+
/// before replacing the client that is wrapped with a new fresh one.
14+
///
15+
/// Sometimes we know by other means that a client has gone away. It could be that we
16+
/// have clients that automatically sends us a new capability when it reconnects to us.
17+
///
18+
/// For these situations you can use the implementation of this trait that you get from
19+
/// [`auto_reconnect`] or [`lazy_auto_reconnect`] to manually set the target of the
20+
/// wrapped client.
21+
///
22+
/// # Example
23+
///
24+
/// ```ignore
25+
/// // The reconnecting client that automatically calls connect
26+
/// let (foo_client, set_target) = auto_reconnect(|| {
27+
/// Ok(new_future_client(connect()))
28+
/// })?;
29+
///
30+
/// // do work with foo_client
31+
/// ...
32+
///
33+
/// // We become aware that the client has gone so reconnect manually
34+
/// set_target.set_target(new_future_client(connect()));
35+
///
36+
/// // do more work with foo_client
37+
/// ...
38+
/// ```
939
pub trait SetTarget<C> {
40+
/// Adds a new reference to this implementation of SetTarget.
41+
///
42+
/// This is mostly to get around that `Clone` requires `Sized` and so you need this
43+
/// trick to get a copy of the `Box<dyn SetTarget<C>>` you got from making the
44+
/// reconneting client.
1045
fn add_ref(&self) -> Box<dyn SetTarget<C>>;
46+
47+
/// Sets the target client of the reconecting client that this trait implementation is
48+
/// for.
1149
fn set_target(&self, target: C);
1250
}
1351

@@ -218,6 +256,65 @@ where
218256
}
219257
}
220258

259+
/// Creates a new client that reconnects when getting [`ErrorKind::Disconnected`](capnp::ErrorKind::Disconnected) errors.
260+
///
261+
/// Usually when you get a [`Disconnected`](capnp::ErrorKind::Disconnected) error respoonse from calling a method on a capability
262+
/// it means the end of that capability for good. And so you can't call methods on that
263+
/// capability any more.
264+
///
265+
/// When you have a way of getting the capability back: Be it from a bootstrap or because
266+
/// the capability is persistent this method can help you wrap that reconnnection logic into a client
267+
/// that automatically runs the logic whenever a method call returns [`Disconnected`](capnp::ErrorKind::Disconnected).
268+
///
269+
/// The way it works is that you provide a closure that returns a fresh client or a permanent error and
270+
/// you get a new conected client and a [`SetTarget`] interface that you can optionally use to prematurely
271+
/// replace the client.
272+
///
273+
/// There is one caveat though: The original request that got a [`Disconnected`](capnp::ErrorKind::Disconnected)
274+
/// will still get that response. It is up to the caller to retry the call if relevant. `auto_reconnect`` only
275+
/// deals with the calls that come after.
276+
///
277+
/// # Example
278+
///
279+
/// ```capnp
280+
/// # Cap'n Proto schema
281+
/// interface Foo {
282+
/// identity @0 (x: UInt32) -> (y: UInt32);
283+
/// }
284+
/// ```
285+
///
286+
/// ```ignore
287+
/// // A simple bootstrapped tcp connection to remote.example.com
288+
/// async fn connect() -> capnp::Result<foo_client::Client> {
289+
/// let stream = tokio::net::TcpStream::connect(&"remote.example.com:3001").await?;
290+
/// stream.set_nodelay(true)?;
291+
/// let (reader, writer) = tokio_util::compat::TokioAsyncReadCompatExt::compat(stream).split();
292+
///
293+
/// let network = Box::new(twoparty::VatNetwork::new(
294+
/// futures::io::BufReader::new(reader),
295+
/// futures::io::BufWriter::new(writer),
296+
/// rpc_twoparty_capnp::Side::Client,
297+
/// Default::default(),
298+
/// ));
299+
///
300+
/// let mut rpc_system = RpcSystem::new(network, None);
301+
/// let foo_client: foo_client::Client = rpc_system.bootstrap(rpc_twoparty_capnp::Side::Server);
302+
/// tokio::task::spawn_local(rpc_system);
303+
/// Ok(foo_client)
304+
/// }
305+
/// // The reconnecting client that automatically calls connect
306+
/// let (foo_client, _) = auto_reconnect(|| {
307+
/// // By using new_future_client we delay any calls until we have a new connection.
308+
/// Ok(new_future_client(connect()))
309+
/// })?;
310+
/// // Calling Foo like normally.
311+
/// let mut request = foo_client.identity_request();
312+
/// request.get().set_x(123);
313+
/// let promise = request.send().promise.and_then(|response| {
314+
/// println!("results = {}", response.get()?.get_y());
315+
/// Ok(())
316+
/// });
317+
/// ```
221318
pub fn auto_reconnect<F, C>(mut connect: F) -> capnp::Result<(C, Box<dyn SetTarget<C>>)>
222319
where
223320
F: FnMut() -> capnp::Result<C>,
@@ -232,6 +329,13 @@ where
232329
Ok((FromClientHook::new(hook), Box::new(c)))
233330
}
234331

332+
/// Creates a new client that lazily connect and also reconnects when getting [`ErrorKind::Disconnected`](capnp::ErrorKind::Disconnected) errors.
333+
///
334+
/// For explanation of how this functions see: [`auto_reconnect`]
335+
///
336+
/// The main difference between [`auto_reconnect`] and this function is that while [`auto_reconnect`] will call
337+
/// the closure immediatly to get an inner client to wrap, this function starts out disconnected and only calls
338+
/// the closure to get the actual client when the capability is first used.
235339
pub fn lazy_auto_reconnect<F, C>(connect: F) -> (C, Box<dyn SetTarget<C>>)
236340
where
237341
F: FnMut() -> capnp::Result<C>,

0 commit comments

Comments
 (0)