@@ -1975,11 +1975,73 @@ impl Client {
19751975 & self ,
19761976 request_config : Option < RequestConfig > ,
19771977 ) -> HttpResult < get_supported_versions:: Response > {
1978- let server_versions = self
1979- . send_inner ( get_supported_versions:: Request :: new ( ) , request_config, Default :: default ( ) )
1980- . await ?;
1978+ // Since this was called by the user, try to refresh the access token if
1979+ // necessary.
1980+ self . fetch_server_versions_inner ( false , request_config) . await
1981+ }
1982+
1983+ /// Fetches server versions from network; no caching.
1984+ ///
1985+ /// If the access token is expired and `failsafe` is `false`, this will
1986+ /// attempt to refresh the access token, otherwise this will try to make an
1987+ /// unauthenticated request instead.
1988+ pub ( crate ) async fn fetch_server_versions_inner (
1989+ & self ,
1990+ failsafe : bool ,
1991+ request_config : Option < RequestConfig > ,
1992+ ) -> HttpResult < get_supported_versions:: Response > {
1993+ if !failsafe {
1994+ // `Client::send()` handles refreshing access tokens.
1995+ return self
1996+ . send ( get_supported_versions:: Request :: new ( ) )
1997+ . with_request_config ( request_config)
1998+ . await ;
1999+ }
19812000
1982- Ok ( server_versions)
2001+ let homeserver = self . homeserver ( ) . to_string ( ) ;
2002+
2003+ // If we have a fresh access token, try with it first.
2004+ if !request_config. as_ref ( ) . is_some_and ( |config| config. skip_auth && !config. force_auth )
2005+ && self . auth_ctx ( ) . has_valid_access_token ( )
2006+ && let Some ( access_token) = self . access_token ( )
2007+ {
2008+ let result = self
2009+ . inner
2010+ . http_client
2011+ . send (
2012+ get_supported_versions:: Request :: new ( ) ,
2013+ request_config,
2014+ homeserver. clone ( ) ,
2015+ Some ( & access_token) ,
2016+ ( ) ,
2017+ Default :: default ( ) ,
2018+ )
2019+ . await ;
2020+
2021+ if let Err ( Some ( ErrorKind :: UnknownToken { .. } ) ) =
2022+ result. as_ref ( ) . map_err ( HttpError :: client_api_error_kind)
2023+ {
2024+ // If the access token is actually expired, mark it as expired and fallback to
2025+ // the unauthenticated request below.
2026+ self . auth_ctx ( ) . set_access_token_expired ( & access_token) ;
2027+ } else {
2028+ // If the request succeeded or it's an other error, just stop now.
2029+ return result;
2030+ }
2031+ }
2032+
2033+ // Try without authentication.
2034+ self . inner
2035+ . http_client
2036+ . send (
2037+ get_supported_versions:: Request :: new ( ) ,
2038+ request_config,
2039+ homeserver. clone ( ) ,
2040+ None ,
2041+ ( ) ,
2042+ Default :: default ( ) ,
2043+ )
2044+ . await
19832045 }
19842046
19852047 /// Fetches client well_known from network; no caching.
@@ -2019,7 +2081,13 @@ impl Client {
20192081
20202082 /// Load supported versions from storage, or fetch them from network and
20212083 /// cache them.
2022- async fn load_or_fetch_supported_versions ( & self ) -> HttpResult < SupportedVersionsResponse > {
2084+ ///
2085+ /// If `failsafe` is true, this will try to minimize side effects to avoid
2086+ /// possible deadlocks.
2087+ async fn load_or_fetch_supported_versions (
2088+ & self ,
2089+ failsafe : bool ,
2090+ ) -> HttpResult < SupportedVersionsResponse > {
20232091 match self . state_store ( ) . get_kv_data ( StateStoreDataKey :: SupportedVersions ) . await {
20242092 Ok ( Some ( stored) ) => {
20252093 if let Some ( supported_versions) =
@@ -2037,7 +2105,7 @@ impl Client {
20372105 }
20382106 }
20392107
2040- let server_versions = self . fetch_server_versions ( None ) . await ?;
2108+ let server_versions = self . fetch_server_versions_inner ( failsafe , None ) . await ?;
20412109 let supported_versions = SupportedVersionsResponse {
20422110 versions : server_versions. versions ,
20432111 unstable_features : server_versions. unstable_features ,
@@ -2099,6 +2167,18 @@ impl Client {
20992167 /// # anyhow::Ok(()) };
21002168 /// ```
21012169 pub async fn supported_versions ( & self ) -> HttpResult < SupportedVersions > {
2170+ self . supported_versions_inner ( false ) . await
2171+ }
2172+
2173+ /// Get the Matrix versions and features supported by the homeserver by
2174+ /// fetching them from the server or the cache.
2175+ ///
2176+ /// If `failsafe` is true, this will try to minimize side effects to avoid
2177+ /// possible deadlocks.
2178+ pub ( crate ) async fn supported_versions_inner (
2179+ & self ,
2180+ failsafe : bool ,
2181+ ) -> HttpResult < SupportedVersions > {
21022182 let cached_supported_versions = & self . inner . caches . supported_versions ;
21032183 if let CachedValue :: Cached ( val) = & * cached_supported_versions. read ( ) . await {
21042184 return Ok ( val. clone ( ) ) ;
@@ -2109,7 +2189,7 @@ impl Client {
21092189 return Ok ( val. clone ( ) ) ;
21102190 }
21112191
2112- let supported = self . load_or_fetch_supported_versions ( ) . await ?;
2192+ let supported = self . load_or_fetch_supported_versions ( failsafe ) . await ?;
21132193
21142194 // Fill both unstable features and server versions at once.
21152195 let mut supported_versions = supported. supported_versions ( ) ;
@@ -3569,6 +3649,7 @@ pub(crate) mod tests {
35693649
35703650 let versions_mock = server
35713651 . mock_versions ( )
3652+ . expect_default_access_token ( )
35723653 . ok_with_unstable_features ( )
35733654 . named ( "first versions mock" )
35743655 . expect ( 1 )
@@ -3590,6 +3671,8 @@ pub(crate) mod tests {
35903671
35913672 assert ! ( client. server_versions( ) . await . unwrap( ) . contains( & MatrixVersion :: V1_0 ) ) ;
35923673
3674+ // The result was cached.
3675+ assert_matches ! ( client. supported_versions_cached( ) . await , Ok ( Some ( _) ) ) ;
35933676 // This subsequent call hits the in-memory cache.
35943677 assert ! ( client. server_versions( ) . await . unwrap( ) . contains( & MatrixVersion :: V1_0 ) ) ;
35953678
0 commit comments