diff --git a/desktop/src/main/stream.ts b/desktop/src/main/stream.ts index 8ddb80dc6..14447e7cb 100644 --- a/desktop/src/main/stream.ts +++ b/desktop/src/main/stream.ts @@ -1,15 +1,16 @@ /** * @file stream data to-from renderer using a custom protocol handler. */ -import { protocol } from "electron/main"; +import { net, protocol } from "electron/main"; import { createWriteStream, existsSync } from "node:fs"; import fs from "node:fs/promises"; import { Readable } from "node:stream"; +import { pathToFileURL } from "node:url"; import log from "./log"; /** * Register a protocol handler that we use for streaming large files between the - * main process (node) and the renderer process (browser) layer. + * main (Node.js) and renderer (Chromium) processes. * * [Note: IPC streams] * @@ -17,11 +18,14 @@ import log from "./log"; * across IPC. And passing the entire contents of the file is not feasible for * large video files because of the memory pressure the copying would entail. * - * As an alternative, we register a custom protocol handler that can provided a + * As an alternative, we register a custom protocol handler that can provides a * bi-directional stream. The renderer can stream data to the node side by * streaming the request. The node side can stream to the renderer side by * streaming the response. * + * The stream is not full duplex - while both reads and writes can be streamed, + * they need to be streamed separately. + * * See also: [Note: Transferring large amount of data over IPC] * * Depends on {@link registerPrivilegedSchemes}. @@ -29,12 +33,16 @@ import log from "./log"; export const registerStreamProtocol = () => { protocol.handle("stream", async (request: Request) => { const url = request.url; + // The request URL contains the command to run as the host, and the + // pathname of the file as the path. For example, + // + // stream://write/path/to/file + // host-pathname----- + // const { host, pathname } = new URL(url); // Convert e.g. "%20" to spaces. const path = decodeURIComponent(pathname); switch (host) { - /* stream://write/path/to/file */ - /* host-pathname----- */ case "write": try { await writeStream(path, request.body); @@ -46,6 +54,17 @@ export const registerStreamProtocol = () => { { status: 500 }, ); } + + case "read": + try { + return net.fetch(pathToFileURL(path).toString()); + } catch (e) { + log.error(`Failed to read stream for ${url}`, e); + return new Response(`Failed to read stream: ${e.message}`, { + status: 500, + }); + } + default: return new Response("", { status: 404 }); }