Skip to content

Commit 1a692b9

Browse files
authored
Use fetch() in a way that works for file URLs (#3071)
fetch returns a response code of 0 when it successfully loads a `file://` resource. This means we can't just rely on `response.ok`. Required for #2994
1 parent 2885e7e commit 1a692b9

File tree

5 files changed

+64
-5
lines changed

5 files changed

+64
-5
lines changed

src/config/Config.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
type ConfigOptions,
1414
type ResolvedConfigOptions,
1515
} from "./ConfigOptions";
16+
import { isFailure } from "../utils/fetch";
1617

1718
export class Config {
1819
private static internalInstance: Config | undefined;
@@ -74,14 +75,14 @@ async function downloadConfig(
7475
configJsonFilename: string,
7576
): Promise<ConfigOptions> {
7677
const url = new URL(configJsonFilename, window.location.href);
77-
const res = await fetch(url);
78+
const response = await fetch(url);
7879

79-
if (!res.ok || res.status === 404 || res.status === 0) {
80+
if (isFailure(response)) {
8081
// Lack of a config isn't an error, we should just use the defaults.
8182
// Also treat a blank config as no config, assuming the status code is 0, because we don't get 404s from file:
8283
// URIs so this is the only way we can not fail if the file doesn't exist when loading from a file:// URI.
8384
return DEFAULT_CONFIG;
8485
}
8586

86-
return res.json();
87+
return response.json();
8788
}

src/initializer.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import { getUrlParams } from "./UrlParams";
2828
import { Config } from "./config/Config";
2929
import { ElementCallOpenTelemetry } from "./otel/otel";
3030
import { platform } from "./Platform";
31+
import { isFailure } from "./utils/fetch";
3132

3233
// This generates a map of locale names to their URL (based on import.meta.url), which looks like this:
3334
// {
@@ -79,7 +80,7 @@ const Backend = {
7980
},
8081
});
8182

82-
if (!response.ok) {
83+
if (isFailure(response)) {
8384
throw Error(`Failed to fetch ${url}`);
8485
}
8586

src/soundUtils.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ Please see LICENSE in the repository root for full details.
77

88
import { logger } from "matrix-js-sdk/src/logger";
99

10+
import { isFailure } from "./utils/fetch";
11+
1012
type SoundDefinition = { mp3?: string; ogg: string };
1113

1214
export type PrefetchedSounds<S extends string> = Promise<
@@ -49,7 +51,7 @@ export async function prefetchSounds<S extends string>(
4951
const response = await fetch(
5052
preferredFormat === "ogg" ? ogg : (mp3 ?? ogg),
5153
);
52-
if (!response.ok) {
54+
if (isFailure(response)) {
5355
// If the sound doesn't load, it's not the end of the world. We won't play
5456
// the sound when requested, but it's better than failing the whole application.
5557
logger.warn(`Could not load sound ${name}, response was not okay`);

src/utils/fetch.test.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
Copyright 2025 New Vector Ltd.
3+
4+
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
5+
Please see LICENSE in the repository root for full details.
6+
*/
7+
8+
import { expect, describe, it } from "vitest";
9+
10+
import { isFailure } from "./fetch";
11+
12+
describe("isFailure", () => {
13+
it("returns false for a successful response", () => {
14+
expect(isFailure({ ok: true, url: "https://foo.com" } as Response)).toBe(
15+
false,
16+
);
17+
});
18+
19+
it("returns true for a failed response", () => {
20+
expect(isFailure({ ok: false, url: "https://foo.com" } as Response)).toBe(
21+
true,
22+
);
23+
});
24+
25+
it("returns false for a file:// URL with status 0", () => {
26+
expect(
27+
isFailure({ ok: false, url: "file://foo", status: 0 } as Response),
28+
).toBe(false);
29+
});
30+
});

src/utils/fetch.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
Copyright 2025 New Vector Ltd.
3+
4+
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
5+
Please see LICENSE in the repository root for full details.
6+
*/
7+
8+
/**
9+
* Check if a fetch response is a failure in a way that works with file:// URLs
10+
* @param response the response to check
11+
* @returns true if the response is a failure, false otherwise
12+
*/
13+
export function isFailure(response: Response): boolean {
14+
// if response says it's okay, then it's not a failure
15+
if (response.ok) {
16+
return false;
17+
}
18+
19+
// fetch will return status === 0 for a success on a file:// URL, so we special case it
20+
if (response.url.startsWith("file:") && response.status === 0) {
21+
return false;
22+
}
23+
24+
return true;
25+
}

0 commit comments

Comments
 (0)