From 657334a5fd1535790ab10e69319440bbc909786b Mon Sep 17 00:00:00 2001 From: Ricky <5317425+rickythink@users.noreply.github.com> Date: Mon, 20 Nov 2023 15:30:32 +0800 Subject: [PATCH] feat: fetch stream compatibility enhance (#1551) --- web/global.d.ts | 5 ++++- web/package.json | 1 + web/service/base.ts | 8 +++++++- web/utils/stream.ts | 21 +++++++++++++++++++++ 4 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 web/utils/stream.ts diff --git a/web/global.d.ts b/web/global.d.ts index 0dff35ce0d..f5e596c752 100644 --- a/web/global.d.ts +++ b/web/global.d.ts @@ -1,2 +1,5 @@ declare module 'lamejs'; -declare module 'react-18-input-autosize'; \ No newline at end of file +declare module 'react-18-input-autosize'; +declare module 'fetch-readablestream' { + export default function fetchReadableStream(url: string, options?: RequestInit): Promise +} diff --git a/web/package.json b/web/package.json index 725c283545..ecb3bed9c5 100644 --- a/web/package.json +++ b/web/package.json @@ -36,6 +36,7 @@ "echarts": "^5.4.1", "echarts-for-react": "^3.0.2", "emoji-mart": "^5.5.2", + "fetch-readablestream": "^0.2.0", "i18next": "^22.4.13", "i18next-resources-to-backend": "^1.1.3", "immer": "^9.0.19", diff --git a/web/service/base.ts b/web/service/base.ts index 0c07602cb9..26fbf8c42a 100644 --- a/web/service/base.ts +++ b/web/service/base.ts @@ -1,8 +1,11 @@ +import fetchStream from 'fetch-readablestream' import { API_PREFIX, IS_CE_EDITION, PUBLIC_API_PREFIX } from '@/config' import Toast from '@/app/components/base/toast' import type { MessageEnd, MessageReplace, ThoughtItem } from '@/app/components/app/chat/type' +import { isSupportNativeFetchStream } from '@/utils/stream' const TIME_OUT = 100000 +const supportNativeFetchStream = isSupportNativeFetchStream() const ContentType = { json: 'application/json', @@ -220,6 +223,9 @@ const baseFetch = ( if (body && bodyStringify) options.body = JSON.stringify(body) + // for those do not support native fetch stream, we use fetch-readablestream as polyfill + const doFetch = supportNativeFetchStream ? globalThis.fetch : fetchStream + // Handle timeout return Promise.race([ new Promise((resolve, reject) => { @@ -228,7 +234,7 @@ const baseFetch = ( }, TIME_OUT) }), new Promise((resolve, reject) => { - globalThis.fetch(urlWithPrefix, options as RequestInit) + doFetch(urlWithPrefix, options as RequestInit) .then((res) => { const resClone = res.clone() // Error handler diff --git a/web/utils/stream.ts b/web/utils/stream.ts new file mode 100644 index 0000000000..c6bd2a3062 --- /dev/null +++ b/web/utils/stream.ts @@ -0,0 +1,21 @@ +// https://developer.chrome.com/articles/fetch-streaming-requests/#feature-detection +export const isSupportNativeFetchStream = () => { + const supportsRequestStreams = (() => { + let duplexAccessed = false + + const params = { + body: new ReadableStream(), + method: 'POST', + get duplex() { + duplexAccessed = true + return 'half' + }, + } + + const hasContentType = new Request('', params).headers.has('Content-Type') + + return duplexAccessed && !hasContentType + })() + + return supportsRequestStreams +}