feat: [net] add "priority" option to net.request (#47320)
document the default value of priority option Update the priority test to not use the httpbin.org as server Fixed the lint errors Fixed the build error Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Zeeker <13848632+zeeker999@users.noreply.github.com>
This commit is contained in:
parent
ec1704a1dd
commit
b328de39e5
5 changed files with 103 additions and 2 deletions
|
@ -60,6 +60,10 @@ following properties:
|
||||||
`strict-origin-when-cross-origin`.
|
`strict-origin-when-cross-origin`.
|
||||||
* `cache` string (optional) - can be `default`, `no-store`, `reload`,
|
* `cache` string (optional) - can be `default`, `no-store`, `reload`,
|
||||||
`no-cache`, `force-cache` or `only-if-cached`.
|
`no-cache`, `force-cache` or `only-if-cached`.
|
||||||
|
* `priority` string (optional) - can be `throttled`, `idle`, `lowest`,
|
||||||
|
`low`, `medium`, or `highest`. Defaults to `idle`.
|
||||||
|
* `priorityIncremental` boolean (optional) - the incremental loading flag as part
|
||||||
|
of HTTP extensible priorities (RFC 9218). Default is `true`.
|
||||||
|
|
||||||
`options` properties such as `protocol`, `host`, `hostname`, `port` and `path`
|
`options` properties such as `protocol`, `host`, `hostname`, `port` and `path`
|
||||||
strictly follow the Node.js model as described in the
|
strictly follow the Node.js model as described in the
|
||||||
|
|
|
@ -288,8 +288,12 @@ function parseOptions (optionsIn: ClientRequestConstructorOptions | string): Nod
|
||||||
origin: options.origin,
|
origin: options.origin,
|
||||||
referrerPolicy: options.referrerPolicy,
|
referrerPolicy: options.referrerPolicy,
|
||||||
cache: options.cache,
|
cache: options.cache,
|
||||||
allowNonHttpProtocols: Object.hasOwn(options, kAllowNonHttpProtocols)
|
allowNonHttpProtocols: Object.hasOwn(options, kAllowNonHttpProtocols),
|
||||||
|
priority: options.priority
|
||||||
};
|
};
|
||||||
|
if ('priorityIncremental' in options) {
|
||||||
|
urlLoaderOptions.priorityIncremental = options.priorityIncremental;
|
||||||
|
}
|
||||||
const headers: Record<string, string | string[]> = options.headers || {};
|
const headers: Record<string, string | string[]> = options.headers || {};
|
||||||
for (const [name, value] of Object.entries(headers)) {
|
for (const [name, value] of Object.entries(headers)) {
|
||||||
validateHeader(name, value);
|
validateHeader(name, value);
|
||||||
|
|
|
@ -644,6 +644,24 @@ gin::Handle<SimpleURLLoaderWrapper> SimpleURLLoaderWrapper::Create(
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (std::string priority; opts.Get("priority", &priority)) {
|
||||||
|
static constexpr auto Lookup =
|
||||||
|
base::MakeFixedFlatMap<std::string_view, net::RequestPriority>({
|
||||||
|
{"throttled", net::THROTTLED},
|
||||||
|
{"idle", net::IDLE},
|
||||||
|
{"lowest", net::LOWEST},
|
||||||
|
{"low", net::LOW},
|
||||||
|
{"medium", net::MEDIUM},
|
||||||
|
{"highest", net::HIGHEST},
|
||||||
|
});
|
||||||
|
if (auto iter = Lookup.find(priority); iter != Lookup.end())
|
||||||
|
request->priority = iter->second;
|
||||||
|
}
|
||||||
|
if (bool priorityIncremental = request->priority_incremental;
|
||||||
|
opts.Get("priorityIncremental", &priorityIncremental)) {
|
||||||
|
request->priority_incremental = priorityIncremental;
|
||||||
|
}
|
||||||
|
|
||||||
const bool use_session_cookies =
|
const bool use_session_cookies =
|
||||||
opts.ValueOrDefault("useSessionCookies", false);
|
opts.ValueOrDefault("useSessionCookies", false);
|
||||||
int options = network::mojom::kURLLoadOptionSniffMimeType;
|
int options = network::mojom::kURLLoadOptionSniffMimeType;
|
||||||
|
|
|
@ -1,15 +1,19 @@
|
||||||
import { net, ClientRequest, ClientRequestConstructorOptions, utilityProcess } from 'electron/main';
|
import { net, session, ClientRequest, ClientRequestConstructorOptions, utilityProcess } from 'electron/main';
|
||||||
|
|
||||||
import { expect } from 'chai';
|
import { expect } from 'chai';
|
||||||
|
|
||||||
import { once } from 'node:events';
|
import { once } from 'node:events';
|
||||||
|
import * as fs from 'node:fs';
|
||||||
import * as http from 'node:http';
|
import * as http from 'node:http';
|
||||||
|
import * as http2 from 'node:http2';
|
||||||
import * as path from 'node:path';
|
import * as path from 'node:path';
|
||||||
import { setTimeout } from 'node:timers/promises';
|
import { setTimeout } from 'node:timers/promises';
|
||||||
|
|
||||||
import { collectStreamBody, collectStreamBodyBuffer, getResponse, kOneKiloByte, kOneMegaByte, randomBuffer, randomString, respondNTimes, respondOnce } from './lib/net-helpers';
|
import { collectStreamBody, collectStreamBodyBuffer, getResponse, kOneKiloByte, kOneMegaByte, randomBuffer, randomString, respondNTimes, respondOnce } from './lib/net-helpers';
|
||||||
|
import { listen, defer } from './lib/spec-helpers';
|
||||||
|
|
||||||
const utilityFixturePath = path.resolve(__dirname, 'fixtures', 'api', 'utility-process', 'api-net-spec.js');
|
const utilityFixturePath = path.resolve(__dirname, 'fixtures', 'api', 'utility-process', 'api-net-spec.js');
|
||||||
|
const fixturesPath = path.resolve(__dirname, 'fixtures');
|
||||||
|
|
||||||
async function itUtility (name: string, fn?: Function, args?: {[key:string]: any}) {
|
async function itUtility (name: string, fn?: Function, args?: {[key:string]: any}) {
|
||||||
it(`${name} in utility process`, async () => {
|
it(`${name} in utility process`, async () => {
|
||||||
|
@ -46,6 +50,34 @@ describe('net module', () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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 if (req.method === 'GET' && req.headers[':path'] === '/get') {
|
||||||
|
res.end(JSON.stringify({
|
||||||
|
headers: req.headers
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
res.end('<html></html>');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
http2URL = (await listen(h2server)).url + '/';
|
||||||
|
});
|
||||||
|
|
||||||
|
after(() => {
|
||||||
|
h2server.close();
|
||||||
|
});
|
||||||
|
|
||||||
for (const test of [itIgnoringArgs, itUtility]) {
|
for (const test of [itIgnoringArgs, itUtility]) {
|
||||||
describe('HTTP basics', () => {
|
describe('HTTP basics', () => {
|
||||||
test('should be able to issue a basic GET request', async () => {
|
test('should be able to issue a basic GET request', async () => {
|
||||||
|
@ -1615,4 +1647,45 @@ describe('net module', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const test of [itIgnoringArgs]) {
|
||||||
|
describe('ClientRequest API', () => {
|
||||||
|
for (const [priorityName, urgency] of Object.entries({
|
||||||
|
throttled: 'u=5',
|
||||||
|
idle: 'u=4',
|
||||||
|
lowest: '',
|
||||||
|
low: 'u=2',
|
||||||
|
medium: 'u=1',
|
||||||
|
highest: 'u=0'
|
||||||
|
})) {
|
||||||
|
for (const priorityIncremental of [true, false]) {
|
||||||
|
test(`should set priority to ${priorityName}/${priorityIncremental} if requested`, async () => {
|
||||||
|
// Priority header is available on HTTP/2, which is only
|
||||||
|
// supported over TLS, so...
|
||||||
|
session.defaultSession.setCertificateVerifyProc((req, cb) => cb(0));
|
||||||
|
defer(() => {
|
||||||
|
session.defaultSession.setCertificateVerifyProc(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
const urlRequest = net.request({
|
||||||
|
url: `${http2URL}get`,
|
||||||
|
priority: priorityName as any,
|
||||||
|
priorityIncremental
|
||||||
|
});
|
||||||
|
const response = await getResponse(urlRequest);
|
||||||
|
const data = JSON.parse(await collectStreamBody(response));
|
||||||
|
let expectedPriority = urgency;
|
||||||
|
if (priorityIncremental) {
|
||||||
|
expectedPriority = expectedPriority ? expectedPriority + ', i' : 'i';
|
||||||
|
}
|
||||||
|
if (expectedPriority === '') {
|
||||||
|
expect(data.headers.priority).to.be.undefined();
|
||||||
|
} else {
|
||||||
|
expect(data.headers.priority).to.be.a('string').and.equal(expectedPriority);
|
||||||
|
}
|
||||||
|
}, { priorityName, urgency, priorityIncremental });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
2
typings/internal-ambient.d.ts
vendored
2
typings/internal-ambient.d.ts
vendored
|
@ -177,6 +177,8 @@ declare namespace NodeJS {
|
||||||
mode?: string;
|
mode?: string;
|
||||||
destination?: string;
|
destination?: string;
|
||||||
bypassCustomProtocolHandlers?: boolean;
|
bypassCustomProtocolHandlers?: boolean;
|
||||||
|
priority?: 'throttled' | 'idle' | 'lowest' | 'low' | 'medium' | 'highest';
|
||||||
|
priorityIncremental?: boolean;
|
||||||
};
|
};
|
||||||
type ResponseHead = {
|
type ResponseHead = {
|
||||||
statusCode: number;
|
statusCode: number;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue