feat: allow setting the Origin header and Sec-Fetch-* headers in net.request() (#26135)

This commit is contained in:
LuoJinghua 2020-11-18 06:25:41 +08:00 committed by GitHub
parent b8372fdc29
commit e1cc78f275
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 251 additions and 13 deletions

View file

@ -197,7 +197,7 @@ class ChunkedBodyStream extends Writable {
type RedirectPolicy = 'manual' | 'follow' | 'error';
function parseOptions (optionsIn: ClientRequestConstructorOptions | string): NodeJS.CreateURLLoaderOptions & { redirectPolicy: RedirectPolicy, extraHeaders: Record<string, string> } {
function parseOptions (optionsIn: ClientRequestConstructorOptions | string): NodeJS.CreateURLLoaderOptions & { redirectPolicy: RedirectPolicy, headers: Record<string, { name: string, value: string | string[] }> } {
const options: any = typeof optionsIn === 'string' ? url.parse(optionsIn) : { ...optionsIn };
let urlStr: string = options.url;
@ -249,22 +249,26 @@ function parseOptions (optionsIn: ClientRequestConstructorOptions | string): Nod
throw new TypeError('headers must be an object');
}
const urlLoaderOptions: NodeJS.CreateURLLoaderOptions & { redirectPolicy: RedirectPolicy, extraHeaders: Record<string, string | string[]> } = {
const urlLoaderOptions: NodeJS.CreateURLLoaderOptions & { redirectPolicy: RedirectPolicy, headers: Record<string, { name: string, value: string | string[] }> } = {
method: (options.method || 'GET').toUpperCase(),
url: urlStr,
redirectPolicy,
extraHeaders: options.headers || {},
headers: {},
body: null as any,
useSessionCookies: options.useSessionCookies,
credentials: options.credentials
credentials: options.credentials,
origin: options.origin
};
for (const [name, value] of Object.entries(urlLoaderOptions.extraHeaders!)) {
const headers: Record<string, string | string[]> = options.headers || {};
for (const [name, value] of Object.entries(headers)) {
if (!isValidHeaderName(name)) {
throw new Error(`Invalid header name: '${name}'`);
}
if (!isValidHeaderValue(value.toString())) {
throw new Error(`Invalid value for header '${name}': '${value}'`);
}
const key = name.toLowerCase();
urlLoaderOptions.headers[key] = { name, value };
}
if (options.session) {
// Weak check, but it should be enough to catch 99% of accidental misuses.
@ -289,7 +293,7 @@ export class ClientRequest extends Writable implements Electron.ClientRequest {
_aborted: boolean = false;
_chunkedEncoding: boolean | undefined;
_body: Writable | undefined;
_urlLoaderOptions: NodeJS.CreateURLLoaderOptions & { extraHeaders: Record<string, string> };
_urlLoaderOptions: NodeJS.CreateURLLoaderOptions & { headers: Record<string, { name: string, value: string | string[] }> };
_redirectPolicy: RedirectPolicy;
_followRedirectCb?: () => void;
_uploadProgress?: { active: boolean, started: boolean, current: number, total: number };
@ -350,7 +354,7 @@ export class ClientRequest extends Writable implements Electron.ClientRequest {
}
const key = name.toLowerCase();
this._urlLoaderOptions.extraHeaders[key] = value;
this._urlLoaderOptions.headers[key] = { name, value };
}
getHeader (name: string) {
@ -359,7 +363,8 @@ export class ClientRequest extends Writable implements Electron.ClientRequest {
}
const key = name.toLowerCase();
return this._urlLoaderOptions.extraHeaders[key];
const header = this._urlLoaderOptions.headers[key];
return header && header.value as any;
}
removeHeader (name: string) {
@ -372,7 +377,7 @@ export class ClientRequest extends Writable implements Electron.ClientRequest {
}
const key = name.toLowerCase();
delete this._urlLoaderOptions.extraHeaders[key];
delete this._urlLoaderOptions.headers[key];
}
_write (chunk: Buffer, encoding: BufferEncoding, callback: () => void) {
@ -401,15 +406,20 @@ export class ClientRequest extends Writable implements Electron.ClientRequest {
_startRequest () {
this._started = true;
const stringifyValues = (obj: Record<string, any>) => {
const stringifyValues = (obj: Record<string, { name: string, value: string | string[] }>) => {
const ret: Record<string, string> = {};
for (const k of Object.keys(obj)) {
ret[k] = obj[k].toString();
const kv = obj[k];
ret[kv.name] = kv.value.toString();
}
return ret;
};
this._urlLoaderOptions.referrer = this._urlLoaderOptions.extraHeaders.referer || '';
const opts = { ...this._urlLoaderOptions, extraHeaders: stringifyValues(this._urlLoaderOptions.extraHeaders) };
this._urlLoaderOptions.referrer = this.getHeader('referer') || '';
this._urlLoaderOptions.origin = this._urlLoaderOptions.origin || this.getHeader('origin') || '';
this._urlLoaderOptions.hasUserActivation = this.getHeader('sec-fetch-user') === '?1';
this._urlLoaderOptions.mode = this.getHeader('sec-fetch-mode') || '';
this._urlLoaderOptions.destination = this.getHeader('sec-fetch-dest') || '';
const opts = { ...this._urlLoaderOptions, extraHeaders: stringifyValues(this._urlLoaderOptions.headers) };
this._urlLoader = createURLLoader(opts);
this._urlLoader.on('response-started', (event, finalUrl, responseHead) => {
const response = this._response = new IncomingMessage(responseHead);