@@ -1839,17 +1839,20 @@ impl Client {
1839
1839
Ok ( server_info)
1840
1840
}
1841
1841
1842
- async fn get_or_load_and_cache_server_info < T , F : Fn ( & ClientServerInfo ) -> Option < T > > (
1842
+ async fn get_or_load_and_cache_server_info <
1843
+ Value ,
1844
+ MapFunction : Fn ( & ClientServerInfo ) -> Option < Value > ,
1845
+ > (
1843
1846
& self ,
1844
- f : F ,
1845
- ) -> HttpResult < T > {
1847
+ map : MapFunction ,
1848
+ ) -> HttpResult < Value > {
1846
1849
let server_info = & self . inner . caches . server_info ;
1847
- if let Some ( val) = f ( & * server_info. read ( ) . await ) {
1850
+ if let Some ( val) = map ( & * server_info. read ( ) . await ) {
1848
1851
return Ok ( val) ;
1849
1852
}
1850
1853
1851
- let mut guard = server_info. write ( ) . await ;
1852
- if let Some ( val) = f ( & guard ) {
1854
+ let mut guarded_server_info = server_info. write ( ) . await ;
1855
+ if let Some ( val) = map ( & guarded_server_info ) {
1853
1856
return Ok ( val) ;
1854
1857
}
1855
1858
@@ -1861,12 +1864,13 @@ impl Client {
1861
1864
versions. push ( MatrixVersion :: V1_0 ) ;
1862
1865
}
1863
1866
1864
- guard . server_versions = Some ( versions. into ( ) ) ;
1865
- guard . unstable_features = Some ( server_info. unstable_features ) ;
1866
- guard . well_known = server_info. well_known ;
1867
+ guarded_server_info . server_versions = Some ( versions. into ( ) ) ;
1868
+ guarded_server_info . unstable_features = Some ( server_info. unstable_features ) ;
1869
+ guarded_server_info . well_known = Some ( server_info. well_known ) ;
1867
1870
1868
- // SAFETY: both fields were set above, so the function will always return some.
1869
- Ok ( f ( & guard) . unwrap ( ) )
1871
+ // SAFETY: all fields were set above, so (assuming the caller doesn't attempt to
1872
+ // fetch an optional property), the function will always return some.
1873
+ Ok ( map ( & guarded_server_info) . unwrap ( ) )
1870
1874
}
1871
1875
1872
1876
/// Get the Matrix versions supported by the homeserver by fetching them
@@ -1935,13 +1939,11 @@ impl Client {
1935
1939
/// # anyhow::Ok(()) };
1936
1940
/// ```
1937
1941
pub async fn rtc_foci ( & self ) -> HttpResult < Vec < RtcFocusInfo > > {
1938
- self . get_or_load_and_cache_server_info ( |server_info| {
1939
- server_info
1940
- . well_known
1941
- . as_ref ( )
1942
- . and_then ( |well_known| well_known. rtc_foci . clone ( ) . into ( ) )
1943
- } )
1944
- . await
1942
+ let well_known = self
1943
+ . get_or_load_and_cache_server_info ( |server_info| server_info. well_known . clone ( ) )
1944
+ . await ?;
1945
+
1946
+ Ok ( well_known. map ( |well_known| well_known. rtc_foci ) . unwrap_or_default ( ) )
1945
1947
}
1946
1948
1947
1949
/// Empty the server version and unstable features cache.
@@ -2693,13 +2695,18 @@ impl WeakClient {
2693
2695
2694
2696
#[ derive( Clone ) ]
2695
2697
struct ClientServerInfo {
2696
- /// The Matrix versions the server supports (well- known ones only).
2698
+ /// The Matrix versions the server supports (known ones only).
2697
2699
server_versions : Option < Box < [ MatrixVersion ] > > ,
2698
2700
2699
2701
/// The unstable features and their on/off state on the server.
2700
2702
unstable_features : Option < BTreeMap < String , bool > > ,
2701
2703
2702
- well_known : Option < WellKnownResponse > ,
2704
+ /// The server's well-known file, if any.
2705
+ ///
2706
+ /// Note: The outer `Option` represents whether a value has been set or
2707
+ /// not, and the inner `Option` represents whether the server has a
2708
+ /// well-known file or not.
2709
+ well_known : Option < Option < WellKnownResponse > > ,
2703
2710
}
2704
2711
2705
2712
// The http mocking library is not supported for wasm32
@@ -3219,6 +3226,78 @@ pub(crate) mod tests {
3219
3226
assert_eq ! ( client. rtc_foci( ) . await . unwrap( ) , rtc_foci) ;
3220
3227
}
3221
3228
3229
+ #[ async_test]
3230
+ async fn test_server_info_without_a_well_known ( ) {
3231
+ let server = MockServer :: start ( ) . await ;
3232
+ let rtc_foci: Vec < RtcFocusInfo > = vec ! [ ] ;
3233
+
3234
+ let versions_mock = Mock :: given ( method ( "GET" ) )
3235
+ . and ( path ( "/_matrix/client/versions" ) )
3236
+ . respond_with ( ResponseTemplate :: new ( 200 ) . set_body_json ( & * test_json:: VERSIONS ) )
3237
+ . named ( "first versions mock" )
3238
+ . expect ( 1 )
3239
+ . mount_as_scoped ( & server)
3240
+ . await ;
3241
+
3242
+ let memory_store = Arc :: new ( MemoryStore :: new ( ) ) ;
3243
+ let client = Client :: builder ( )
3244
+ . homeserver_url ( server. uri ( ) ) // Configure this client directly so as to not hit the discovery endpoint.
3245
+ . store_config (
3246
+ StoreConfig :: new ( "cross-process-store-locks-holder-name" . to_owned ( ) )
3247
+ . state_store ( memory_store. clone ( ) ) ,
3248
+ )
3249
+ . build ( )
3250
+ . await
3251
+ . unwrap ( ) ;
3252
+
3253
+ assert_eq ! ( client. server_versions( ) . await . unwrap( ) . len( ) , 1 ) ;
3254
+
3255
+ // These subsequent calls hit the in-memory cache.
3256
+ assert ! ( client. server_versions( ) . await . unwrap( ) . contains( & MatrixVersion :: V1_0 ) ) ;
3257
+ assert_eq ! ( client. rtc_foci( ) . await . unwrap( ) , rtc_foci) ;
3258
+
3259
+ drop ( client) ;
3260
+
3261
+ let client = Client :: builder ( )
3262
+ . homeserver_url ( server. uri ( ) ) // Configure this client directly so as to not hit the discovery endpoint.
3263
+ . store_config (
3264
+ StoreConfig :: new ( "cross-process-store-locks-holder-name" . to_owned ( ) )
3265
+ . state_store ( memory_store. clone ( ) ) ,
3266
+ )
3267
+ . build ( )
3268
+ . await
3269
+ . unwrap ( ) ;
3270
+
3271
+ // This call to the new client hits the on-disk cache.
3272
+ assert_eq ! (
3273
+ client. unstable_features( ) . await . unwrap( ) . get( "org.matrix.e2e_cross_signing" ) ,
3274
+ Some ( & true )
3275
+ ) ;
3276
+
3277
+ // Then this call hits the in-memory cache.
3278
+ assert_eq ! ( client. rtc_foci( ) . await . unwrap( ) , rtc_foci) ;
3279
+
3280
+ drop ( versions_mock) ;
3281
+ server. verify ( ) . await ;
3282
+
3283
+ // Now, reset the cache, and observe the endpoints being called again once.
3284
+ client. reset_server_info ( ) . await . unwrap ( ) ;
3285
+
3286
+ Mock :: given ( method ( "GET" ) )
3287
+ . and ( path ( "/_matrix/client/versions" ) )
3288
+ . respond_with ( ResponseTemplate :: new ( 200 ) . set_body_json ( & * test_json:: VERSIONS ) )
3289
+ . expect ( 1 )
3290
+ . named ( "second versions mock" )
3291
+ . mount ( & server)
3292
+ . await ;
3293
+
3294
+ // Hits network again.
3295
+ assert_eq ! ( client. server_versions( ) . await . unwrap( ) . len( ) , 1 ) ;
3296
+ // Hits in-memory cache again.
3297
+ assert ! ( client. server_versions( ) . await . unwrap( ) . contains( & MatrixVersion :: V1_0 ) ) ;
3298
+ assert_eq ! ( client. rtc_foci( ) . await . unwrap( ) , rtc_foci) ;
3299
+ }
3300
+
3222
3301
#[ async_test]
3223
3302
async fn test_no_network_doesnt_cause_infinite_retries ( ) {
3224
3303
// Note: not `no_retry_test_client` or `logged_in_client` which uses the former,
0 commit comments