electron **功能请求**:提高协议的可用性,处理API,

agyaoht7  于 3个月前  发布在  Electron
关注(0)|答案(3)|浏览(45)

预检清单

问题描述

如果您有

protocol.handle('https', handler)

的代码,您必须对请求负责到结束。您不能仅仅返回无内容以触发浏览器的默认行为。您必须返回一个响应。但是,在某些情况下,您可能会遇到麻烦。

  1. 丢失进度事件
// onprogress will never be called.
xhr.upload.onprogress = onprogress;
xhr.onprogress = onprogress;
  1. 如果请求体是 blob,则丢失 request.body。
// from lib/browser/api/protocol.ts
function convertToRequestBody (uploadData: ProtocolRequest['uploadData']): RequestInit['body'] {
if (!uploadData) return null;
// Optimization: skip creating a stream if the request is just a single buffer.
if (uploadData.length === 1 && (uploadData[0] as any).type === 'rawData') return uploadData[0].bytes;

const chunks = [...uploadData] as any[]; // TODO: types are wrong
let current: ReadableStreamDefaultReader | null = null;
return new ReadableStream({
pull (controller) {
if (current) {
current.read().then(({ done, value }) => {
controller.enqueue(value);
if (done) current = null;
}, (err) => {
controller.error(err);
});
} else {
if (!chunks.length) { return controller.close(); }
const chunk = chunks.shift()!;
if (chunk.type === 'rawData') { controller.enqueue(chunk.bytes); } else if (chunk.type === 'file') {
current = Readable.toWeb(createReadStream(chunk.filePath, { start: chunk.offset ?? 0, end: chunk.length >= 0 ? chunk.offset + chunk.length : undefined })).getReader();
this.pull!(controller);
} else if (chunk.type === 'stream') {
current = makeStreamFromPipe(chunk.body).getReader();
this.pull!(controller);
}
}
}
}) as RequestInit['body'];
}

上面的函数如果块的类型是 blob,不会处理。您可以添加以下代码来解决,但大型文件 blob 将完全加载到内存中。

else if (chunk.type === 'blob') {
                // 增加对blob的处理,如果不处理,发出的http body会缺少数据
                // 将blob对象引用转成arraybuffer,不确定是否进程共享内存(blob本身是共享内存)
                // 如有性能问题再做打算
                return session.getBlobData(chunk.blobUUID).then((buf) => {
                    controller.enqueue(buf);
                });
            }
  1. 重定向
protocol.handle('https', (req) => net.fetch(req, {
bypassCustomProtocolHandlers: true
});

如果 req 是 html 页面的请求,您必须关注请求重定向。您不希望使用网站 B 的源代码加载网站 A。
您不希望这样编写代码。

const req = new Request(preq.url, reqInit);
        // 这里需要从内部调用获得request对象,监听页面跳转,所以patch了net的request方法
        // patch后和unpatch前,需要确保没有其他人可以调用,所以必须立即同步unpatch
        const monkey = patch(net, 'request');
        let presp: Promise<Response>;
        try {
            presp = localSession.fetch(req, {
                redirect: 'manual',
                // @ts-ignore
                bypassCustomProtocolHandlers: true,
            });
        } finally {
            // 一定要确保unpatch
            monkey.unpatch();
        }

        const { ret: oReq } = monkey.calls[0] as {ret: Electron.ClientRequest};

        let redirectCtx: any = undefined;
        oReq.on('redirect', (statusCode, newMethod, newUrl, _headers) => {
            redirectCtx = {
                url: newUrl,
                statusCode,
            };
        });
        let res: Response | undefined;
        try {
            res = await presp;
        } catch (er) {
            if (redirectCtx) {
                res = Response.redirect(redirectCtx.url, redirectCtx.statusCode);
            }
        }

我认为在阅读您的源代码后,我可以找到第 2 点和第 3 点的解决方案。但我认为第 1 点在 JavaScript 中无法解决。
为什么我要拦截所有的 https 请求?
我想使用 electron 构建一个应用程序,该应用程序在资源查询匹配时无需网络访问,将 https 在线资源代理到本地资源。我原以为能够访问 cors、cookie、localstorage 等功能。
所有这些功能在使用 file scheme 时都无法正常工作。

建议的解决方案

为了在 protocol.handle api 中不需要给出响应对象。毕竟,在大多数情况下,我们不想对请求负责到底。我们只想触发浏览器的默认行为。
为以下代码添加支持以使用 chrome 的默认行为:

protocol.handle('https', (req) => {
    return
    // or
    return {
        session,
    }
})

我认为 electron 发送请求并命中协议拦截处理程序的过程是

ProxyingURLLoaderFactory
  bypass_custom_protocol_handlers No
      ElectronURLLoaderFactory

ElectronURLLoaderFactory 将克隆一个请求并在 javascript 层调用处理程序,并期望一个 kfree 类型的回调参数。
您这位 electron 维护者能否在 options 中调用 browserContext's urlLoader with bypass_custom_protocol_handlers 以触发 chrome 的默认行为(如果处理程序的返回值未定义)?

已考虑的其他方案

我不是真正的开发者。我只是假装知道代码并信任我的老板。对我来说找到解决方案太难了。😂😂😂😂😂😂

q9yhzks0

q9yhzks01#

我迫切需要这个。我需要一个非常简单的东西:拦截特定的请求并修改其主体。然而,没有直接的方法来保留其他情况下的原始行为,当我只想进行一个非常轻微的拦截时,它最终会破坏很多东西。令人沮丧!
我100%支持一种触发默认行为的方法,换句话说,"跳过"自定义处理。

2ic8powd

2ic8powd2#

我迫切需要这个功能。我需要一个非常简单的东西:拦截特定的请求并修改其主体。然而,没有直接的方法来保留其他情况下的原始行为,当我只想进行一个非常小的拦截时,它最终会破坏很多东西。令人沮丧!
我100%支持一种触发默认行为的方法,换句话说,“跳过”自定义处理。
我已经找到了一个解决方案,但它也相当奇怪😂。使用本地服务工作代码将服务工作者注册到您的应用程序中。这似乎不可能,但使用协议处理API是可行的。首先,我们使用protocol.handle拦截所有https请求,将http服务工作者URLMap到本地文件。在服务工作者注册后,我们删除https句柄。服务工作者的执行方式与浏览器中的一样,但是您不会发送带有自定义协议的请求尝试访问协议句柄,这将导致异常。
希望Electron团队能为这个问题找到一个更好的解决方案。

eyh26e7m

eyh26e7m3#

当前最佳方法是什么,让Electron使用协议处理API来访问本地文件?我想在渲染器部分读取图像、视频或其他文件。

相关问题