pub struct Presence { /* private fields */ }
Expand description

Presence to visualize the mesh.

Even if Ditto works as a standalone, other peers are needed to exploit it at its maximum extend : syncing data and merging it.

The Presence struct allows you to get and monitor known peers. The peers are regrouped inside a PresenceGraph.

You can get Presence using the Ditto::presence method. To have an immediate graph of peers, use Presence::graph:

let presence_graph = ditto.presence().graph();

To monitor peers, use Presence::observe to call a callback each time peers are updated:

let handle = ditto.presence().observe(|presence_graph| {
    // Do something with peers
});

Implementations§

source§

impl Presence

source

pub fn observe( self: &Arc<Self>, callback: impl Fn(&PresenceGraph) + Send + Sync + 'static ) -> PresenceObserver

Allow to call a callback each time there is a change in known peers. The returned PresenceObserver must be kept in scope to keep receiving updates.

source

pub fn graph(&self) -> PresenceGraph

Return an immediate representation of known peers

source§

impl Presence

source

pub fn set_peer_metadata( &self, peer_metadata: &impl Serialize ) -> Result<(), DittoError>

Set a dictionary of arbitrary data about this device to be shared with peers in the mesh.

This data is gossiped in the presence collection across the mesh. This can be useful to include extra metadata like app versions,capabilities, etc., to help peers decide who to interact with.

This peer information is persisted in the SDK, and thus needn’t be set at every start of Ditto.

Security (and caveats)

This peer info will be signed by your peer key to prevent forgery of this info by other peers.

However, for compatibility, there is no attestation of the lack of peer info – that is, participants in the mesh could maliciously remove peer info. If this is a concern for your application, a workaround for this is to have your application require that peers have a signed peer info dictionary present.

Similarly, as there is no monotonic version counter or timestamp/expiration of the signed peer info, replay attacks (replacing the current info with previously, possibly outdated, signed info) are possible without counter-measures. If this is a concern for your application, you might consider including a counter or creation timestamp to prevent replays, depending on your use-case.

When a peer is only connected via WebSocket, peer metadata is not available.

Performance caveats

Because this information is included in the presence data that is gossiped among peers, the size of this peer info and the frequency it is updated can drastically affect performance if it is too large.

Errors

Because of the performance implications, the serialized info dictionary is currently limited to 4KiB.

Examples
use dittolive_ditto::prelude::*;
use serde_json::json;
ditto.presence().set_peer_metadata(&json!({
    "app_version": "1.0.0",
}))?;
source

pub fn set_peer_metadata_json_str(&self, json: &str) -> Result<(), DittoError>

Set arbitrary metadata formatted as JSON to be associated with the current peer.

The metadata must not exceed 4 KB in size. Expects JSON.

source

pub fn peer_metadata_json_str(&self) -> String

Metadata associated with the current peer as JSON-encoded data.

Other peers in the same mesh can access this user-provided dictionary of metadata via the presence graph at Self::graph() and when evaluating connection requests using Self::set_connection_request_handler(). Use Self::set_peer_metadata() or Self::set_peer_metadata_json_str() to set this value.

source

pub fn peer_metadata_serde<T: DeserializeOwned>(&self) -> Result<T>

source

pub fn peer_metadata(&self) -> Arc<Map<String, Value>>

Metadata associated with the current peer.

Other peers in the same mesh can access this user-provided dictionary of metadata via the presence graph at Self::graph() and when evaluating connection requests using Self::set_connection_request_handler(). Use Self::set_peer_metadata() or Self::set_peer_metadata_json_str() to set this value.

This is a convenience property that wraps Self::peer_metadata_json_str().

source

pub fn set_connection_request_handler<F: IntoOption<impl 'static + Send + Sync + Fn(ConnectionRequest) -> ConnectionRequestAuthorization>>( &self, handler_or_none: F )

Set this handler to control which peers in a Ditto mesh can connect to the current peer.

Each peer in a Ditto mesh will attempt to connect to other peers that it can reach. By default, the mesh will try and establish connections that optimize for the best overall connectivity between peers. However, you can set this handler to assert some control over which peers you connect to.

If set, this handler is called for every incoming connection request from a remote peer and is passed the other peer’s peer_key, peer_metadata, and identity_service_metadata. The handler can then accept or reject the request by returning an according ConnectionRequestAuthorization value. When the connection request is rejected, the remote peer may retry the connection request after a short delay.

Connection request handlers must reliably respond to requests within a short time: if a handler takes too long to return, the connection request will fall back to being denied. The response –currently— times out after 10 seconds, but this exact value may be subject to change in future releases.

  • Note: the handler is called from a different thread (“background hook”).
  • See also: Self::peer_metadata()
Example
/// Let's imagine the app we are maintaining has a bug in `1.2.3`:
const BUGGY_VERSION: &str = "1.2.3";

// We avoid problems in updated versions of our app with these ones by
// rejecting connections to them, like so:
ditto
    .presence()
    .set_connection_request_handler(|connection_request: ConnectionRequest| {
        match connection_request
            .peer_metadata()
            .get("app_version")
            .and_then(|it| it.as_str())
        {
            // Reject peers reporting a known buggy version or reporting no
            // version at all.
            Some(BUGGY_VERSION) | None => return ConnectionRequestAuthorization::Deny,
            Some(_non_buggy_version) => { /* no reason to reject here */ }
        }
        // Potentially other checks/reasons to reject…

        // Eventually:
        ConnectionRequestAuthorization::Allow
    });

// You can also unset the `connection_request_handler` by setting it to `None`.
// This uses the default handler, which accepts all requests.
ditto.presence().set_connection_request_handler(None);
source

pub fn set_connection_request_handler_async<ConnectionRequestAuthorizationFut>( &self, async_callback: impl 'static + Send + Sync + Fn(ConnectionRequest) -> ConnectionRequestAuthorizationFut )where ConnectionRequestAuthorizationFut: 'static + Send + Future<Output = ConnectionRequestAuthorization>,

Convenience around Self::set_connection_request_handler() that allows the callback to be async.

Not responding in time will lead to a handshake timeout, effectively rejecting the peer.

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<T> CompatExt for T

§

fn compat(self) -> Compat<T>

Applies the [Compat] adapter by value. Read more
§

fn compat_ref(&self) -> Compat<&T>

Applies the [Compat] adapter by shared reference. Read more
§

fn compat_mut(&mut self) -> Compat<&mut T>

Applies the [Compat] adapter by mutable reference. Read more
§

impl<T> FitForCBox for T

§

type CBoxWrapped = Box_<T>

source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T, U> Into<U> for Twhere U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

§

impl<T> ManuallyDropMut for T

§

type Ret = ManuallyDrop<T>

§

fn manually_drop_mut<'__>(&'__ mut self) -> &'__ mut ManuallyDrop<T>

§

impl<T> To for Twhere T: ?Sized,

§

fn to<T>(self) -> Twhere Self: Into<T>,

Converts to T by calling Into<T>::into.
§

fn try_to<T>(self) -> Result<T, Self::Error>where Self: TryInto<T>,

Tries to convert to T by calling TryInto<T>::try_into.
source§

impl<T, U> TryFrom<U> for Twhere U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for Twhere V: MultiLane<T>,

§

fn vzip(self) -> V