diff --git a/bricks/icons/src/fa-icon/index.tsx b/bricks/icons/src/fa-icon/index.tsx index 8d2ece938..5702736df 100644 --- a/bricks/icons/src/fa-icon/index.tsx +++ b/bricks/icons/src/fa-icon/index.tsx @@ -24,15 +24,15 @@ async function resolveIconDefinition( id: string ): Promise { // istanbul ignore next: experimental - if (supportsMultipartRangeRequest) { - try { + try { + if (await supportsMultipartRangeRequest()) { const content = await faRangeRequest.fetch(id); return JSON.parse(content); - } catch (error) { - // eslint-disable-next-line no-console - console.warn("Failed to fetch icon by range:", id, error); - // Fallback to traditional fetch. } + } catch (error) { + // eslint-disable-next-line no-console + console.warn("Failed to fetch icon by range:", id, error); + // Fallback to traditional fetch. } try { diff --git a/bricks/icons/src/shared/RangeRequest.ts b/bricks/icons/src/shared/RangeRequest.ts index 5c80a6ac9..aa59ebca3 100644 --- a/bricks/icons/src/shared/RangeRequest.ts +++ b/bricks/icons/src/shared/RangeRequest.ts @@ -1,12 +1,12 @@ // istanbul ignore file: experimental import { has } from "lodash"; +import { getRuntime } from "@next-core/runtime"; import antdIcons from "../antd-icon/generated/icons.json"; import easyopsIcons from "../easyops-icon/generated/icons.json"; import faIcons from "../fa-icon/generated/icons.json"; import antdRanges from "../antd-icon/generated/ranges.json"; import faRanges from "../fa-icon/generated/ranges.json"; import easyopsRanges from "../easyops-icon/generated/ranges.json"; -import { getRuntime } from "@next-core/runtime"; const publicPath = process.env.NODE_ENV === "test" ? "" : __webpack_public_path__; @@ -29,10 +29,69 @@ const SETTINGS_MAP = { }, } as unknown as Record; -export let supportsMultipartRangeRequest = +const TEST_URL = `${publicPath}manifest.json`; + +let supports = process.env.NODE_ENV !== "test" && getRuntime?.()?.getFeatureFlags()["icons-multipart-range-request"]; +const supportsPromise = new Promise((resolve) => { + if (!supports) { + resolve(false); + return; + } + + const waitSeconds = 3; + const timeout = setTimeout(() => { + // eslint-disable-next-line no-console + console.warn( + `Multipart range request test timed out after ${waitSeconds} seconds` + ); + resolve(false); + }, waitSeconds * 1000); + + (async () => { + const res = await fetch(TEST_URL, { + headers: { + Range: `bytes=0-1, 3-4`, + }, + }); + + if (!res.ok || res.status !== 206) { + // eslint-disable-next-line no-console + console.warn( + `Multipart range request test failed with status: ${res.status} ${res.statusText}` + ); + resolve(false); + clearTimeout(timeout); + return; + } + + const contentType = res.headers.get("Content-Type"); + if ( + !contentType?.match(/\bmultipart\/byteranges;\s*\S*?boundary=([^\s;]+)/) + ) { + // eslint-disable-next-line no-console + console.warn( + `Multipart range request test failed with unexpected Content-Type: "${contentType}"` + ); + resolve(false); + clearTimeout(timeout); + return; + } + + resolve(true); + clearTimeout(timeout); + })(); +}); + +export async function supportsMultipartRangeRequest() { + if (!supports) { + return false; + } + return await supportsPromise; +} + type Lib = "antd" | "fa" | "easyops"; interface Settings { @@ -378,7 +437,6 @@ async function request( headers: { Range: `bytes=${bytes.join(", ")}`, }, - cache: "force-cache", }); if (!response.ok) { @@ -394,7 +452,7 @@ async function request( console.error( `Unexpected response status: ${response.status} ${response.statusText}` ); - supportsMultipartRangeRequest = false; + supports = false; return; } @@ -406,7 +464,7 @@ async function request( if (!matches) { // eslint-disable-next-line no-console console.error(`Unexpected Content-Type: "${contentType}"`); - supportsMultipartRangeRequest = false; + supports = false; return; } diff --git a/bricks/icons/src/shared/SvgCache.ts b/bricks/icons/src/shared/SvgCache.ts index bffcf4392..0d29494cb 100644 --- a/bricks/icons/src/shared/SvgCache.ts +++ b/bricks/icons/src/shared/SvgCache.ts @@ -31,14 +31,14 @@ async function resolveIcon( let content: string | undefined; // istanbul ignore next: experimental - if (options?.id && supportsMultipartRangeRequest) { - try { + try { + if (options?.id && (await supportsMultipartRangeRequest())) { const rangeRequest = options.lib === "easyops" ? easyopsRangeRequest : antdRangeRequest; content = await rangeRequest.fetch(options.id); - } catch { - // Fallback to traditional fetch. } + } catch { + // Fallback to traditional fetch. } if (!content) {