From de50be9d1bff3091be389fe080fd2573dba60b8b Mon Sep 17 00:00:00 2001 From: Abdullah Atta Date: Fri, 25 Aug 2023 19:12:55 +0500 Subject: [PATCH] desktop: use net.fetch instead when handling protocol --- apps/desktop/global.d.ts | 31 ++++++ apps/desktop/src/utils/protocol.ts | 147 +++++++---------------------- 2 files changed, 67 insertions(+), 111 deletions(-) diff --git a/apps/desktop/global.d.ts b/apps/desktop/global.d.ts index 12bc240d1..ed9a11a10 100644 --- a/apps/desktop/global.d.ts +++ b/apps/desktop/global.d.ts @@ -19,9 +19,40 @@ along with this program. If not, see . /* eslint-disable no-var */ import { BrowserWindow } from "electron"; +import { + type FormData as FormDataType, + type Headers as HeadersType, + type Request as RequestType, + type Response as ResponseType +} from "undici"; declare global { var window: BrowserWindow | null; var RELEASE: boolean; var MAC_APP_STORE: boolean; + + // Re-export undici fetch function and various classes to global scope. + // These are classes and functions expected to be at global scope according to Node.js v18 API + // documentation. + // See: https://nodejs.org/dist/latest-v18.x/docs/api/globals.html + // eslint-disable-next-line no-var + export var { + FormData, + Headers, + Request, + Response, + fetch + }: typeof import("undici"); + + type FormData = FormDataType; + type Headers = HeadersType; + type Request = RequestType; + type Response = ResponseType; } + +// NOTE: the import in the global block above needs to be a var for this to work properly. +globalThis.fetch = fetch; +globalThis.FormData = FormData; +globalThis.Headers = Headers; +globalThis.Request = Request; +globalThis.Response = Response; diff --git a/apps/desktop/src/utils/protocol.ts b/apps/desktop/src/utils/protocol.ts index b95393e16..b4ca93326 100644 --- a/apps/desktop/src/utils/protocol.ts +++ b/apps/desktop/src/utils/protocol.ts @@ -17,16 +17,14 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -import { protocol, ProtocolRequest } from "electron"; +import { protocol, net } from "electron"; import { isDevelopment } from "./index"; import { createReadStream } from "fs"; import { extname, normalize } from "path"; import { URL } from "url"; -import fetch, { Response } from "node-fetch"; const BASE_PATH = isDevelopment() ? "../public" : ""; const HOSTNAME = `app.notesnook.com`; -const FILE_NOT_FOUND = -6; const SCHEME = "https"; const extensionToMimeType: Record = { html: "text/html", @@ -34,101 +32,47 @@ const extensionToMimeType: Record = { js: "application/javascript", css: "text/css", svg: "image/svg+xml", - png: "image/png" + png: "image/png", + jpg: "image/jpg", + ttf: "font/ttf", + woff: "font/woff", + woff2: "font/woff2" }; function registerProtocol() { - const protocolInterceptionResult = protocol.interceptStreamProtocol( - SCHEME, - async (request, callback) => { - const url = new URL(request.url); - if (shouldInterceptRequest(url)) { - console.info("Intercepting request:", request.url); - - const loadIndex = !extname(url.pathname); - const filePath = normalize( - `${__dirname}${ - loadIndex - ? `${BASE_PATH}/index.html` - : `${BASE_PATH}/${url.pathname}` - }` - ); - if (!filePath) { - console.error("Local asset file not found at", filePath); - callback({ error: FILE_NOT_FOUND }); - return; - } - const fileExtension = extname(filePath).replace(".", ""); - - const data = createReadStream(filePath); - callback({ - data, - mimeType: extensionToMimeType[fileExtension] - }); - } else { - let response: Response; - try { - const body = await getBody(request); - response = await fetch(request.url, { - ...request, - body, - headers: { - ...request.headers - // origin: `${PROTOCOL}://${HOSTNAME}/` - }, - referrer: request.referrer, - redirect: "manual" - }); - } catch (e) { - console.error(e); - console.error(`Error sending request to `, request.url, "Error: ", e); - callback({ statusCode: 400 }); - return; - } - callback({ - statusCode: response.status, - data: response.body || undefined, - headers: Object.fromEntries(response.headers.entries()), - mimeType: response.headers.get("Content-Type") || undefined + protocol.handle(SCHEME, async (request) => { + const url = new URL(request.url); + if (shouldInterceptRequest(url)) { + console.info("Intercepting request:", request.url); + const loadIndex = !extname(url.pathname); + const filePath = normalize( + `${__dirname}${ + loadIndex ? `${BASE_PATH}/index.html` : `${BASE_PATH}/${url.pathname}` + }` + ); + if (!filePath) { + console.error("Local asset file not found at", filePath); + return new Response(undefined, { + status: 404, + statusText: "FILE_NOT_FOUND" }); } + const fileExtension = extname(filePath).replace(".", ""); + return new Response(createReadStream(filePath), { + headers: { "Content-Type": extensionToMimeType[fileExtension] } + }); + } else { + try { + return net.fetch(request, { + bypassCustomProtocolHandlers: true + }); + } catch (e) { + console.error(e); + } + return Response.error(); } - ); - - console.info( - `${SCHEME} protocol inteception ${ - protocolInterceptionResult ? "successful" : "failed" - }.` - ); - - // protocol.handle(SCHEME, (request) => { - // const url = new URL(request.url); - // if (shouldInterceptRequest(url)) { - // console.info("Intercepting request:", request.url); - // const loadIndex = !extname(url.pathname); - // const absoluteFilePath = normalize( - // `${__dirname}${ - // loadIndex ? `${BASE_PATH}/index.html` : `${BASE_PATH}/${url.pathname}` - // }` - // ); - // const filePath = getPath(absoluteFilePath); - // if (!filePath) { - // console.error("Local asset file not found at", filePath); - // return new Response(undefined, { - // status: 404, - // statusText: "FILE_NOT_FOUND" - // }); - // } - // const fileExtension = extname(filePath).replace(".", ""); - // const data = createReadStream(filePath); - // return new Response(data, { - // headers: { "Content-Type": extensionToMimeType[fileExtension] } - // }); - // } else { - // return net.fetch(request); - // } - // }); - // console.info(`${SCHEME} protocol inteception "successful"`); + }); + console.info(`${SCHEME} protocol inteception "successful"`); } const bypassedRoutes: string[] = []; @@ -139,22 +83,3 @@ function shouldInterceptRequest(url: URL) { const PROTOCOL_URL = `${SCHEME}://${HOSTNAME}/`; export { registerProtocol, PROTOCOL_URL }; - -async function getBody(request: ProtocolRequest) { - const session = globalThis?.window?.webContents?.session; - - const blobParts = []; - if (!request.uploadData || !request.uploadData.length) return null; - for (const data of request.uploadData) { - if (data.bytes) { - blobParts.push(new Uint8Array(data.bytes)); - } else if (session && data.blobUUID) { - const buffer = await session.getBlobData(data.blobUUID); - if (!buffer) continue; - blobParts.push(new Uint8Array(buffer)); - } - } - const blob = new Blob(blobParts); - return blob; -} -