From c74295c604f0ea465390aa6b51a19fa47e1300f4 Mon Sep 17 00:00:00 2001 From: Doug Date: Mon, 2 Jun 2025 16:14:42 +0100 Subject: [PATCH] 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. --- bindings/matrix-sdk-ffi/src/client.rs | 10 +- .../src/store/integration_tests.rs | 34 ++++--- .../matrix-sdk-base/src/store/memory_store.rs | 18 ++-- crates/matrix-sdk-base/src/store/mod.rs | 4 +- crates/matrix-sdk-base/src/store/traits.rs | 72 +++++++------- .../src/state_store/mod.rs | 21 ++-- crates/matrix-sdk-sqlite/src/state_store.rs | 14 +-- crates/matrix-sdk-ui/src/sync_service.rs | 2 +- crates/matrix-sdk/src/client/builder/mod.rs | 10 +- crates/matrix-sdk/src/client/caches.rs | 8 +- crates/matrix-sdk/src/client/mod.rs | 97 +++++++++---------- crates/matrix-sdk/tests/integration/media.rs | 2 +- 12 files changed, 141 insertions(+), 151 deletions(-) diff --git a/bindings/matrix-sdk-ffi/src/client.rs b/bindings/matrix-sdk-ffi/src/client.rs index cb6dac3c7..4b78c55c0 100644 --- a/bindings/matrix-sdk-ffi/src/client.rs +++ b/bindings/matrix-sdk-ffi/src/client.rs @@ -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?) } } diff --git a/crates/matrix-sdk-base/src/store/integration_tests.rs b/crates/matrix-sdk-base/src/store/integration_tests.rs index 5c80a098d..70cc8f672 100644 --- a/crates/matrix-sdk-base/src/store/integration_tests.rs +++ b/crates/matrix-sdk-base/src/store/integration_tests.rs @@ -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] diff --git a/crates/matrix-sdk-base/src/store/memory_store.rs b/crates/matrix-sdk-base/src/store/memory_store.rs index af4d106ab..067c00f0a 100644 --- a/crates/matrix-sdk-base/src/store/memory_store.rs +++ b/crates/matrix-sdk-base/src/store/memory_store.rs @@ -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), ComposerDraft>, user_avatar_url: HashMap, sync_token: Option, - server_capabilities: Option, + server_info: Option, filters: HashMap, utd_hook_manager_data: Option, account_data: HashMap>, @@ -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); } diff --git a/crates/matrix-sdk-base/src/store/mod.rs b/crates/matrix-sdk-base/src/store/mod.rs index ca4141b17..c9350f740 100644 --- a/crates/matrix-sdk-base/src/store/mod.rs +++ b/crates/matrix-sdk-base/src/store/mod.rs @@ -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, }, }; diff --git a/crates/matrix-sdk-base/src/store/traits.rs b/crates/matrix-sdk-base/src/store/traits.rs index 27378c62c..98fba71d5 100644 --- a/crates/matrix-sdk-base/src/store/traits.rs +++ b/crates/matrix-sdk-base/src/store/traits.rs @@ -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, /// 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) -> 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, unstable_features: BTreeMap) -> 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, BTreeMap)> { + pub fn maybe_decode(&self) -> Option { 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 { + get_supported_versions::Response::new(self.versions.clone()) + .known_versions() + .collect::>() + } } /// 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 { - as_variant!(self, Self::ServerCapabilities) + /// Get this value if it is the server info metadata. + pub fn into_server_info(self) -> Option { + 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()); } } diff --git a/crates/matrix-sdk-indexeddb/src/state_store/mod.rs b/crates/matrix-sdk-indexeddb/src/state_store/mod.rs index 5752ef004..2fb4b5cac 100644 --- a/crates/matrix-sdk-indexeddb/src/state_store/mod.rs +++ b/crates/matrix-sdk-indexeddb/src/state_store/mod.rs @@ -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::(&f)) .transpose()? .map(StateStoreDataValue::SyncToken), - StateStoreDataKey::ServerCapabilities => value - .map(|f| self.deserialize_value::(&f)) + StateStoreDataKey::ServerInfo => value + .map(|f| self.deserialize_value::(&f)) .transpose()? - .map(StateStoreDataValue::ServerCapabilities), + .map(StateStoreDataValue::ServerInfo), StateStoreDataKey::Filter(_) => value .map(|f| self.deserialize_value::(&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")) diff --git a/crates/matrix-sdk-sqlite/src/state_store.rs b/crates/matrix-sdk-sqlite/src/state_store.rs index 261bc4de1..eb9a82671 100644 --- a/crates/matrix-sdk-sqlite/src/state_store.rs +++ b/crates/matrix-sdk-sqlite/src/state_store.rs @@ -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"))? diff --git a/crates/matrix-sdk-ui/src/sync_service.rs b/crates/matrix-sdk-ui/src/sync_service.rs index f62d2e04d..ff5b3eb5d 100644 --- a/crates/matrix-sdk-ui/src/sync_service.rs +++ b/crates/matrix-sdk-ui/src/sync_service.rs @@ -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, } diff --git a/crates/matrix-sdk/src/client/builder/mod.rs b/crates/matrix-sdk/src/client/builder/mod.rs index dfcf30852..048649b2a 100644 --- a/crates/matrix-sdk/src/client/builder/mod.rs +++ b/crates/matrix-sdk/src/client/builder/mod.rs @@ -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, diff --git a/crates/matrix-sdk/src/client/caches.rs b/crates/matrix-sdk/src/client/caches.rs index 858f6082b..c81ba4abc 100644 --- a/crates/matrix-sdk/src/client/caches.rs +++ b/crates/matrix-sdk/src/client/caches.rs @@ -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, + /// Server info, either prefilled during building or fetched from the + /// server. + pub(super) server_info: RwLock, pub(crate) server_metadata: tokio::sync::Mutex>, } diff --git a/crates/matrix-sdk/src/client/mod.rs b/crates/matrix-sdk/src/client/mod.rs index 07cbd9744..3b0c34505 100644 --- a/crates/matrix-sdk/src/client/mod.rs +++ b/crates/matrix-sdk/src/client/mod.rs @@ -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, send_queue: Arc, @@ -366,7 +366,7 @@ impl ClientInner { cross_process_store_locks_holder_name: String, ) -> Arc { 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, - ) -> HttpResult<(Box<[MatrixVersion]>, BTreeMap)> { + ) -> HttpResult<(Vec, BTreeMap)> { 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::>(); - 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)> { - 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 { + 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, - >( + async fn get_or_load_and_cache_server_info Option>( &self, f: F, ) -> HttpResult { - 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> { - 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> { - 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>, @@ -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")) diff --git a/crates/matrix-sdk/tests/integration/media.rs b/crates/matrix-sdk/tests/integration/media.rs index a4303509d..a124a5f66 100644 --- a/crates/matrix-sdk/tests/integration/media.rs +++ b/crates/matrix-sdk/tests/integration/media.rs @@ -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"))