feat: [net] add "credentials" option to net.request (#25284)
* feat: [net] add "credentials" option to net.request * remove debugging log * add tests
This commit is contained in:
parent
8970c80520
commit
0e7d59dd79
5 changed files with 388 additions and 266 deletions
|
@ -13,30 +13,40 @@ interface and is therefore an [EventEmitter][event-emitter].
|
||||||
the request URL. If it is an object, it is expected to fully specify an HTTP request via the
|
the request URL. If it is an object, it is expected to fully specify an HTTP request via the
|
||||||
following properties:
|
following properties:
|
||||||
* `method` String (optional) - The HTTP request method. Defaults to the GET
|
* `method` String (optional) - The HTTP request method. Defaults to the GET
|
||||||
method.
|
method.
|
||||||
* `url` String (optional) - The request URL. Must be provided in the absolute
|
* `url` String (optional) - The request URL. Must be provided in the absolute
|
||||||
form with the protocol scheme specified as http or https.
|
form with the protocol scheme specified as http or https.
|
||||||
* `session` Session (optional) - The [`Session`](session.md) instance with
|
* `session` Session (optional) - The [`Session`](session.md) instance with
|
||||||
which the request is associated.
|
which the request is associated.
|
||||||
* `partition` String (optional) - The name of the [`partition`](session.md)
|
* `partition` String (optional) - The name of the [`partition`](session.md)
|
||||||
with which the request is associated. Defaults to the empty string. The
|
with which the request is associated. Defaults to the empty string. The
|
||||||
`session` option prevails on `partition`. Thus if a `session` is explicitly
|
`session` option supersedes `partition`. Thus if a `session` is explicitly
|
||||||
specified, `partition` is ignored.
|
specified, `partition` is ignored.
|
||||||
|
* `credentials` String (optional) - Can be `include` or `omit`. Whether to
|
||||||
|
send [credentials](https://fetch.spec.whatwg.org/#credentials) with this
|
||||||
|
request. If set to `include`, credentials from the session associated with
|
||||||
|
the request will be used. If set to `omit`, credentials will not be sent
|
||||||
|
with the request (and the `'login'` event will not be triggered in the
|
||||||
|
event of a 401). This matches the behavior of the
|
||||||
|
[fetch](https://fetch.spec.whatwg.org/#concept-request-credentials-mode)
|
||||||
|
option of the same name. If this option is not specified, authentication
|
||||||
|
data from the session will be sent, and cookies will not be sent (unless
|
||||||
|
`useSessionCookies` is set).
|
||||||
* `useSessionCookies` Boolean (optional) - Whether to send cookies with this
|
* `useSessionCookies` Boolean (optional) - Whether to send cookies with this
|
||||||
request from the provided session. This will make the `net` request's
|
request from the provided session. If `credentials` is specified, this
|
||||||
cookie behavior match a `fetch` request. Default is `false`.
|
option has no effect. Default is `false`.
|
||||||
* `protocol` String (optional) - The protocol scheme in the form 'scheme:'.
|
* `protocol` String (optional) - Can be `http:` or `https:`. The protocol
|
||||||
Currently supported values are 'http:' or 'https:'. Defaults to 'http:'.
|
scheme in the form 'scheme:'. Defaults to 'http:'.
|
||||||
* `host` String (optional) - The server host provided as a concatenation of
|
* `host` String (optional) - The server host provided as a concatenation of
|
||||||
the hostname and the port number 'hostname:port'.
|
the hostname and the port number 'hostname:port'.
|
||||||
* `hostname` String (optional) - The server host name.
|
* `hostname` String (optional) - The server host name.
|
||||||
* `port` Integer (optional) - The server's listening port number.
|
* `port` Integer (optional) - The server's listening port number.
|
||||||
* `path` String (optional) - The path part of the request URL.
|
* `path` String (optional) - The path part of the request URL.
|
||||||
* `redirect` String (optional) - The redirect mode for this request. Should be
|
* `redirect` String (optional) - Can be `follow`, `error` or `manual`. The
|
||||||
one of `follow`, `error` or `manual`. Defaults to `follow`. When mode is `error`,
|
redirect mode for this request. When mode is `error`, any redirection will
|
||||||
any redirection will be aborted. When mode is `manual` the redirection will be
|
be aborted. When mode is `manual` the redirection will be cancelled unless
|
||||||
cancelled unless [`request.followRedirect`](#requestfollowredirect) is invoked
|
[`request.followRedirect`](#requestfollowredirect) is invoked synchronously
|
||||||
synchronously during the [`redirect`](#event-redirect) event.
|
during the [`redirect`](#event-redirect) event. Defaults to `follow`.
|
||||||
|
|
||||||
`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
|
||||||
|
|
|
@ -2,6 +2,7 @@ import * as url from 'url';
|
||||||
import { Readable, Writable } from 'stream';
|
import { Readable, Writable } from 'stream';
|
||||||
import { app } from 'electron/main';
|
import { app } from 'electron/main';
|
||||||
import type { ClientRequestConstructorOptions, UploadProgress } from 'electron/main';
|
import type { ClientRequestConstructorOptions, UploadProgress } from 'electron/main';
|
||||||
|
|
||||||
const {
|
const {
|
||||||
isValidHeaderName,
|
isValidHeaderName,
|
||||||
isValidHeaderValue,
|
isValidHeaderValue,
|
||||||
|
@ -243,7 +244,8 @@ function parseOptions (optionsIn: ClientRequestConstructorOptions | string): Nod
|
||||||
redirectPolicy,
|
redirectPolicy,
|
||||||
extraHeaders: options.headers || {},
|
extraHeaders: options.headers || {},
|
||||||
body: null as any,
|
body: null as any,
|
||||||
useSessionCookies: options.useSessionCookies || false
|
useSessionCookies: options.useSessionCookies,
|
||||||
|
credentials: options.credentials
|
||||||
};
|
};
|
||||||
for (const [name, value] of Object.entries(urlLoaderOptions.extraHeaders!)) {
|
for (const [name, value] of Object.entries(urlLoaderOptions.extraHeaders!)) {
|
||||||
if (!isValidHeaderName(name)) {
|
if (!isValidHeaderName(name)) {
|
||||||
|
|
|
@ -47,6 +47,27 @@ struct Converter<network::mojom::HttpRawHeaderPairPtr> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct Converter<network::mojom::CredentialsMode> {
|
||||||
|
static bool FromV8(v8::Isolate* isolate,
|
||||||
|
v8::Local<v8::Value> val,
|
||||||
|
network::mojom::CredentialsMode* out) {
|
||||||
|
std::string mode;
|
||||||
|
if (!ConvertFromV8(isolate, val, &mode))
|
||||||
|
return false;
|
||||||
|
if (mode == "omit")
|
||||||
|
*out = network::mojom::CredentialsMode::kOmit;
|
||||||
|
else if (mode == "include")
|
||||||
|
*out = network::mojom::CredentialsMode::kInclude;
|
||||||
|
else
|
||||||
|
// "same-origin" is technically a member of this enum as well, but it
|
||||||
|
// doesn't make sense in the context of `net.request()`, so don't convert
|
||||||
|
// it.
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}; // namespace gin
|
||||||
|
|
||||||
} // namespace gin
|
} // namespace gin
|
||||||
|
|
||||||
namespace electron {
|
namespace electron {
|
||||||
|
@ -355,6 +376,8 @@ gin::Handle<SimpleURLLoaderWrapper> SimpleURLLoaderWrapper::Create(
|
||||||
opts.Get("method", &request->method);
|
opts.Get("method", &request->method);
|
||||||
opts.Get("url", &request->url);
|
opts.Get("url", &request->url);
|
||||||
opts.Get("referrer", &request->referrer);
|
opts.Get("referrer", &request->referrer);
|
||||||
|
bool credentials_specified =
|
||||||
|
opts.Get("credentials", &request->credentials_mode);
|
||||||
std::map<std::string, std::string> extra_headers;
|
std::map<std::string, std::string> extra_headers;
|
||||||
if (opts.Get("extraHeaders", &extra_headers)) {
|
if (opts.Get("extraHeaders", &extra_headers)) {
|
||||||
for (const auto& it : extra_headers) {
|
for (const auto& it : extra_headers) {
|
||||||
|
@ -370,7 +393,10 @@ gin::Handle<SimpleURLLoaderWrapper> SimpleURLLoaderWrapper::Create(
|
||||||
bool use_session_cookies = false;
|
bool use_session_cookies = false;
|
||||||
opts.Get("useSessionCookies", &use_session_cookies);
|
opts.Get("useSessionCookies", &use_session_cookies);
|
||||||
int options = 0;
|
int options = 0;
|
||||||
if (!use_session_cookies) {
|
if (!credentials_specified && !use_session_cookies) {
|
||||||
|
// This is the default case, as well as the case when credentials is not
|
||||||
|
// specified and useSessionCoookies is false. credentials_mode will be
|
||||||
|
// kInclude, but cookies will be blocked.
|
||||||
request->credentials_mode = network::mojom::CredentialsMode::kInclude;
|
request->credentials_mode = network::mojom::CredentialsMode::kInclude;
|
||||||
options |= network::mojom::kURLLoadOptionBlockAllCookies;
|
options |= network::mojom::kURLLoadOptionBlockAllCookies;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { expect } from 'chai';
|
import { expect } from 'chai';
|
||||||
import { net, session, ClientRequest, BrowserWindow } from 'electron/main';
|
import { net, session, ClientRequest, BrowserWindow, ClientRequestConstructorOptions } from 'electron/main';
|
||||||
import * as http from 'http';
|
import * as http from 'http';
|
||||||
import * as url from 'url';
|
import * as url from 'url';
|
||||||
import { AddressInfo, Socket } from 'net';
|
import { AddressInfo, Socket } from 'net';
|
||||||
|
@ -215,6 +215,8 @@ describe('net module', () => {
|
||||||
expect(chunkIndex).to.be.equal(chunkCount);
|
expect(chunkIndex).to.be.equal(chunkCount);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
for (const extraOptions of [{}, { credentials: 'include' }, { useSessionCookies: false, credentials: 'include' }] as ClientRequestConstructorOptions[]) {
|
||||||
|
describe(`authentication when ${JSON.stringify(extraOptions)}`, () => {
|
||||||
it('should emit the login event when 401', async () => {
|
it('should emit the login event when 401', async () => {
|
||||||
const [user, pass] = ['user', 'pass'];
|
const [user, pass] = ['user', 'pass'];
|
||||||
const serverUrl = await respondOnce.toSingleURL((request, response) => {
|
const serverUrl = await respondOnce.toSingleURL((request, response) => {
|
||||||
|
@ -224,7 +226,7 @@ describe('net module', () => {
|
||||||
response.writeHead(200).end('ok');
|
response.writeHead(200).end('ok');
|
||||||
});
|
});
|
||||||
let loginAuthInfo: Electron.AuthInfo;
|
let loginAuthInfo: Electron.AuthInfo;
|
||||||
const request = net.request({ method: 'GET', url: serverUrl });
|
const request = net.request({ method: 'GET', url: serverUrl, ...extraOptions });
|
||||||
request.on('login', (authInfo, cb) => {
|
request.on('login', (authInfo, cb) => {
|
||||||
loginAuthInfo = authInfo;
|
loginAuthInfo = authInfo;
|
||||||
cb(user, pass);
|
cb(user, pass);
|
||||||
|
@ -235,7 +237,7 @@ describe('net module', () => {
|
||||||
expect(loginAuthInfo!.scheme).to.equal('basic');
|
expect(loginAuthInfo!.scheme).to.equal('basic');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should response when cancelling authentication', async () => {
|
it('should receive 401 response when cancelling authentication', async () => {
|
||||||
const serverUrl = await respondOnce.toSingleURL((request, response) => {
|
const serverUrl = await respondOnce.toSingleURL((request, response) => {
|
||||||
if (!request.headers.authorization) {
|
if (!request.headers.authorization) {
|
||||||
response.writeHead(401, { 'WWW-Authenticate': 'Basic realm="Foo"' });
|
response.writeHead(401, { 'WWW-Authenticate': 'Basic realm="Foo"' });
|
||||||
|
@ -244,12 +246,13 @@ describe('net module', () => {
|
||||||
response.writeHead(200).end('ok');
|
response.writeHead(200).end('ok');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const request = net.request({ method: 'GET', url: serverUrl });
|
const request = net.request({ method: 'GET', url: serverUrl, ...extraOptions });
|
||||||
request.on('login', (authInfo, cb) => {
|
request.on('login', (authInfo, cb) => {
|
||||||
cb();
|
cb();
|
||||||
});
|
});
|
||||||
const response = await getResponse(request);
|
const response = await getResponse(request);
|
||||||
const body = await collectStreamBody(response);
|
const body = await collectStreamBody(response);
|
||||||
|
expect(response.statusCode).to.equal(401);
|
||||||
expect(body).to.equal('unauthenticated');
|
expect(body).to.equal('unauthenticated');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -268,7 +271,7 @@ describe('net module', () => {
|
||||||
});
|
});
|
||||||
await bw.loadURL(serverUrl);
|
await bw.loadURL(serverUrl);
|
||||||
bw.close();
|
bw.close();
|
||||||
const request = net.request({ method: 'GET', url: serverUrl });
|
const request = net.request({ method: 'GET', url: serverUrl, ...extraOptions });
|
||||||
let logInCount = 0;
|
let logInCount = 0;
|
||||||
request.on('login', () => {
|
request.on('login', () => {
|
||||||
logInCount++;
|
logInCount++;
|
||||||
|
@ -295,7 +298,7 @@ describe('net module', () => {
|
||||||
});
|
});
|
||||||
await bw.loadURL('http://127.0.0.1:9999');
|
await bw.loadURL('http://127.0.0.1:9999');
|
||||||
bw.close();
|
bw.close();
|
||||||
const request = net.request({ method: 'GET', url: 'http://127.0.0.1:9999', session: customSession });
|
const request = net.request({ method: 'GET', url: 'http://127.0.0.1:9999', session: customSession, ...extraOptions });
|
||||||
let logInCount = 0;
|
let logInCount = 0;
|
||||||
request.on('login', () => {
|
request.on('login', () => {
|
||||||
logInCount++;
|
logInCount++;
|
||||||
|
@ -318,7 +321,7 @@ describe('net module', () => {
|
||||||
request.on('end', () => response.end());
|
request.on('end', () => response.end());
|
||||||
});
|
});
|
||||||
const requestData = randomString(kOneKiloByte);
|
const requestData = randomString(kOneKiloByte);
|
||||||
const request = net.request({ method: 'GET', url: serverUrl });
|
const request = net.request({ method: 'GET', url: serverUrl, ...extraOptions });
|
||||||
request.on('login', (authInfo, cb) => {
|
request.on('login', (authInfo, cb) => {
|
||||||
cb(user, pass);
|
cb(user, pass);
|
||||||
});
|
});
|
||||||
|
@ -328,6 +331,77 @@ describe('net module', () => {
|
||||||
expect(responseData).to.equal(requestData);
|
expect(responseData).to.equal(requestData);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('authentication when {"credentials":"omit"}', () => {
|
||||||
|
it('should not emit the login event when 401', async () => {
|
||||||
|
const serverUrl = await respondOnce.toSingleURL((request, response) => {
|
||||||
|
if (!request.headers.authorization) {
|
||||||
|
return response.writeHead(401, { 'WWW-Authenticate': 'Basic realm="Foo"' }).end();
|
||||||
|
}
|
||||||
|
response.writeHead(200).end('ok');
|
||||||
|
});
|
||||||
|
const request = net.request({ method: 'GET', url: serverUrl, credentials: 'omit' });
|
||||||
|
request.on('login', () => {
|
||||||
|
expect.fail('unexpected login event');
|
||||||
|
});
|
||||||
|
const response = await getResponse(request);
|
||||||
|
expect(response.statusCode).to.equal(401);
|
||||||
|
expect(response.headers['www-authenticate']).to.equal('Basic realm="Foo"');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not share credentials with WebContents', async () => {
|
||||||
|
const [user, pass] = ['user', 'pass'];
|
||||||
|
const serverUrl = await respondNTimes.toSingleURL((request, response) => {
|
||||||
|
if (!request.headers.authorization) {
|
||||||
|
return response.writeHead(401, { 'WWW-Authenticate': 'Basic realm="Foo"' }).end();
|
||||||
|
}
|
||||||
|
return response.writeHead(200).end('ok');
|
||||||
|
}, 2);
|
||||||
|
const bw = new BrowserWindow({ show: false });
|
||||||
|
bw.webContents.on('login', (event, details, authInfo, cb) => {
|
||||||
|
event.preventDefault();
|
||||||
|
cb(user, pass);
|
||||||
|
});
|
||||||
|
await bw.loadURL(serverUrl);
|
||||||
|
bw.close();
|
||||||
|
const request = net.request({ method: 'GET', url: serverUrl, credentials: 'omit' });
|
||||||
|
request.on('login', () => {
|
||||||
|
expect.fail();
|
||||||
|
});
|
||||||
|
const response = await getResponse(request);
|
||||||
|
expect(response.statusCode).to.equal(401);
|
||||||
|
expect(response.headers['www-authenticate']).to.equal('Basic realm="Foo"');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should share proxy credentials with WebContents', async () => {
|
||||||
|
const [user, pass] = ['user', 'pass'];
|
||||||
|
const proxyUrl = await respondNTimes((request, response) => {
|
||||||
|
if (!request.headers['proxy-authorization']) {
|
||||||
|
return response.writeHead(407, { 'Proxy-Authenticate': 'Basic realm="Foo"' }).end();
|
||||||
|
}
|
||||||
|
return response.writeHead(200).end('ok');
|
||||||
|
}, 2);
|
||||||
|
const customSession = session.fromPartition(`net-proxy-test-${Math.random()}`);
|
||||||
|
await customSession.setProxy({ proxyRules: proxyUrl.replace('http://', ''), proxyBypassRules: '<-loopback>' });
|
||||||
|
const bw = new BrowserWindow({ show: false, webPreferences: { session: customSession } });
|
||||||
|
bw.webContents.on('login', (event, details, authInfo, cb) => {
|
||||||
|
event.preventDefault();
|
||||||
|
cb(user, pass);
|
||||||
|
});
|
||||||
|
await bw.loadURL('http://127.0.0.1:9999');
|
||||||
|
bw.close();
|
||||||
|
const request = net.request({ method: 'GET', url: 'http://127.0.0.1:9999', session: customSession, credentials: 'omit' });
|
||||||
|
request.on('login', () => {
|
||||||
|
expect.fail();
|
||||||
|
});
|
||||||
|
const response = await getResponse(request);
|
||||||
|
const body = await collectStreamBody(response);
|
||||||
|
expect(response.statusCode).to.equal(200);
|
||||||
|
expect(body).to.equal('ok');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('ClientRequest API', () => {
|
describe('ClientRequest API', () => {
|
||||||
it('request/response objects should emit expected events', async () => {
|
it('request/response objects should emit expected events', async () => {
|
||||||
|
@ -466,7 +540,7 @@ describe('net module', () => {
|
||||||
it('should be able to set cookie header line', async () => {
|
it('should be able to set cookie header line', async () => {
|
||||||
const cookieHeaderName = 'Cookie';
|
const cookieHeaderName = 'Cookie';
|
||||||
const cookieHeaderValue = 'test=12345';
|
const cookieHeaderValue = 'test=12345';
|
||||||
const customSession = session.fromPartition('test-cookie-header');
|
const customSession = session.fromPartition(`test-cookie-header-${Math.random()}`);
|
||||||
const serverUrl = await respondOnce.toSingleURL((request, response) => {
|
const serverUrl = await respondOnce.toSingleURL((request, response) => {
|
||||||
expect(request.headers[cookieHeaderName.toLowerCase()]).to.equal(cookieHeaderValue);
|
expect(request.headers[cookieHeaderName.toLowerCase()]).to.equal(cookieHeaderValue);
|
||||||
response.statusCode = 200;
|
response.statusCode = 200;
|
||||||
|
@ -511,7 +585,7 @@ describe('net module', () => {
|
||||||
response.setHeader('x-cookie', `${request.headers.cookie!}`);
|
response.setHeader('x-cookie', `${request.headers.cookie!}`);
|
||||||
response.end();
|
response.end();
|
||||||
});
|
});
|
||||||
const sess = session.fromPartition('cookie-tests-1');
|
const sess = session.fromPartition(`cookie-tests-${Math.random()}`);
|
||||||
const cookieVal = `${Date.now()}`;
|
const cookieVal = `${Date.now()}`;
|
||||||
await sess.cookies.set({
|
await sess.cookies.set({
|
||||||
url: serverUrl,
|
url: serverUrl,
|
||||||
|
@ -526,6 +600,8 @@ describe('net module', () => {
|
||||||
expect(response.headers['x-cookie']).to.equal('undefined');
|
expect(response.headers['x-cookie']).to.equal('undefined');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
for (const extraOptions of [{ useSessionCookies: true }, { credentials: 'include' }] as ClientRequestConstructorOptions[]) {
|
||||||
|
describe(`when ${JSON.stringify(extraOptions)}`, () => {
|
||||||
it('should be able to use the sessions cookie store', async () => {
|
it('should be able to use the sessions cookie store', async () => {
|
||||||
const serverUrl = await respondOnce.toSingleURL((request, response) => {
|
const serverUrl = await respondOnce.toSingleURL((request, response) => {
|
||||||
response.statusCode = 200;
|
response.statusCode = 200;
|
||||||
|
@ -533,7 +609,7 @@ describe('net module', () => {
|
||||||
response.setHeader('x-cookie', request.headers.cookie!);
|
response.setHeader('x-cookie', request.headers.cookie!);
|
||||||
response.end();
|
response.end();
|
||||||
});
|
});
|
||||||
const sess = session.fromPartition('cookie-tests-2');
|
const sess = session.fromPartition(`cookie-tests-${Math.random()}`);
|
||||||
const cookieVal = `${Date.now()}`;
|
const cookieVal = `${Date.now()}`;
|
||||||
await sess.cookies.set({
|
await sess.cookies.set({
|
||||||
url: serverUrl,
|
url: serverUrl,
|
||||||
|
@ -543,7 +619,7 @@ describe('net module', () => {
|
||||||
const urlRequest = net.request({
|
const urlRequest = net.request({
|
||||||
url: serverUrl,
|
url: serverUrl,
|
||||||
session: sess,
|
session: sess,
|
||||||
useSessionCookies: true
|
...extraOptions
|
||||||
});
|
});
|
||||||
const response = await getResponse(urlRequest);
|
const response = await getResponse(urlRequest);
|
||||||
expect(response.headers['x-cookie']).to.equal(`wild_cookie=${cookieVal}`);
|
expect(response.headers['x-cookie']).to.equal(`wild_cookie=${cookieVal}`);
|
||||||
|
@ -556,13 +632,13 @@ describe('net module', () => {
|
||||||
response.setHeader('set-cookie', 'foo=bar');
|
response.setHeader('set-cookie', 'foo=bar');
|
||||||
response.end();
|
response.end();
|
||||||
});
|
});
|
||||||
const sess = session.fromPartition('cookie-tests-3');
|
const sess = session.fromPartition(`cookie-tests-${Math.random()}`);
|
||||||
let cookies = await sess.cookies.get({});
|
let cookies = await sess.cookies.get({});
|
||||||
expect(cookies).to.have.lengthOf(0);
|
expect(cookies).to.have.lengthOf(0);
|
||||||
const urlRequest = net.request({
|
const urlRequest = net.request({
|
||||||
url: serverUrl,
|
url: serverUrl,
|
||||||
session: sess,
|
session: sess,
|
||||||
useSessionCookies: true
|
...extraOptions
|
||||||
});
|
});
|
||||||
await collectStreamBody(await getResponse(urlRequest));
|
await collectStreamBody(await getResponse(urlRequest));
|
||||||
cookies = await sess.cookies.get({});
|
cookies = await sess.cookies.get({});
|
||||||
|
@ -589,13 +665,13 @@ describe('net module', () => {
|
||||||
response.setHeader('x-cookie', `${request.headers.cookie}`);
|
response.setHeader('x-cookie', `${request.headers.cookie}`);
|
||||||
response.end();
|
response.end();
|
||||||
}, 2);
|
}, 2);
|
||||||
const sess = session.fromPartition(`cookie-tests-same-site-${mode}`);
|
const sess = session.fromPartition(`cookie-tests-${Math.random()}`);
|
||||||
let cookies = await sess.cookies.get({});
|
let cookies = await sess.cookies.get({});
|
||||||
expect(cookies).to.have.lengthOf(0);
|
expect(cookies).to.have.lengthOf(0);
|
||||||
const urlRequest = net.request({
|
const urlRequest = net.request({
|
||||||
url: serverUrl,
|
url: serverUrl,
|
||||||
session: sess,
|
session: sess,
|
||||||
useSessionCookies: true
|
...extraOptions
|
||||||
});
|
});
|
||||||
const response = await getResponse(urlRequest);
|
const response = await getResponse(urlRequest);
|
||||||
expect(response.headers['x-cookie']).to.equal('undefined');
|
expect(response.headers['x-cookie']).to.equal('undefined');
|
||||||
|
@ -616,7 +692,7 @@ describe('net module', () => {
|
||||||
const urlRequest2 = net.request({
|
const urlRequest2 = net.request({
|
||||||
url: serverUrl,
|
url: serverUrl,
|
||||||
session: sess,
|
session: sess,
|
||||||
useSessionCookies: true
|
...extraOptions
|
||||||
});
|
});
|
||||||
const response2 = await getResponse(urlRequest2);
|
const response2 = await getResponse(urlRequest2);
|
||||||
expect(response2.headers['x-cookie']).to.equal('same=site');
|
expect(response2.headers['x-cookie']).to.equal('same=site');
|
||||||
|
@ -637,7 +713,7 @@ describe('net module', () => {
|
||||||
response.setHeader('location', newUrl.replace('127.0.0.1', 'localhost'));
|
response.setHeader('location', newUrl.replace('127.0.0.1', 'localhost'));
|
||||||
response.end();
|
response.end();
|
||||||
});
|
});
|
||||||
const sess = session.fromPartition('cookie-tests-4');
|
const sess = session.fromPartition(`cookie-tests-${Math.random()}`);
|
||||||
const cookie127Val = `${Date.now()}-127`;
|
const cookie127Val = `${Date.now()}-127`;
|
||||||
const cookieLocalVal = `${Date.now()}-local`;
|
const cookieLocalVal = `${Date.now()}-local`;
|
||||||
const localhostUrl = serverUrl.replace('127.0.0.1', 'localhost');
|
const localhostUrl = serverUrl.replace('127.0.0.1', 'localhost');
|
||||||
|
@ -656,7 +732,7 @@ describe('net module', () => {
|
||||||
const urlRequest = net.request({
|
const urlRequest = net.request({
|
||||||
url: serverUrl,
|
url: serverUrl,
|
||||||
session: sess,
|
session: sess,
|
||||||
useSessionCookies: true
|
...extraOptions
|
||||||
});
|
});
|
||||||
urlRequest.on('redirect', (status, method, url, headers) => {
|
urlRequest.on('redirect', (status, method, url, headers) => {
|
||||||
// The initial redirect response should have received the 127 value here
|
// The initial redirect response should have received the 127 value here
|
||||||
|
@ -672,6 +748,13 @@ describe('net module', () => {
|
||||||
// and attach the cookies for the new target domain
|
// and attach the cookies for the new target domain
|
||||||
expect(response.headers['x-cookie']).to.equal(`wild_cookie=${cookieLocalVal}`);
|
expect(response.headers['x-cookie']).to.equal(`wild_cookie=${cookieLocalVal}`);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('when {"credentials":"omit"}', () => {
|
||||||
|
it('should not send cookies');
|
||||||
|
it('should not store cookies');
|
||||||
|
});
|
||||||
|
|
||||||
it('should be able to abort an HTTP request before first write', async () => {
|
it('should be able to abort an HTTP request before first write', async () => {
|
||||||
const serverUrl = await respondOnce.toSingleURL((request, response) => {
|
const serverUrl = await respondOnce.toSingleURL((request, response) => {
|
||||||
|
@ -916,7 +999,7 @@ describe('net module', () => {
|
||||||
it('should to able to create and intercept a request using a custom session object', async () => {
|
it('should to able to create and intercept a request using a custom session object', async () => {
|
||||||
const requestUrl = '/requestUrl';
|
const requestUrl = '/requestUrl';
|
||||||
const redirectUrl = '/redirectUrl';
|
const redirectUrl = '/redirectUrl';
|
||||||
const customPartitionName = 'custom-partition';
|
const customPartitionName = `custom-partition-${Math.random()}`;
|
||||||
let requestIsRedirected = false;
|
let requestIsRedirected = false;
|
||||||
const serverUrl = await respondOnce.toURL(redirectUrl, (request, response) => {
|
const serverUrl = await respondOnce.toURL(redirectUrl, (request, response) => {
|
||||||
requestIsRedirected = true;
|
requestIsRedirected = true;
|
||||||
|
@ -957,7 +1040,7 @@ describe('net module', () => {
|
||||||
it('should to able to create and intercept a request using a custom partition name', async () => {
|
it('should to able to create and intercept a request using a custom partition name', async () => {
|
||||||
const requestUrl = '/requestUrl';
|
const requestUrl = '/requestUrl';
|
||||||
const redirectUrl = '/redirectUrl';
|
const redirectUrl = '/redirectUrl';
|
||||||
const customPartitionName = 'custom-partition';
|
const customPartitionName = `custom-partition-${Math.random()}`;
|
||||||
let requestIsRedirected = false;
|
let requestIsRedirected = false;
|
||||||
const serverUrl = await respondOnce.toURL(redirectUrl, (request, response) => {
|
const serverUrl = await respondOnce.toURL(redirectUrl, (request, response) => {
|
||||||
requestIsRedirected = true;
|
requestIsRedirected = true;
|
||||||
|
|
1
typings/internal-ambient.d.ts
vendored
1
typings/internal-ambient.d.ts
vendored
|
@ -94,6 +94,7 @@ declare namespace NodeJS {
|
||||||
url: string;
|
url: string;
|
||||||
extraHeaders?: Record<string, string>;
|
extraHeaders?: Record<string, string>;
|
||||||
useSessionCookies?: boolean;
|
useSessionCookies?: boolean;
|
||||||
|
credentials?: 'include' | 'omit';
|
||||||
body: Uint8Array | BodyFunc;
|
body: Uint8Array | BodyFunc;
|
||||||
session?: Electron.Session;
|
session?: Electron.Session;
|
||||||
partition?: string;
|
partition?: string;
|
||||||
|
|
Loading…
Reference in a new issue