feat: add protocol.handle (#36674)
This commit is contained in:
parent
6a6908c4c8
commit
fda8ea9277
25 changed files with 1254 additions and 89 deletions
|
@ -1,13 +1,16 @@
|
|||
import { expect } from 'chai';
|
||||
import * as http from 'http';
|
||||
import * as http2 from 'http2';
|
||||
import * as qs from 'querystring';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import * as url from 'url';
|
||||
import * as WebSocket from 'ws';
|
||||
import { ipcMain, protocol, session, WebContents, webContents } from 'electron/main';
|
||||
import { Socket } from 'net';
|
||||
import { listen } from './lib/spec-helpers';
|
||||
import { AddressInfo, Socket } from 'net';
|
||||
import { listen, defer } from './lib/spec-helpers';
|
||||
import { once } from 'events';
|
||||
import { ReadableStream } from 'stream/web';
|
||||
|
||||
const fixturesPath = path.resolve(__dirname, 'fixtures');
|
||||
|
||||
|
@ -35,14 +38,35 @@ describe('webRequest module', () => {
|
|||
}
|
||||
});
|
||||
let defaultURL: string;
|
||||
let http2URL: string;
|
||||
|
||||
const certPath = path.join(fixturesPath, 'certificates');
|
||||
const h2server = http2.createSecureServer({
|
||||
key: fs.readFileSync(path.join(certPath, 'server.key')),
|
||||
cert: fs.readFileSync(path.join(certPath, 'server.pem'))
|
||||
}, async (req, res) => {
|
||||
if (req.method === 'POST') {
|
||||
const chunks = [];
|
||||
for await (const chunk of req) chunks.push(chunk);
|
||||
res.end(Buffer.concat(chunks).toString('utf8'));
|
||||
} else {
|
||||
res.end('<html></html>');
|
||||
}
|
||||
});
|
||||
|
||||
before(async () => {
|
||||
protocol.registerStringProtocol('cors', (req, cb) => cb(''));
|
||||
defaultURL = (await listen(server)).url + '/';
|
||||
await new Promise<void>((resolve) => {
|
||||
h2server.listen(0, '127.0.0.1', () => resolve());
|
||||
});
|
||||
http2URL = `https://127.0.0.1:${(h2server.address() as AddressInfo).port}/`;
|
||||
console.log(http2URL);
|
||||
});
|
||||
|
||||
after(() => {
|
||||
server.close();
|
||||
h2server.close();
|
||||
protocol.unregisterProtocol('cors');
|
||||
});
|
||||
|
||||
|
@ -50,6 +74,8 @@ describe('webRequest module', () => {
|
|||
// NB. sandbox: true is used because it makes navigations much (~8x) faster.
|
||||
before(async () => {
|
||||
contents = (webContents as typeof ElectronInternal.WebContents).create({ sandbox: true });
|
||||
// const w = new BrowserWindow({webPreferences: {sandbox: true}})
|
||||
// contents = w.webContents
|
||||
await contents.loadFile(path.join(fixturesPath, 'pages', 'fetch.html'));
|
||||
});
|
||||
after(() => contents.destroy());
|
||||
|
@ -161,6 +187,92 @@ describe('webRequest module', () => {
|
|||
});
|
||||
await expect(ajax(fileURL)).to.eventually.be.rejected();
|
||||
});
|
||||
|
||||
it('can handle a streaming upload', async () => {
|
||||
// Streaming fetch uploads are only supported on HTTP/2, which is only
|
||||
// supported over TLS, so...
|
||||
session.defaultSession.setCertificateVerifyProc((req, cb) => cb(0));
|
||||
defer(() => {
|
||||
session.defaultSession.setCertificateVerifyProc(null);
|
||||
});
|
||||
const contents = (webContents as typeof ElectronInternal.WebContents).create({ sandbox: true });
|
||||
defer(() => contents.close());
|
||||
await contents.loadURL(http2URL);
|
||||
|
||||
ses.webRequest.onBeforeRequest((details, callback) => {
|
||||
callback({});
|
||||
});
|
||||
|
||||
const result = await contents.executeJavaScript(`
|
||||
const stream = new ReadableStream({
|
||||
async start(controller) {
|
||||
controller.enqueue('hello world');
|
||||
controller.close();
|
||||
},
|
||||
}).pipeThrough(new TextEncoderStream());
|
||||
fetch("${http2URL}", {
|
||||
method: 'POST',
|
||||
body: stream,
|
||||
duplex: 'half',
|
||||
}).then(r => r.text())
|
||||
`);
|
||||
expect(result).to.equal('hello world');
|
||||
});
|
||||
|
||||
it('can handle a streaming upload if the uploadData is read', async () => {
|
||||
// Streaming fetch uploads are only supported on HTTP/2, which is only
|
||||
// supported over TLS, so...
|
||||
session.defaultSession.setCertificateVerifyProc((req, cb) => cb(0));
|
||||
defer(() => {
|
||||
session.defaultSession.setCertificateVerifyProc(null);
|
||||
});
|
||||
const contents = (webContents as typeof ElectronInternal.WebContents).create({ sandbox: true });
|
||||
defer(() => contents.close());
|
||||
await contents.loadURL(http2URL);
|
||||
|
||||
function makeStreamFromPipe (pipe: any): ReadableStream {
|
||||
const buf = new Uint8Array(1024 * 1024 /* 1 MB */);
|
||||
return new ReadableStream({
|
||||
async pull (controller) {
|
||||
try {
|
||||
const rv = await pipe.read(buf);
|
||||
if (rv > 0) {
|
||||
controller.enqueue(buf.subarray(0, rv));
|
||||
} else {
|
||||
controller.close();
|
||||
}
|
||||
} catch (e) {
|
||||
controller.error(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ses.webRequest.onBeforeRequest(async (details, callback) => {
|
||||
const chunks = [];
|
||||
for await (const chunk of makeStreamFromPipe((details.uploadData[0] as any).body)) { chunks.push(chunk); }
|
||||
callback({});
|
||||
});
|
||||
|
||||
const result = await contents.executeJavaScript(`
|
||||
const stream = new ReadableStream({
|
||||
async start(controller) {
|
||||
controller.enqueue('hello world');
|
||||
controller.close();
|
||||
},
|
||||
}).pipeThrough(new TextEncoderStream());
|
||||
fetch("${http2URL}", {
|
||||
method: 'POST',
|
||||
body: stream,
|
||||
duplex: 'half',
|
||||
}).then(r => r.text())
|
||||
`);
|
||||
|
||||
// NOTE: since the upload stream was consumed by the onBeforeRequest
|
||||
// handler, it can't be used again to upload to the actual server.
|
||||
// This is a limitation of the WebRequest API.
|
||||
expect(result).to.equal('');
|
||||
});
|
||||
});
|
||||
|
||||
describe('webRequest.onBeforeSendHeaders', () => {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue