预检清单
- 我已阅读此项目的 Contributing Guidelines。
- 我同意遵循此项目遵循的 Code of Conduct。
- 我在 issue tracker 中搜索了一个与我想提交的功能请求相匹配的特征请求,但没有成功。
问题描述
如果您有
protocol.handle('https', handler)
的代码,您必须对请求负责到结束。您不能仅仅返回无内容以触发浏览器的默认行为。您必须返回一个响应。但是,在某些情况下,您可能会遇到麻烦。
- 丢失进度事件
// onprogress will never be called.
xhr.upload.onprogress = onprogress;
xhr.onprogress = onprogress;
- 如果请求体是 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);
});
}
- 重定向
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 的默认行为(如果处理程序的返回值未定义)?
已考虑的其他方案
我不是真正的开发者。我只是假装知道代码并信任我的老板。对我来说找到解决方案太难了。😂😂😂😂😂😂
3条答案
按热度按时间q9yhzks01#
我迫切需要这个。我需要一个非常简单的东西:拦截特定的请求并修改其主体。然而,没有直接的方法来保留其他情况下的原始行为,当我只想进行一个非常轻微的拦截时,它最终会破坏很多东西。令人沮丧!
我100%支持一种触发默认行为的方法,换句话说,"跳过"自定义处理。
2ic8powd2#
我迫切需要这个功能。我需要一个非常简单的东西:拦截特定的请求并修改其主体。然而,没有直接的方法来保留其他情况下的原始行为,当我只想进行一个非常小的拦截时,它最终会破坏很多东西。令人沮丧!
我100%支持一种触发默认行为的方法,换句话说,“跳过”自定义处理。
我已经找到了一个解决方案,但它也相当奇怪😂。使用本地服务工作代码将服务工作者注册到您的应用程序中。这似乎不可能,但使用协议处理API是可行的。首先,我们使用protocol.handle拦截所有https请求,将http服务工作者URLMap到本地文件。在服务工作者注册后,我们删除https句柄。服务工作者的执行方式与浏览器中的一样,但是您不会发送带有自定义协议的请求尝试访问协议句柄,这将导致异常。
希望Electron团队能为这个问题找到一个更好的解决方案。
eyh26e7m3#
当前最佳方法是什么,让Electron使用协议处理API来访问本地文件?我想在渲染器部分读取图像、视频或其他文件。