1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
use_prelude!();
use std::sync::{Arc, OnceLock};
use super::{presence::JsonObject, ConnectionType};
/// Contains information about a remote peer that has requested a connection.
///
/// Connection requests and their authorization are scoped to a specific Ditto
/// peer and connection type.
///
/// Information about a peer Ditto is currently trying to connect to. This struct is used by the
/// [`Presence::set_connection_request_handler()`] callback to decide whether the Ditto client
/// should connect to a given peer or not.
pub struct ConnectionRequest {
raw: Arc<repr_c::Box<ffi_sdk::FfiConnectionRequest>>,
// An optional map of peer-provided information, signed by the peer.
cached_peer_metadata: OnceLock<Arc<JsonObject>>,
// An optional map of identity service-provided information, signed by the identity
// service.
cached_identity_service_metadata: OnceLock<Arc<JsonObject>>,
// The connecting peer's (pub) key (can be empty for legacy peers).
cached_peer_key: OnceLock<Arc<str>>,
}
impl ConnectionRequest {
pub(crate) fn new(raw: repr_c::Box<ffi_sdk::FfiConnectionRequest>) -> Self {
Self {
raw: Arc::new(raw),
cached_peer_metadata: <_>::default(),
cached_identity_service_metadata: <_>::default(),
cached_peer_key: <_>::default(),
}
}
pub(crate) fn raw(&self) -> Arc<repr_c::Box<ffi_sdk::FfiConnectionRequest>> {
self.raw.retain()
}
/// JSON-encoded metadata associated with the remote peer.
///
/// JSON string representing an empty dictionary if the remote peer has not
/// set any metadata or when this request is for a WebSocket connection.
///
/// Set peer metadata for the local peer using
/// [`Presence::set_peer_metadata()`] or
/// [`Presence::set_peer_metadata_json_str()`].
pub fn peer_metadata_json_str(&self) -> String {
String::from_utf8(
ffi_sdk::dittoffi_connection_request_peer_metadata_json(&*self.raw).to_vec(),
)
.expect("UTF-8")
}
/// [`DeserializeOwned`] convenience wrapper around [`Self::peer_metadata_json_str()`].
pub fn peer_metadata_serde<T: DeserializeOwned>(&self) -> Result<T> {
let value = ::serde_json::from_str(&self.peer_metadata_json_str())?;
Ok(value)
}
/// Metadata associated with the remote peer.
///
/// Empty dictionary if the remote peer has not set any metadata, or
/// when this request is for a WebSocket connection (which does not
/// currently support peer metadata).
///
/// Set peer metadata for the local peer using
/// [`Presence::set_peer_metadata()`] or
/// [`Presence::set_peer_metadata_json_str()`].
///
/// Convenience property that wraps [`Self::peer_metadata_json_str()`].
pub fn peer_metadata(&self) -> Arc<JsonObject> {
self.cached_peer_metadata
.get_or_init(|| {
Arc::new(
self.peer_metadata_serde()
.expect("incorrect json from `dittoffi`"),
)
})
.retain()
}
/// JSON-encoded metadata for the remote peer that is provided by the
/// identity service.
///
/// Use an authentication webhook to set this value. See Ditto's online
/// documentation for more information on how to configure an authentication
/// webhook.
pub fn identity_service_metadata_json_str(&self) -> String {
String::from_utf8(
ffi_sdk::dittoffi_connection_request_identity_service_metadata_json(&*self.raw)
.to_vec(),
)
.expect("UTF-8")
}
/// [`DeserializeOwned`] convenience wrapper around
/// [`Self::identity_service_metadata_json_str()`].
pub fn identity_service_metadata_serde<T: DeserializeOwned>(&self) -> Result<T> {
let value = ::serde_json::from_str(&self.identity_service_metadata_json_str())?;
Ok(value)
}
/// Metadata for the remote peer that is provided by the identity service.
///
/// Use an authentication webhook to set this value. See Ditto's online
/// documentation for more information on how to configure an authentication
/// webhook.
///
/// Convenience property that wraps [`Self::identity_service_metadata_json_str()`].
pub fn identity_service_metadata(&self) -> Arc<JsonObject> {
self.cached_identity_service_metadata
.get_or_init(|| {
Arc::new(
self.identity_service_metadata_serde()
.expect("incorrect json from `dittoffi`"),
)
})
.retain()
}
/// The unique peer key of the remote peer.
///
/// - See also: [`crate::transport::Peer::peer_key_string`] for more information on peer keys.
pub fn peer_key_string(&self) -> Arc<str> {
self.cached_peer_key
.get_or_init(|| {
ffi_sdk::dittoffi_connection_request_peer_key_string(&*self.raw)
.to_str()
.into()
})
.retain()
}
/// The network transport of this connection request.
///
/// Expect to receive separate connection requests for each network
/// transport that connects the local and remote peer.
pub fn connection_type(&self) -> ConnectionType {
ConnectionType::from_ffi(::ffi_sdk::dittoffi_connection_request_connection_type(
&*self.raw,
))
}
}
#[derive(Debug)]
/// Private, to future-proof the API (_e.g._, imagine `Deny { reason: … }` or whatnot).
pub(crate) enum ConnectionRequestAuthorizationInner {
Allow { _future_fields: () },
Deny { _future_fields: () },
}
/// Indicates whether a connection request should be authorized.
#[derive(Debug)]
pub struct ConnectionRequestAuthorization(pub(crate) ConnectionRequestAuthorizationInner);
impl ConnectionRequestAuthorization {
#![allow(non_upper_case_globals)]
/// The connection request will be allowed.
pub const Allow: Self = Self(ConnectionRequestAuthorizationInner::Allow { _future_fields: () });
/// The connection request will be denied.
pub const Deny: Self = Self(ConnectionRequestAuthorizationInner::Deny { _future_fields: () });
pub(crate) fn to_ffi(self, raw: &ffi_sdk::FfiConnectionRequest) {
let ffi_authorization = match self.0 {
ConnectionRequestAuthorizationInner::Allow { _future_fields: () } => {
ffi_sdk::ConnectionRequestAuthorization::Allow
}
ConnectionRequestAuthorizationInner::Deny { _future_fields: () } => {
ffi_sdk::ConnectionRequestAuthorization::Deny
}
};
ffi_sdk::dittoffi_connection_request_authorize(&raw, ffi_authorization);
}
}