diff --git a/crates/matrix-sdk/changelog.d/6712.added.md b/crates/matrix-sdk/changelog.d/6712.added.md new file mode 100644 index 00000000000..84228e87bbe Binary files /dev/null and b/crates/matrix-sdk/changelog.d/6712.added.md differ diff --git a/crates/matrix-sdk/src/client/builder/mod.rs b/crates/matrix-sdk/src/client/builder/mod.rs index 16e4c23a087..8aa475211d8 100644 --- a/crates/matrix-sdk/src/client/builder/mod.rs +++ b/crates/matrix-sdk/src/client/builder/mod.rs @@ -25,6 +25,7 @@ use std::{ collections::BTreeSet, fmt, sync::{Arc, RwLock as StdRwLock}, + time::Duration, }; #[cfg(feature = "sqlite")] @@ -138,6 +139,7 @@ pub struct ClientBuilder { search_index_store_kind: SearchIndexStoreKind, dm_room_definition: DmRoomDefinition, media_fetcher: Arc, + discovery_cache_timeout: Duration, } impl ClientBuilder { @@ -176,6 +178,7 @@ impl ClientBuilder { search_index_store_kind: SearchIndexStoreKind::InMemory, dm_room_definition: DmRoomDefinition::MatrixSpec, media_fetcher: Arc::new(DefaultMediaFetcher), + discovery_cache_timeout: Duration::from_secs(60 * 60 * 24), } } @@ -671,6 +674,7 @@ impl ClientBuilder { search_index, thread_subscriptions_catchup, self.media_fetcher.clone(), + self.discovery_cache_timeout, ) .await; @@ -678,6 +682,16 @@ impl ClientBuilder { Ok(Client { inner }) } + + /// Set the duration after which cached discovery data (e.g. supported + /// versions, well-known info) is considered stale and needs to be + /// refreshed. + /// + /// By default, this is 24 hours + pub fn discovery_cache_timeout(mut self, stale_timeout: Duration) -> Self { + self.discovery_cache_timeout = stale_timeout; + self + } } /// Creates a server name from a user supplied string. The string is first diff --git a/crates/matrix-sdk/src/client/caches.rs b/crates/matrix-sdk/src/client/caches.rs index 068e9deb8f6..5d5b2334a95 100644 --- a/crates/matrix-sdk/src/client/caches.rs +++ b/crates/matrix-sdk/src/client/caches.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::sync::Arc; +use std::{sync::Arc, time::Duration}; use matrix_sdk_base::store::WellKnownResponse; use matrix_sdk_common::{locks::Mutex, ttl::TtlValue}; @@ -43,6 +43,8 @@ pub(crate) struct ClientCaches { pub(crate) server_metadata: Cache, /// Homeserver capabilities. pub(crate) homeserver_capabilities: Cache>, + /// The duration after which cached discovery data is considered stale. + pub(crate) discovery_cache_timeout: Duration, } /// A cached value that can either be set or not set, used to avoid confusion diff --git a/crates/matrix-sdk/src/client/mod.rs b/crates/matrix-sdk/src/client/mod.rs index fb282a93eee..3bec04e4b43 100644 --- a/crates/matrix-sdk/src/client/mod.rs +++ b/crates/matrix-sdk/src/client/mod.rs @@ -91,7 +91,8 @@ use crate::{ Account, AuthApi, AuthSession, Error, HttpError, Media, Pusher, RefreshTokenError, Result, Room, SessionTokens, TransmissionProgress, authentication::{ - AuthCtx, AuthData, ReloadSessionCallback, SaveSessionCallback, matrix::MatrixAuth, + AuthCtx, AuthData, ReloadSessionCallback, SaveSessionCallback, + matrix::MatrixAuth, oauth::OAuth, }, client::{ @@ -448,12 +449,14 @@ impl ClientInner { #[cfg(feature = "experimental-search")] search_index_handler: SearchIndex, thread_subscription_catchup: OnceCell>, media_fetcher: Arc, + discovery_cache_timeout: Duration, ) -> Arc { let caches = ClientCaches { supported_versions: Cache::with_value(supported_versions), well_known: Cache::with_value(well_known), server_metadata: Cache::new(), homeserver_capabilities: Cache::new(), + discovery_cache_timeout, }; let client = Self { @@ -3308,6 +3311,7 @@ impl Client { self.inner.search_index.clone(), self.inner.thread_subscription_catchup.clone(), (*self.inner.media_fetcher.read().await).clone(), + self.inner.caches.discovery_cache_timeout, ) .await, }; @@ -3621,6 +3625,24 @@ impl Client { pub async fn get_media_fetcher(&self) -> Arc { self.inner.media_fetcher.read().await.clone() } + + /// Force a refresh of all the cached discovery data (supported versions, + /// well-known info, homeserver capabilities, and OAuth 2.0 server + /// metadata), ignoring whether the existing cache is still fresh. + /// + /// If refreshing the well-known info fails, or if the OAuth 2.0 server + /// metadata request fails for any reason, it is silently ignored. + /// refresh failures are returned as an error, and any remaining + /// refreshes that haven't run yet are skipped. + pub async fn rediscover(&self) -> Result<(), Error> { + self.refresh_supported_versions_cache(false).await?; + self.refresh_well_known_cache().await; + self.homeserver_capabilities().refresh().await?; + match self.oauth().server_metadata().await { + Ok(_) | Err(_) => {} + } + Ok(()) + } } /// Contains the disk size of the different stores, if known. It won't be @@ -3716,7 +3738,11 @@ pub(crate) mod tests { RoomId, ServerName, UserId, api::{ FeatureFlag, MatrixVersion, - client::{room::create_room::v3::Request as CreateRoomRequest, rtc::RtcTransport}, + client::{ + discovery::get_capabilities::v3::Capabilities, + room::create_room::v3::Request as CreateRoomRequest, + rtc::RtcTransport, + }, }, assign, events::{ @@ -3940,6 +3966,27 @@ pub(crate) mod tests { client.server_versions().await.unwrap(); } + #[async_test] + async fn test_rediscover() { + let server = MatrixMockServer::new().await; + + // Mock the two required endpoints + server.mock_versions().ok().mock_once().named("versions").mount().await; + server + .mock_get_homeserver_capabilities() + .ok_with_capabilities(Capabilities::default()) + .mock_once() + .named("capabilities") + .mount() + .await; + + // Build the client + let client = server.client_builder().build().await; + + // Call rediscover and assert it succeeded + client.rediscover().await.unwrap(); + } + #[async_test] async fn test_discovery_broken_server() { let server = MatrixMockServer::new().await;