Skip to content

Commit d30dae3

Browse files
pixlwavepoljar
authored andcommitted
fix: Handle a crash accessing the RTC foci when the well-known was None.
1 parent bdb640a commit d30dae3

File tree

2 files changed

+100
-21
lines changed

2 files changed

+100
-21
lines changed

crates/matrix-sdk/src/client/builder/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -563,7 +563,7 @@ impl ClientBuilder {
563563
let server_info = ClientServerInfo {
564564
server_versions: self.server_versions,
565565
unstable_features: None,
566-
well_known: well_known.map(Into::into),
566+
well_known: Some(well_known.map(Into::into)),
567567
};
568568

569569
let event_cache = OnceCell::new();

crates/matrix-sdk/src/client/mod.rs

Lines changed: 99 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1839,17 +1839,20 @@ impl Client {
18391839
Ok(server_info)
18401840
}
18411841

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+
>(
18431846
&self,
1844-
f: F,
1845-
) -> HttpResult<T> {
1847+
map: MapFunction,
1848+
) -> HttpResult<Value> {
18461849
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) {
18481851
return Ok(val);
18491852
}
18501853

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) {
18531856
return Ok(val);
18541857
}
18551858

@@ -1861,12 +1864,13 @@ impl Client {
18611864
versions.push(MatrixVersion::V1_0);
18621865
}
18631866

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);
18671870

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())
18701874
}
18711875

18721876
/// Get the Matrix versions supported by the homeserver by fetching them
@@ -1935,13 +1939,11 @@ impl Client {
19351939
/// # anyhow::Ok(()) };
19361940
/// ```
19371941
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())
19451947
}
19461948

19471949
/// Empty the server version and unstable features cache.
@@ -2693,13 +2695,18 @@ impl WeakClient {
26932695

26942696
#[derive(Clone)]
26952697
struct ClientServerInfo {
2696-
/// The Matrix versions the server supports (well-known ones only).
2698+
/// The Matrix versions the server supports (known ones only).
26972699
server_versions: Option<Box<[MatrixVersion]>>,
26982700

26992701
/// The unstable features and their on/off state on the server.
27002702
unstable_features: Option<BTreeMap<String, bool>>,
27012703

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>>,
27032710
}
27042711

27052712
// The http mocking library is not supported for wasm32
@@ -3219,6 +3226,78 @@ pub(crate) mod tests {
32193226
assert_eq!(client.rtc_foci().await.unwrap(), rtc_foci);
32203227
}
32213228

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+
32223301
#[async_test]
32233302
async fn test_no_network_doesnt_cause_infinite_retries() {
32243303
// Note: not `no_retry_test_client` or `logged_in_client` which uses the former,

0 commit comments

Comments
 (0)