@@ -1973,11 +1973,73 @@ impl Client {
19731973 & self ,
19741974 request_config : Option < RequestConfig > ,
19751975 ) -> HttpResult < get_supported_versions:: Response > {
1976- let server_versions = self
1977- . send_inner ( get_supported_versions:: Request :: new ( ) , request_config, Default :: default ( ) )
1978- . await ?;
1976+ // Since this was called by the user, try to refresh the access token if
1977+ // necessary.
1978+ self . fetch_server_versions_inner ( false , request_config) . await
1979+ }
1980+
1981+ /// Fetches server versions from network; no caching.
1982+ ///
1983+ /// If the access token is expired and `failsafe` is `false`, this will
1984+ /// attempt to refresh the access token, otherwise this will try to make an
1985+ /// unauthenticated request instead.
1986+ pub ( crate ) async fn fetch_server_versions_inner (
1987+ & self ,
1988+ failsafe : bool ,
1989+ request_config : Option < RequestConfig > ,
1990+ ) -> HttpResult < get_supported_versions:: Response > {
1991+ if !failsafe {
1992+ // `Client::send()` handles refreshing access tokens.
1993+ return self
1994+ . send ( get_supported_versions:: Request :: new ( ) )
1995+ . with_request_config ( request_config)
1996+ . await ;
1997+ }
19791998
1980- Ok ( server_versions)
1999+ let homeserver = self . homeserver ( ) . to_string ( ) ;
2000+
2001+ // If we have a fresh access token, try with it first.
2002+ if !request_config. as_ref ( ) . is_some_and ( |config| config. skip_auth && !config. force_auth )
2003+ && self . auth_ctx ( ) . has_valid_access_token ( )
2004+ && let Some ( access_token) = self . access_token ( )
2005+ {
2006+ let result = self
2007+ . inner
2008+ . http_client
2009+ . send (
2010+ get_supported_versions:: Request :: new ( ) ,
2011+ request_config,
2012+ homeserver. clone ( ) ,
2013+ Some ( & access_token) ,
2014+ ( ) ,
2015+ Default :: default ( ) ,
2016+ )
2017+ . await ;
2018+
2019+ if let Err ( Some ( ErrorKind :: UnknownToken { .. } ) ) =
2020+ result. as_ref ( ) . map_err ( HttpError :: client_api_error_kind)
2021+ {
2022+ // If the access token is actually expired, mark it as expired and fallback to
2023+ // the unauthenticated request below.
2024+ self . auth_ctx ( ) . set_access_token_expired ( & access_token) ;
2025+ } else {
2026+ // If the request succeeded or it's an other error, just stop now.
2027+ return result;
2028+ }
2029+ }
2030+
2031+ // Try without authentication.
2032+ self . inner
2033+ . http_client
2034+ . send (
2035+ get_supported_versions:: Request :: new ( ) ,
2036+ request_config,
2037+ homeserver. clone ( ) ,
2038+ None ,
2039+ ( ) ,
2040+ Default :: default ( ) ,
2041+ )
2042+ . await
19812043 }
19822044
19832045 /// Fetches client well_known from network; no caching.
@@ -2017,7 +2079,13 @@ impl Client {
20172079
20182080 /// Load supported versions from storage, or fetch them from network and
20192081 /// cache them.
2020- async fn load_or_fetch_supported_versions ( & self ) -> HttpResult < SupportedVersionsResponse > {
2082+ ///
2083+ /// If `failsafe` is true, this will try to minimize side effects to avoid
2084+ /// possible deadlocks.
2085+ async fn load_or_fetch_supported_versions (
2086+ & self ,
2087+ failsafe : bool ,
2088+ ) -> HttpResult < SupportedVersionsResponse > {
20212089 match self . state_store ( ) . get_kv_data ( StateStoreDataKey :: SupportedVersions ) . await {
20222090 Ok ( Some ( stored) ) => {
20232091 if let Some ( supported_versions) =
@@ -2035,7 +2103,7 @@ impl Client {
20352103 }
20362104 }
20372105
2038- let server_versions = self . fetch_server_versions ( None ) . await ?;
2106+ let server_versions = self . fetch_server_versions_inner ( failsafe , None ) . await ?;
20392107 let supported_versions = SupportedVersionsResponse {
20402108 versions : server_versions. versions ,
20412109 unstable_features : server_versions. unstable_features ,
@@ -2097,6 +2165,18 @@ impl Client {
20972165 /// # anyhow::Ok(()) };
20982166 /// ```
20992167 pub async fn supported_versions ( & self ) -> HttpResult < SupportedVersions > {
2168+ self . supported_versions_inner ( false ) . await
2169+ }
2170+
2171+ /// Get the Matrix versions and features supported by the homeserver by
2172+ /// fetching them from the server or the cache.
2173+ ///
2174+ /// If `failsafe` is true, this will try to minimize side effects to avoid
2175+ /// possible deadlocks.
2176+ pub ( crate ) async fn supported_versions_inner (
2177+ & self ,
2178+ failsafe : bool ,
2179+ ) -> HttpResult < SupportedVersions > {
21002180 let cached_supported_versions = & self . inner . caches . supported_versions ;
21012181 if let CachedValue :: Cached ( val) = & * cached_supported_versions. read ( ) . await {
21022182 return Ok ( val. clone ( ) ) ;
@@ -2107,7 +2187,7 @@ impl Client {
21072187 return Ok ( val. clone ( ) ) ;
21082188 }
21092189
2110- let supported = self . load_or_fetch_supported_versions ( ) . await ?;
2190+ let supported = self . load_or_fetch_supported_versions ( failsafe ) . await ?;
21112191
21122192 // Fill both unstable features and server versions at once.
21132193 let mut supported_versions = supported. supported_versions ( ) ;
@@ -3567,6 +3647,7 @@ pub(crate) mod tests {
35673647
35683648 let versions_mock = server
35693649 . mock_versions ( )
3650+ . expect_default_access_token ( )
35703651 . ok_with_unstable_features ( )
35713652 . named ( "first versions mock" )
35723653 . expect ( 1 )
@@ -3588,6 +3669,8 @@ pub(crate) mod tests {
35883669
35893670 assert ! ( client. server_versions( ) . await . unwrap( ) . contains( & MatrixVersion :: V1_0 ) ) ;
35903671
3672+ // The result was cached.
3673+ assert_matches ! ( client. supported_versions_cached( ) . await , Ok ( Some ( _) ) ) ;
35913674 // This subsequent call hits the in-memory cache.
35923675 assert ! ( client. server_versions( ) . await . unwrap( ) . contains( & MatrixVersion :: V1_0 ) ) ;
35933676
0 commit comments