chore: Refactor ServerCapabilities into ServerInfo.
It has nothing to do with /capabilities so is confusing. We can use this new struct to combine the well-known response into a single cache too.
This commit is contained in:
@@ -725,11 +725,11 @@ impl Client {
|
||||
|
||||
/// Empty the server version and unstable features cache.
|
||||
///
|
||||
/// Since the SDK caches server capabilities (versions and unstable
|
||||
/// features), it's possible to have a stale entry in the cache. This
|
||||
/// functions makes it possible to force reset it.
|
||||
pub async fn reset_server_capabilities(&self) -> Result<(), ClientError> {
|
||||
Ok(self.inner.reset_server_capabilities().await?)
|
||||
/// Since the SDK caches server info (versions, unstable features,
|
||||
/// well-known etc), it's possible to have a stale entry in the cache.
|
||||
/// This functions makes it possible to force reset it.
|
||||
pub async fn reset_server_info(&self) -> Result<(), ClientError> {
|
||||
Ok(self.inner.reset_server_info().await?)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ use serde_json::{json, value::Value as JsonValue};
|
||||
|
||||
use super::{
|
||||
send_queue::SentRequestKey, DependentQueuedRequestKind, DisplayName, DynStateStore,
|
||||
RoomLoadSettings, ServerCapabilities,
|
||||
RoomLoadSettings, ServerInfo,
|
||||
};
|
||||
use crate::{
|
||||
deserialized_responses::MemberEvent,
|
||||
@@ -90,8 +90,8 @@ pub trait StateStoreIntegrationTests {
|
||||
async fn test_send_queue_dependents(&self);
|
||||
/// Test an update to a send queue dependent request.
|
||||
async fn test_update_send_queue_dependent(&self);
|
||||
/// Test saving/restoring server capabilities.
|
||||
async fn test_server_capabilities_saving(&self);
|
||||
/// Test saving/restoring server info.
|
||||
async fn test_server_info_saving(&self);
|
||||
/// Test fetching room infos based on [`RoomLoadSettings`].
|
||||
async fn test_get_room_infos(&self);
|
||||
}
|
||||
@@ -472,34 +472,36 @@ impl StateStoreIntegrationTests for DynStateStore {
|
||||
);
|
||||
}
|
||||
|
||||
async fn test_server_capabilities_saving(&self) {
|
||||
async fn test_server_info_saving(&self) {
|
||||
let versions = &[MatrixVersion::V1_1, MatrixVersion::V1_2, MatrixVersion::V1_11];
|
||||
let server_caps = ServerCapabilities::new(
|
||||
versions,
|
||||
let server_info = ServerInfo::new(
|
||||
versions.iter().map(|version| version.to_string()).collect(),
|
||||
[("org.matrix.experimental".to_owned(), true)].into(),
|
||||
);
|
||||
|
||||
self.set_kv_data(
|
||||
StateStoreDataKey::ServerCapabilities,
|
||||
StateStoreDataValue::ServerCapabilities(server_caps.clone()),
|
||||
StateStoreDataKey::ServerInfo,
|
||||
StateStoreDataValue::ServerInfo(server_info.clone()),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_let!(
|
||||
Ok(Some(StateStoreDataValue::ServerCapabilities(stored_caps))) =
|
||||
self.get_kv_data(StateStoreDataKey::ServerCapabilities).await
|
||||
Ok(Some(StateStoreDataValue::ServerInfo(stored_info))) =
|
||||
self.get_kv_data(StateStoreDataKey::ServerInfo).await
|
||||
);
|
||||
assert_eq!(stored_caps, server_caps);
|
||||
assert_eq!(stored_info, server_info);
|
||||
|
||||
let (stored_versions, stored_features) = stored_caps.maybe_decode().unwrap();
|
||||
let decoded_server_info = stored_info.maybe_decode().unwrap();
|
||||
let stored_versions = decoded_server_info.known_versions();
|
||||
let stored_features = decoded_server_info.unstable_features;
|
||||
|
||||
assert_eq!(stored_versions, versions);
|
||||
assert_eq!(stored_features.len(), 1);
|
||||
assert_eq!(stored_features.get("org.matrix.experimental"), Some(&true));
|
||||
|
||||
self.remove_kv_data(StateStoreDataKey::ServerCapabilities).await.unwrap();
|
||||
assert_matches!(self.get_kv_data(StateStoreDataKey::ServerCapabilities).await, Ok(None));
|
||||
self.remove_kv_data(StateStoreDataKey::ServerInfo).await.unwrap();
|
||||
assert_matches!(self.get_kv_data(StateStoreDataKey::ServerInfo).await, Ok(None));
|
||||
}
|
||||
|
||||
async fn test_sync_token_saving(&self) {
|
||||
@@ -1807,9 +1809,9 @@ macro_rules! statestore_integration_tests {
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_server_capabilities_saving() {
|
||||
async fn test_server_info_saving() {
|
||||
let store = get_store().await.unwrap().into_state_store();
|
||||
store.test_server_capabilities_saving().await
|
||||
store.test_server_info_saving().await
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
|
||||
@@ -37,7 +37,7 @@ use tracing::{debug, instrument, warn};
|
||||
|
||||
use super::{
|
||||
send_queue::{ChildTransactionId, QueuedRequest, SentRequestKey},
|
||||
traits::{ComposerDraft, ServerCapabilities},
|
||||
traits::{ComposerDraft, ServerInfo},
|
||||
DependentQueuedRequest, DependentQueuedRequestKind, QueuedRequestKind, Result, RoomInfo,
|
||||
RoomLoadSettings, StateChanges, StateStore, StoreError,
|
||||
};
|
||||
@@ -54,7 +54,7 @@ struct MemoryStoreInner {
|
||||
composer_drafts: HashMap<(OwnedRoomId, Option<OwnedEventId>), ComposerDraft>,
|
||||
user_avatar_url: HashMap<OwnedUserId, OwnedMxcUri>,
|
||||
sync_token: Option<String>,
|
||||
server_capabilities: Option<ServerCapabilities>,
|
||||
server_info: Option<ServerInfo>,
|
||||
filters: HashMap<String, String>,
|
||||
utd_hook_manager_data: Option<GrowableBloom>,
|
||||
account_data: HashMap<GlobalAccountDataEventType, Raw<AnyGlobalAccountDataEvent>>,
|
||||
@@ -149,8 +149,8 @@ impl StateStore for MemoryStore {
|
||||
StateStoreDataKey::SyncToken => {
|
||||
inner.sync_token.clone().map(StateStoreDataValue::SyncToken)
|
||||
}
|
||||
StateStoreDataKey::ServerCapabilities => {
|
||||
inner.server_capabilities.clone().map(StateStoreDataValue::ServerCapabilities)
|
||||
StateStoreDataKey::ServerInfo => {
|
||||
inner.server_info.clone().map(StateStoreDataValue::ServerInfo)
|
||||
}
|
||||
StateStoreDataKey::Filter(filter_name) => {
|
||||
inner.filters.get(filter_name).cloned().map(StateStoreDataValue::Filter)
|
||||
@@ -222,11 +222,9 @@ impl StateStore for MemoryStore {
|
||||
value.into_composer_draft().expect("Session data not a composer draft"),
|
||||
);
|
||||
}
|
||||
StateStoreDataKey::ServerCapabilities => {
|
||||
inner.server_capabilities = Some(
|
||||
value
|
||||
.into_server_capabilities()
|
||||
.expect("Session data not containing server capabilities"),
|
||||
StateStoreDataKey::ServerInfo => {
|
||||
inner.server_info = Some(
|
||||
value.into_server_info().expect("Session data not containing server info"),
|
||||
);
|
||||
}
|
||||
StateStoreDataKey::SeenKnockRequests(room_id) => {
|
||||
@@ -246,7 +244,7 @@ impl StateStore for MemoryStore {
|
||||
let mut inner = self.inner.write().unwrap();
|
||||
match key {
|
||||
StateStoreDataKey::SyncToken => inner.sync_token = None,
|
||||
StateStoreDataKey::ServerCapabilities => inner.server_capabilities = None,
|
||||
StateStoreDataKey::ServerInfo => inner.server_info = None,
|
||||
StateStoreDataKey::Filter(filter_name) => {
|
||||
inner.filters.remove(filter_name);
|
||||
}
|
||||
|
||||
@@ -81,8 +81,8 @@ pub use self::{
|
||||
SentMediaInfo, SentRequestKey, SerializableEventContent,
|
||||
},
|
||||
traits::{
|
||||
ComposerDraft, ComposerDraftType, DynStateStore, IntoStateStore, ServerCapabilities,
|
||||
StateStore, StateStoreDataKey, StateStoreDataValue, StateStoreExt,
|
||||
ComposerDraft, ComposerDraftType, DynStateStore, IntoStateStore, ServerInfo, StateStore,
|
||||
StateStoreDataKey, StateStoreDataValue, StateStoreExt,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ use async_trait::async_trait;
|
||||
use growable_bloom_filter::GrowableBloom;
|
||||
use matrix_sdk_common::AsyncTraitDeps;
|
||||
use ruma::{
|
||||
api::MatrixVersion,
|
||||
api::{client::discovery::get_supported_versions, MatrixVersion},
|
||||
events::{
|
||||
presence::PresenceEvent,
|
||||
receipt::{Receipt, ReceiptThread, ReceiptType},
|
||||
@@ -950,12 +950,11 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Server capabilities returned by the /client/versions endpoint.
|
||||
/// Useful server info such as data returned by the /client/versions and
|
||||
/// .well-known/client/matrix endpoints.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct ServerCapabilities {
|
||||
pub struct ServerInfo {
|
||||
/// Versions supported by the remote server.
|
||||
///
|
||||
/// This contains [`MatrixVersion`]s converted to strings.
|
||||
pub versions: Vec<String>,
|
||||
|
||||
/// List of unstable features and their enablement status.
|
||||
@@ -966,34 +965,37 @@ pub struct ServerCapabilities {
|
||||
last_fetch_ts: f64,
|
||||
}
|
||||
|
||||
impl ServerCapabilities {
|
||||
impl ServerInfo {
|
||||
/// The number of milliseconds after which the data is considered stale.
|
||||
pub const STALE_THRESHOLD: f64 = (1000 * 60 * 60 * 24 * 7) as _; // seven days
|
||||
|
||||
/// Encode server capabilities into this serializable struct.
|
||||
pub fn new(versions: &[MatrixVersion], unstable_features: BTreeMap<String, bool>) -> Self {
|
||||
Self {
|
||||
versions: versions.iter().map(|item| item.to_string()).collect(),
|
||||
unstable_features,
|
||||
last_fetch_ts: now_timestamp_ms(),
|
||||
}
|
||||
/// Encode server info into this serializable struct.
|
||||
pub fn new(versions: Vec<String>, unstable_features: BTreeMap<String, bool>) -> Self {
|
||||
Self { versions, unstable_features, last_fetch_ts: now_timestamp_ms() }
|
||||
}
|
||||
|
||||
/// Decode server capabilities from this serializable struct.
|
||||
/// Decode server info from this serializable struct.
|
||||
///
|
||||
/// May return `None` if the data is considered stale, after
|
||||
/// [`Self::STALE_THRESHOLD`] milliseconds since the last time we stored
|
||||
/// it.
|
||||
pub fn maybe_decode(&self) -> Option<(Vec<MatrixVersion>, BTreeMap<String, bool>)> {
|
||||
pub fn maybe_decode(&self) -> Option<Self> {
|
||||
if now_timestamp_ms() - self.last_fetch_ts >= Self::STALE_THRESHOLD {
|
||||
None
|
||||
} else {
|
||||
Some((
|
||||
self.versions.iter().filter_map(|item| item.parse().ok()).collect(),
|
||||
self.unstable_features.clone(),
|
||||
))
|
||||
Some(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
/// Extracts known Matrix versions from the un-typed list of strings.
|
||||
///
|
||||
/// Note: Matrix versions that Ruma cannot parse, or does not know about,
|
||||
/// are discarded.
|
||||
pub fn known_versions(&self) -> Vec<MatrixVersion> {
|
||||
get_supported_versions::Response::new(self.versions.clone())
|
||||
.known_versions()
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the current timestamp as the number of milliseconds since Unix Epoch.
|
||||
@@ -1011,8 +1013,8 @@ pub enum StateStoreDataValue {
|
||||
/// The sync token.
|
||||
SyncToken(String),
|
||||
|
||||
/// The server capabilities.
|
||||
ServerCapabilities(ServerCapabilities),
|
||||
/// The server info (versions, well-known etc).
|
||||
ServerInfo(ServerInfo),
|
||||
|
||||
/// A filter with the given ID.
|
||||
Filter(String),
|
||||
@@ -1097,9 +1099,9 @@ impl StateStoreDataValue {
|
||||
as_variant!(self, Self::ComposerDraft)
|
||||
}
|
||||
|
||||
/// Get this value if it is the server capabilities metadata.
|
||||
pub fn into_server_capabilities(self) -> Option<ServerCapabilities> {
|
||||
as_variant!(self, Self::ServerCapabilities)
|
||||
/// Get this value if it is the server info metadata.
|
||||
pub fn into_server_info(self) -> Option<ServerInfo> {
|
||||
as_variant!(self, Self::ServerInfo)
|
||||
}
|
||||
|
||||
/// Get this value if it is the data for the ignored join requests.
|
||||
@@ -1114,8 +1116,8 @@ pub enum StateStoreDataKey<'a> {
|
||||
/// The sync token.
|
||||
SyncToken,
|
||||
|
||||
/// The server capabilities,
|
||||
ServerCapabilities,
|
||||
/// The server info,
|
||||
ServerInfo,
|
||||
|
||||
/// A filter with the given name.
|
||||
Filter(&'a str),
|
||||
@@ -1143,9 +1145,9 @@ pub enum StateStoreDataKey<'a> {
|
||||
impl StateStoreDataKey<'_> {
|
||||
/// Key to use for the [`SyncToken`][Self::SyncToken] variant.
|
||||
pub const SYNC_TOKEN: &'static str = "sync_token";
|
||||
/// Key to use for the [`ServerCapabilities`][Self::ServerCapabilities]
|
||||
/// Key to use for the [`ServerInfo`][Self::ServerInfo]
|
||||
/// variant.
|
||||
pub const SERVER_CAPABILITIES: &'static str = "server_capabilities";
|
||||
pub const SERVER_INFO: &'static str = "server_capabilities"; // Note: this is the old name, kept for backwards compatibility.
|
||||
/// Key prefix to use for the [`Filter`][Self::Filter] variant.
|
||||
pub const FILTER: &'static str = "filter";
|
||||
/// Key prefix to use for the [`UserAvatarUrl`][Self::UserAvatarUrl]
|
||||
@@ -1171,21 +1173,21 @@ impl StateStoreDataKey<'_> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{now_timestamp_ms, ServerCapabilities};
|
||||
use super::{now_timestamp_ms, ServerInfo};
|
||||
|
||||
#[test]
|
||||
fn test_stale_server_capabilities() {
|
||||
let mut caps = ServerCapabilities {
|
||||
fn test_stale_server_info() {
|
||||
let mut server_info = ServerInfo {
|
||||
versions: Default::default(),
|
||||
unstable_features: Default::default(),
|
||||
last_fetch_ts: now_timestamp_ms() - ServerCapabilities::STALE_THRESHOLD - 1.0,
|
||||
last_fetch_ts: now_timestamp_ms() - ServerInfo::STALE_THRESHOLD - 1.0,
|
||||
};
|
||||
|
||||
// Definitely stale.
|
||||
assert!(caps.maybe_decode().is_none());
|
||||
assert!(server_info.maybe_decode().is_none());
|
||||
|
||||
// Definitely not stale.
|
||||
caps.last_fetch_ts = now_timestamp_ms() - 1.0;
|
||||
assert!(caps.maybe_decode().is_some());
|
||||
server_info.last_fetch_ts = now_timestamp_ms() - 1.0;
|
||||
assert!(server_info.maybe_decode().is_some());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ use matrix_sdk_base::{
|
||||
store::{
|
||||
ChildTransactionId, ComposerDraft, DependentQueuedRequest, DependentQueuedRequestKind,
|
||||
QueuedRequest, QueuedRequestKind, RoomLoadSettings, SentRequestKey,
|
||||
SerializableEventContent, ServerCapabilities, StateChanges, StateStore, StoreError,
|
||||
SerializableEventContent, ServerInfo, StateChanges, StateStore, StoreError,
|
||||
},
|
||||
MinimalRoomMemberEvent, RoomInfo, RoomMemberships, StateStoreDataKey, StateStoreDataValue,
|
||||
};
|
||||
@@ -400,10 +400,9 @@ impl IndexeddbStateStore {
|
||||
StateStoreDataKey::SyncToken => {
|
||||
self.encode_key(StateStoreDataKey::SYNC_TOKEN, StateStoreDataKey::SYNC_TOKEN)
|
||||
}
|
||||
StateStoreDataKey::ServerCapabilities => self.encode_key(
|
||||
StateStoreDataKey::SERVER_CAPABILITIES,
|
||||
StateStoreDataKey::SERVER_CAPABILITIES,
|
||||
),
|
||||
StateStoreDataKey::ServerInfo => {
|
||||
self.encode_key(StateStoreDataKey::SERVER_INFO, StateStoreDataKey::SERVER_INFO)
|
||||
}
|
||||
StateStoreDataKey::Filter(filter_name) => {
|
||||
self.encode_key(StateStoreDataKey::FILTER, (StateStoreDataKey::FILTER, filter_name))
|
||||
}
|
||||
@@ -537,10 +536,10 @@ impl_state_store!({
|
||||
.map(|f| self.deserialize_value::<String>(&f))
|
||||
.transpose()?
|
||||
.map(StateStoreDataValue::SyncToken),
|
||||
StateStoreDataKey::ServerCapabilities => value
|
||||
.map(|f| self.deserialize_value::<ServerCapabilities>(&f))
|
||||
StateStoreDataKey::ServerInfo => value
|
||||
.map(|f| self.deserialize_value::<ServerInfo>(&f))
|
||||
.transpose()?
|
||||
.map(StateStoreDataValue::ServerCapabilities),
|
||||
.map(StateStoreDataValue::ServerInfo),
|
||||
StateStoreDataKey::Filter(_) => value
|
||||
.map(|f| self.deserialize_value::<String>(&f))
|
||||
.transpose()?
|
||||
@@ -580,10 +579,8 @@ impl_state_store!({
|
||||
let serialized_value = match key {
|
||||
StateStoreDataKey::SyncToken => self
|
||||
.serialize_value(&value.into_sync_token().expect("Session data not a sync token")),
|
||||
StateStoreDataKey::ServerCapabilities => self.serialize_value(
|
||||
&value
|
||||
.into_server_capabilities()
|
||||
.expect("Session data not containing server capabilities"),
|
||||
StateStoreDataKey::ServerInfo => self.serialize_value(
|
||||
&value.into_server_info().expect("Session data not containing server info"),
|
||||
),
|
||||
StateStoreDataKey::Filter(_) => {
|
||||
self.serialize_value(&value.into_filter().expect("Session data not a filter"))
|
||||
|
||||
@@ -410,9 +410,7 @@ impl SqliteStateStore {
|
||||
fn encode_state_store_data_key(&self, key: StateStoreDataKey<'_>) -> Key {
|
||||
let key_s = match key {
|
||||
StateStoreDataKey::SyncToken => Cow::Borrowed(StateStoreDataKey::SYNC_TOKEN),
|
||||
StateStoreDataKey::ServerCapabilities => {
|
||||
Cow::Borrowed(StateStoreDataKey::SERVER_CAPABILITIES)
|
||||
}
|
||||
StateStoreDataKey::ServerInfo => Cow::Borrowed(StateStoreDataKey::SERVER_INFO),
|
||||
StateStoreDataKey::Filter(f) => {
|
||||
Cow::Owned(format!("{}:{f}", StateStoreDataKey::FILTER))
|
||||
}
|
||||
@@ -1029,8 +1027,8 @@ impl StateStore for SqliteStateStore {
|
||||
StateStoreDataKey::SyncToken => {
|
||||
StateStoreDataValue::SyncToken(self.deserialize_value(&data)?)
|
||||
}
|
||||
StateStoreDataKey::ServerCapabilities => {
|
||||
StateStoreDataValue::ServerCapabilities(self.deserialize_value(&data)?)
|
||||
StateStoreDataKey::ServerInfo => {
|
||||
StateStoreDataValue::ServerInfo(self.deserialize_value(&data)?)
|
||||
}
|
||||
StateStoreDataKey::Filter(_) => {
|
||||
StateStoreDataValue::Filter(self.deserialize_value(&data)?)
|
||||
@@ -1064,10 +1062,8 @@ impl StateStore for SqliteStateStore {
|
||||
StateStoreDataKey::SyncToken => self.serialize_value(
|
||||
&value.into_sync_token().expect("Session data not a sync token"),
|
||||
)?,
|
||||
StateStoreDataKey::ServerCapabilities => self.serialize_value(
|
||||
&value
|
||||
.into_server_capabilities()
|
||||
.expect("Session data not containing server capabilities"),
|
||||
StateStoreDataKey::ServerInfo => self.serialize_value(
|
||||
&value.into_server_info().expect("Session data not containing server info"),
|
||||
)?,
|
||||
StateStoreDataKey::Filter(_) => {
|
||||
self.serialize_value(&value.into_filter().expect("Session data not a filter"))?
|
||||
|
||||
@@ -189,7 +189,7 @@ impl SyncTaskSupervisor {
|
||||
//
|
||||
// Still, as a precaution, we're going to sleep here for a while in the Error
|
||||
// case.
|
||||
match client.fetch_server_capabilities(Some(request_config)).await {
|
||||
match client.fetch_server_versions(Some(request_config)).await {
|
||||
Ok(_) => break,
|
||||
Err(_) => sleep(Duration::from_millis(100)).await,
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ use crate::encryption::EncryptionSettings;
|
||||
use crate::http_client::HttpSettings;
|
||||
use crate::{
|
||||
authentication::{oauth::OAuthCtx, AuthCtx},
|
||||
client::ClientServerCapabilities,
|
||||
client::ClientServerInfo,
|
||||
config::RequestConfig,
|
||||
error::RumaApiError,
|
||||
http_client::HttpClient,
|
||||
@@ -560,10 +560,8 @@ impl ClientBuilder {
|
||||
// Enable the send queue by default.
|
||||
let send_queue = Arc::new(SendQueueData::new(true));
|
||||
|
||||
let server_capabilities = ClientServerCapabilities {
|
||||
server_versions: self.server_versions,
|
||||
unstable_features: None,
|
||||
};
|
||||
let server_info =
|
||||
ClientServerInfo { server_versions: self.server_versions, unstable_features: None };
|
||||
|
||||
let event_cache = OnceCell::new();
|
||||
let inner = ClientInner::new(
|
||||
@@ -573,7 +571,7 @@ impl ClientBuilder {
|
||||
sliding_sync_version,
|
||||
http_client,
|
||||
base_client,
|
||||
server_capabilities,
|
||||
server_info,
|
||||
self.respect_login_well_known,
|
||||
event_cache,
|
||||
send_queue,
|
||||
|
||||
@@ -16,13 +16,13 @@ use matrix_sdk_base::ttl_cache::TtlCache;
|
||||
use ruma::api::client::discovery::get_authorization_server_metadata::msc2965::AuthorizationServerMetadata;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use super::ClientServerCapabilities;
|
||||
use super::ClientServerInfo;
|
||||
|
||||
/// A collection of in-memory data that the `Client` might want to cache to
|
||||
/// avoid hitting the homeserver every time users request the data.
|
||||
pub(crate) struct ClientCaches {
|
||||
/// Server capabilities, either prefilled during building or fetched from
|
||||
/// the server.
|
||||
pub(super) server_capabilities: RwLock<ClientServerCapabilities>,
|
||||
/// Server info, either prefilled during building or fetched from the
|
||||
/// server.
|
||||
pub(super) server_info: RwLock<ClientServerInfo>,
|
||||
pub(crate) server_metadata: tokio::sync::Mutex<TtlCache<String, AuthorizationServerMetadata>>,
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ use futures_util::StreamExt;
|
||||
use matrix_sdk_base::crypto::store::LockableCryptoStore;
|
||||
use matrix_sdk_base::{
|
||||
event_cache::store::EventCacheStoreLock,
|
||||
store::{DynStateStore, RoomLoadSettings, ServerCapabilities},
|
||||
store::{DynStateStore, RoomLoadSettings, ServerInfo},
|
||||
sync::{Notification, RoomUpdates},
|
||||
BaseClient, RoomInfoNotableUpdate, RoomState, RoomStateFilter, SendOutsideWasm, SessionMeta,
|
||||
StateStoreDataKey, StateStoreDataValue, SyncOutsideWasm,
|
||||
@@ -357,7 +357,7 @@ impl ClientInner {
|
||||
sliding_sync_version: SlidingSyncVersion,
|
||||
http_client: HttpClient,
|
||||
base_client: BaseClient,
|
||||
server_capabilities: ClientServerCapabilities,
|
||||
server_info: ClientServerInfo,
|
||||
respect_login_well_known: bool,
|
||||
event_cache: OnceCell<EventCache>,
|
||||
send_queue: Arc<SendQueueData>,
|
||||
@@ -366,7 +366,7 @@ impl ClientInner {
|
||||
cross_process_store_locks_holder_name: String,
|
||||
) -> Arc<Self> {
|
||||
let caches = ClientCaches {
|
||||
server_capabilities: server_capabilities.into(),
|
||||
server_info: server_info.into(),
|
||||
server_metadata: Mutex::new(TtlCache::new()),
|
||||
};
|
||||
|
||||
@@ -1736,11 +1736,11 @@ impl Client {
|
||||
.send(SessionChange::UnknownToken { soft_logout: *soft_logout });
|
||||
}
|
||||
|
||||
/// Fetches server capabilities from network; no caching.
|
||||
pub async fn fetch_server_capabilities(
|
||||
/// Fetches server versions from network; no caching.
|
||||
pub async fn fetch_server_versions(
|
||||
&self,
|
||||
request_config: Option<RequestConfig>,
|
||||
) -> HttpResult<(Box<[MatrixVersion]>, BTreeMap<String, bool>)> {
|
||||
) -> HttpResult<(Vec<String>, BTreeMap<String, bool>)> {
|
||||
let resp = self
|
||||
.inner
|
||||
.http_client
|
||||
@@ -1754,78 +1754,73 @@ impl Client {
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Fill both unstable features and server versions at once.
|
||||
let mut versions = resp.known_versions().collect::<Vec<_>>();
|
||||
if versions.is_empty() {
|
||||
versions.push(MatrixVersion::V1_0);
|
||||
}
|
||||
|
||||
Ok((versions.into(), resp.unstable_features))
|
||||
Ok((resp.versions, resp.unstable_features))
|
||||
}
|
||||
|
||||
/// Load server capabilities from storage, or fetch them from network and
|
||||
/// cache them.
|
||||
async fn load_or_fetch_server_capabilities(
|
||||
&self,
|
||||
) -> HttpResult<(Box<[MatrixVersion]>, BTreeMap<String, bool>)> {
|
||||
match self.state_store().get_kv_data(StateStoreDataKey::ServerCapabilities).await {
|
||||
/// Load server info from storage, or fetch them from network and cache
|
||||
/// them.
|
||||
async fn load_or_fetch_server_info(&self) -> HttpResult<ServerInfo> {
|
||||
match self.state_store().get_kv_data(StateStoreDataKey::ServerInfo).await {
|
||||
Ok(Some(stored)) => {
|
||||
if let Some((versions, unstable_features)) =
|
||||
stored.into_server_capabilities().and_then(|cap| cap.maybe_decode())
|
||||
if let Some(server_info) =
|
||||
stored.into_server_info().and_then(|info| info.maybe_decode())
|
||||
{
|
||||
return Ok((versions.into(), unstable_features));
|
||||
return Ok(server_info);
|
||||
}
|
||||
}
|
||||
Ok(None) => {
|
||||
// fallthrough: cache is empty
|
||||
}
|
||||
Err(err) => {
|
||||
warn!("error when loading cached server capabilities: {err}");
|
||||
warn!("error when loading cached server info: {err}");
|
||||
// fallthrough to network.
|
||||
}
|
||||
}
|
||||
|
||||
let (versions, unstable_features) = self.fetch_server_capabilities(None).await?;
|
||||
let (versions, unstable_features) = self.fetch_server_versions(None).await?;
|
||||
let server_info = ServerInfo::new(versions.clone(), unstable_features.clone());
|
||||
|
||||
// Attempt to cache the result in storage.
|
||||
{
|
||||
let encoded = ServerCapabilities::new(&versions, unstable_features.clone());
|
||||
if let Err(err) = self
|
||||
.state_store()
|
||||
.set_kv_data(
|
||||
StateStoreDataKey::ServerCapabilities,
|
||||
StateStoreDataValue::ServerCapabilities(encoded),
|
||||
StateStoreDataKey::ServerInfo,
|
||||
StateStoreDataValue::ServerInfo(server_info.clone()),
|
||||
)
|
||||
.await
|
||||
{
|
||||
warn!("error when caching server capabilities: {err}");
|
||||
warn!("error when caching server info: {err}");
|
||||
}
|
||||
}
|
||||
|
||||
Ok((versions, unstable_features))
|
||||
Ok(server_info)
|
||||
}
|
||||
|
||||
async fn get_or_load_and_cache_server_capabilities<
|
||||
T,
|
||||
F: Fn(&ClientServerCapabilities) -> Option<T>,
|
||||
>(
|
||||
async fn get_or_load_and_cache_server_info<T, F: Fn(&ClientServerInfo) -> Option<T>>(
|
||||
&self,
|
||||
f: F,
|
||||
) -> HttpResult<T> {
|
||||
let caps = &self.inner.caches.server_capabilities;
|
||||
if let Some(val) = f(&*caps.read().await) {
|
||||
let server_info = &self.inner.caches.server_info;
|
||||
if let Some(val) = f(&*server_info.read().await) {
|
||||
return Ok(val);
|
||||
}
|
||||
|
||||
let mut guard = caps.write().await;
|
||||
let mut guard = server_info.write().await;
|
||||
if let Some(val) = f(&guard) {
|
||||
return Ok(val);
|
||||
}
|
||||
|
||||
let (versions, unstable_features) = self.load_or_fetch_server_capabilities().await?;
|
||||
let server_info = self.load_or_fetch_server_info().await?;
|
||||
|
||||
guard.server_versions = Some(versions);
|
||||
guard.unstable_features = Some(unstable_features);
|
||||
// Fill both unstable features and server versions at once.
|
||||
let mut versions = server_info.known_versions();
|
||||
if versions.is_empty() {
|
||||
versions.push(MatrixVersion::V1_0);
|
||||
}
|
||||
|
||||
guard.server_versions = Some(versions.into());
|
||||
guard.unstable_features = Some(server_info.unstable_features);
|
||||
|
||||
// SAFETY: both fields were set above, so the function will always return some.
|
||||
Ok(f(&guard).unwrap())
|
||||
@@ -1850,7 +1845,8 @@ impl Client {
|
||||
/// # anyhow::Ok(()) };
|
||||
/// ```
|
||||
pub async fn server_versions(&self) -> HttpResult<Box<[MatrixVersion]>> {
|
||||
self.get_or_load_and_cache_server_capabilities(|caps| caps.server_versions.clone()).await
|
||||
self.get_or_load_and_cache_server_info(|server_info| server_info.server_versions.clone())
|
||||
.await
|
||||
}
|
||||
|
||||
/// Get the unstable features supported by the homeserver by fetching them
|
||||
@@ -1871,22 +1867,23 @@ impl Client {
|
||||
/// # anyhow::Ok(()) };
|
||||
/// ```
|
||||
pub async fn unstable_features(&self) -> HttpResult<BTreeMap<String, bool>> {
|
||||
self.get_or_load_and_cache_server_capabilities(|caps| caps.unstable_features.clone()).await
|
||||
self.get_or_load_and_cache_server_info(|server_info| server_info.unstable_features.clone())
|
||||
.await
|
||||
}
|
||||
|
||||
/// Empty the server version and unstable features cache.
|
||||
///
|
||||
/// Since the SDK caches server capabilities (versions and unstable
|
||||
/// features), it's possible to have a stale entry in the cache. This
|
||||
/// Since the SDK caches server info (versions, unstable features,
|
||||
/// well-known etc), it's possible to have a stale entry in the cache. This
|
||||
/// functions makes it possible to force reset it.
|
||||
pub async fn reset_server_capabilities(&self) -> Result<()> {
|
||||
pub async fn reset_server_info(&self) -> Result<()> {
|
||||
// Empty the in-memory caches.
|
||||
let mut guard = self.inner.caches.server_capabilities.write().await;
|
||||
let mut guard = self.inner.caches.server_info.write().await;
|
||||
guard.server_versions = None;
|
||||
guard.unstable_features = None;
|
||||
|
||||
// Empty the store cache.
|
||||
Ok(self.state_store().remove_kv_data(StateStoreDataKey::ServerCapabilities).await?)
|
||||
Ok(self.state_store().remove_kv_data(StateStoreDataKey::ServerInfo).await?)
|
||||
}
|
||||
|
||||
/// Check whether MSC 4028 is enabled on the homeserver.
|
||||
@@ -2503,7 +2500,7 @@ impl Client {
|
||||
.base_client
|
||||
.clone_with_in_memory_state_store(&cross_process_store_locks_holder_name, false)
|
||||
.await?,
|
||||
self.inner.caches.server_capabilities.read().await.clone(),
|
||||
self.inner.caches.server_info.read().await.clone(),
|
||||
self.inner.respect_login_well_known,
|
||||
self.inner.event_cache.clone(),
|
||||
self.inner.send_queue_data.clone(),
|
||||
@@ -2622,7 +2619,7 @@ impl WeakClient {
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct ClientServerCapabilities {
|
||||
struct ClientServerInfo {
|
||||
/// The Matrix versions the server supports (well-known ones only).
|
||||
server_versions: Option<Box<[MatrixVersion]>>,
|
||||
|
||||
@@ -3044,7 +3041,7 @@ pub(crate) mod tests {
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_server_capabilities_caching() {
|
||||
async fn test_server_info_caching() {
|
||||
let server = MockServer::start().await;
|
||||
let server_url = server.uri();
|
||||
let domain = server_url.strip_prefix("http://").unwrap();
|
||||
@@ -3107,7 +3104,7 @@ pub(crate) mod tests {
|
||||
server.verify().await;
|
||||
|
||||
// Now, reset the cache, and observe the endpoint being called again once.
|
||||
client.reset_server_capabilities().await.unwrap();
|
||||
client.reset_server_info().await.unwrap();
|
||||
|
||||
Mock::given(method("GET"))
|
||||
.and(path("/_matrix/client/versions"))
|
||||
|
||||
@@ -410,7 +410,7 @@ async fn test_get_media_file_with_auth_matrix_stable_feature() {
|
||||
async fn test_async_media_upload() {
|
||||
let (client, server) = logged_in_client_with_server().await;
|
||||
|
||||
client.reset_server_capabilities().await.unwrap();
|
||||
client.reset_server_info().await.unwrap();
|
||||
|
||||
// Declare Matrix version v1.7.
|
||||
Mock::given(method("GET"))
|
||||
|
||||
Reference in New Issue
Block a user