api-net-session-spec.ts 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642
  1. import { expect } from 'chai';
  2. import * as dns from 'node:dns';
  3. import { net, session, BrowserWindow, ClientRequestConstructorOptions } from 'electron/main';
  4. import { collectStreamBody, getResponse, respondNTimes, respondOnce } from './lib/net-helpers';
  5. // See https://github.com/nodejs/node/issues/40702.
  6. dns.setDefaultResultOrder('ipv4first');
  7. describe('net module (session)', () => {
  8. beforeEach(() => {
  9. respondNTimes.routeFailure = false;
  10. });
  11. afterEach(async function () {
  12. await session.defaultSession.clearCache();
  13. if (respondNTimes.routeFailure && this.test) {
  14. if (!this.test.isFailed()) {
  15. throw new Error('Failing this test due an unhandled error in the respondOnce route handler, check the logs above for the actual error');
  16. }
  17. }
  18. });
  19. describe('HTTP basics', () => {
  20. for (const extraOptions of [{}, { credentials: 'include' }, { useSessionCookies: false, credentials: 'include' }] as ClientRequestConstructorOptions[]) {
  21. describe(`authentication when ${JSON.stringify(extraOptions)}`, () => {
  22. it('should share credentials with WebContents', async () => {
  23. const [user, pass] = ['user', 'pass'];
  24. const serverUrl = await respondNTimes.toSingleURL((request, response) => {
  25. if (!request.headers.authorization) {
  26. return response.writeHead(401, { 'WWW-Authenticate': 'Basic realm="Foo"' }).end();
  27. }
  28. return response.writeHead(200).end('ok');
  29. }, 2);
  30. const bw = new BrowserWindow({ show: false });
  31. bw.webContents.on('login', (event, details, authInfo, cb) => {
  32. event.preventDefault();
  33. cb(user, pass);
  34. });
  35. await bw.loadURL(serverUrl);
  36. bw.close();
  37. const request = net.request({ method: 'GET', url: serverUrl, ...extraOptions });
  38. let logInCount = 0;
  39. request.on('login', () => {
  40. logInCount++;
  41. });
  42. const response = await getResponse(request);
  43. await collectStreamBody(response);
  44. expect(logInCount).to.equal(0, 'should not receive a login event, credentials should be cached');
  45. });
  46. it('should share proxy credentials with WebContents', async () => {
  47. const [user, pass] = ['user', 'pass'];
  48. const proxyUrl = await respondNTimes((request, response) => {
  49. if (!request.headers['proxy-authorization']) {
  50. return response.writeHead(407, { 'Proxy-Authenticate': 'Basic realm="Foo"' }).end();
  51. }
  52. return response.writeHead(200).end('ok');
  53. }, 2);
  54. const customSession = session.fromPartition(`net-proxy-test-${Math.random()}`);
  55. await customSession.setProxy({ proxyRules: proxyUrl.replace('http://', ''), proxyBypassRules: '<-loopback>' });
  56. const bw = new BrowserWindow({ show: false, webPreferences: { session: customSession } });
  57. bw.webContents.on('login', (event, details, authInfo, cb) => {
  58. event.preventDefault();
  59. cb(user, pass);
  60. });
  61. await bw.loadURL('http://127.0.0.1:9999');
  62. bw.close();
  63. const request = net.request({ method: 'GET', url: 'http://127.0.0.1:9999', session: customSession, ...extraOptions });
  64. let logInCount = 0;
  65. request.on('login', () => {
  66. logInCount++;
  67. });
  68. const response = await getResponse(request);
  69. const body = await collectStreamBody(response);
  70. expect(response.statusCode).to.equal(200);
  71. expect(body).to.equal('ok');
  72. expect(logInCount).to.equal(0, 'should not receive a login event, credentials should be cached');
  73. });
  74. });
  75. }
  76. describe('authentication when {"credentials":"omit"}', () => {
  77. it('should not share credentials with WebContents', async () => {
  78. const [user, pass] = ['user', 'pass'];
  79. const serverUrl = await respondNTimes.toSingleURL((request, response) => {
  80. if (!request.headers.authorization) {
  81. return response.writeHead(401, { 'WWW-Authenticate': 'Basic realm="Foo"' }).end();
  82. }
  83. return response.writeHead(200).end('ok');
  84. }, 2);
  85. const bw = new BrowserWindow({ show: false });
  86. bw.webContents.on('login', (event, details, authInfo, cb) => {
  87. event.preventDefault();
  88. cb(user, pass);
  89. });
  90. await bw.loadURL(serverUrl);
  91. bw.close();
  92. const request = net.request({ method: 'GET', url: serverUrl, credentials: 'omit' });
  93. request.on('login', () => {
  94. expect.fail();
  95. });
  96. const response = await getResponse(request);
  97. expect(response.statusCode).to.equal(401);
  98. expect(response.headers['www-authenticate']).to.equal('Basic realm="Foo"');
  99. });
  100. it('should share proxy credentials with WebContents', async () => {
  101. const [user, pass] = ['user', 'pass'];
  102. const proxyUrl = await respondNTimes((request, response) => {
  103. if (!request.headers['proxy-authorization']) {
  104. return response.writeHead(407, { 'Proxy-Authenticate': 'Basic realm="Foo"' }).end();
  105. }
  106. return response.writeHead(200).end('ok');
  107. }, 2);
  108. const customSession = session.fromPartition(`net-proxy-test-${Math.random()}`);
  109. await customSession.setProxy({ proxyRules: proxyUrl.replace('http://', ''), proxyBypassRules: '<-loopback>' });
  110. const bw = new BrowserWindow({ show: false, webPreferences: { session: customSession } });
  111. bw.webContents.on('login', (event, details, authInfo, cb) => {
  112. event.preventDefault();
  113. cb(user, pass);
  114. });
  115. await bw.loadURL('http://127.0.0.1:9999');
  116. bw.close();
  117. const request = net.request({ method: 'GET', url: 'http://127.0.0.1:9999', session: customSession, credentials: 'omit' });
  118. request.on('login', () => {
  119. expect.fail();
  120. });
  121. const response = await getResponse(request);
  122. const body = await collectStreamBody(response);
  123. expect(response.statusCode).to.equal(200);
  124. expect(body).to.equal('ok');
  125. });
  126. });
  127. });
  128. describe('ClientRequest API', () => {
  129. it('should be able to set cookie header line', async () => {
  130. const cookieHeaderName = 'Cookie';
  131. const cookieHeaderValue = 'test=12345';
  132. const customSession = session.fromPartition(`test-cookie-header-${Math.random()}`);
  133. const serverUrl = await respondOnce.toSingleURL((request, response) => {
  134. expect(request.headers[cookieHeaderName.toLowerCase()]).to.equal(cookieHeaderValue);
  135. response.statusCode = 200;
  136. response.statusMessage = 'OK';
  137. response.end();
  138. });
  139. await customSession.cookies.set({
  140. url: `${serverUrl}`,
  141. name: 'test',
  142. value: '11111',
  143. expirationDate: 0
  144. });
  145. const urlRequest = net.request({
  146. method: 'GET',
  147. url: serverUrl,
  148. session: customSession
  149. });
  150. urlRequest.setHeader(cookieHeaderName, cookieHeaderValue);
  151. expect(urlRequest.getHeader(cookieHeaderName)).to.equal(cookieHeaderValue);
  152. const response = await getResponse(urlRequest);
  153. expect(response.statusCode).to.equal(200);
  154. await collectStreamBody(response);
  155. });
  156. it('should not use the sessions cookie store by default', async () => {
  157. const serverUrl = await respondOnce.toSingleURL((request, response) => {
  158. response.statusCode = 200;
  159. response.statusMessage = 'OK';
  160. response.setHeader('x-cookie', `${request.headers.cookie!}`);
  161. response.end();
  162. });
  163. const sess = session.fromPartition(`cookie-tests-${Math.random()}`);
  164. const cookieVal = `${Date.now()}`;
  165. await sess.cookies.set({
  166. url: serverUrl,
  167. name: 'wild_cookie',
  168. value: cookieVal
  169. });
  170. const urlRequest = net.request({
  171. url: serverUrl,
  172. session: sess
  173. });
  174. const response = await getResponse(urlRequest);
  175. expect(response.headers['x-cookie']).to.equal('undefined');
  176. });
  177. for (const extraOptions of [{ useSessionCookies: true }, { credentials: 'include' }] as ClientRequestConstructorOptions[]) {
  178. describe(`when ${JSON.stringify(extraOptions)}`, () => {
  179. it('should be able to use the sessions cookie store', async () => {
  180. const serverUrl = await respondOnce.toSingleURL((request, response) => {
  181. response.statusCode = 200;
  182. response.statusMessage = 'OK';
  183. response.setHeader('x-cookie', request.headers.cookie!);
  184. response.end();
  185. });
  186. const sess = session.fromPartition(`cookie-tests-${Math.random()}`);
  187. const cookieVal = `${Date.now()}`;
  188. await sess.cookies.set({
  189. url: serverUrl,
  190. name: 'wild_cookie',
  191. value: cookieVal
  192. });
  193. const urlRequest = net.request({
  194. url: serverUrl,
  195. session: sess,
  196. ...extraOptions
  197. });
  198. const response = await getResponse(urlRequest);
  199. expect(response.headers['x-cookie']).to.equal(`wild_cookie=${cookieVal}`);
  200. });
  201. it('should be able to use the sessions cookie store with set-cookie', async () => {
  202. const serverUrl = await respondOnce.toSingleURL((request, response) => {
  203. response.statusCode = 200;
  204. response.statusMessage = 'OK';
  205. response.setHeader('set-cookie', 'foo=bar');
  206. response.end();
  207. });
  208. const sess = session.fromPartition(`cookie-tests-${Math.random()}`);
  209. let cookies = await sess.cookies.get({});
  210. expect(cookies).to.have.lengthOf(0);
  211. const urlRequest = net.request({
  212. url: serverUrl,
  213. session: sess,
  214. ...extraOptions
  215. });
  216. await collectStreamBody(await getResponse(urlRequest));
  217. cookies = await sess.cookies.get({});
  218. expect(cookies).to.have.lengthOf(1);
  219. expect(cookies[0]).to.deep.equal({
  220. name: 'foo',
  221. value: 'bar',
  222. domain: '127.0.0.1',
  223. hostOnly: true,
  224. path: '/',
  225. secure: false,
  226. httpOnly: false,
  227. session: true,
  228. sameSite: 'unspecified'
  229. });
  230. });
  231. for (const mode of ['Lax', 'Strict']) {
  232. it(`should be able to use the sessions cookie store with same-site ${mode} cookies`, async () => {
  233. const serverUrl = await respondNTimes.toSingleURL((request, response) => {
  234. response.statusCode = 200;
  235. response.statusMessage = 'OK';
  236. response.setHeader('set-cookie', `same=site; SameSite=${mode}`);
  237. response.setHeader('x-cookie', `${request.headers.cookie}`);
  238. response.end();
  239. }, 2);
  240. const sess = session.fromPartition(`cookie-tests-${Math.random()}`);
  241. let cookies = await sess.cookies.get({});
  242. expect(cookies).to.have.lengthOf(0);
  243. const urlRequest = net.request({
  244. url: serverUrl,
  245. session: sess,
  246. ...extraOptions
  247. });
  248. const response = await getResponse(urlRequest);
  249. expect(response.headers['x-cookie']).to.equal('undefined');
  250. await collectStreamBody(response);
  251. cookies = await sess.cookies.get({});
  252. expect(cookies).to.have.lengthOf(1);
  253. expect(cookies[0]).to.deep.equal({
  254. name: 'same',
  255. value: 'site',
  256. domain: '127.0.0.1',
  257. hostOnly: true,
  258. path: '/',
  259. secure: false,
  260. httpOnly: false,
  261. session: true,
  262. sameSite: mode.toLowerCase()
  263. });
  264. const urlRequest2 = net.request({
  265. url: serverUrl,
  266. session: sess,
  267. ...extraOptions
  268. });
  269. const response2 = await getResponse(urlRequest2);
  270. expect(response2.headers['x-cookie']).to.equal('same=site');
  271. });
  272. }
  273. it('should be able to use the sessions cookie store safely across redirects', async () => {
  274. const serverUrl = await respondOnce.toSingleURL(async (request, response) => {
  275. response.statusCode = 302;
  276. response.statusMessage = 'Moved';
  277. const newUrl = await respondOnce.toSingleURL((req, res) => {
  278. res.statusCode = 200;
  279. res.statusMessage = 'OK';
  280. res.setHeader('x-cookie', req.headers.cookie!);
  281. res.end();
  282. });
  283. response.setHeader('x-cookie', request.headers.cookie!);
  284. response.setHeader('location', newUrl.replace('127.0.0.1', 'localhost'));
  285. response.end();
  286. });
  287. const sess = session.fromPartition(`cookie-tests-${Math.random()}`);
  288. const cookie127Val = `${Date.now()}-127`;
  289. const cookieLocalVal = `${Date.now()}-local`;
  290. const localhostUrl = serverUrl.replace('127.0.0.1', 'localhost');
  291. expect(localhostUrl).to.not.equal(serverUrl);
  292. // cookies with lax or strict same-site settings will not
  293. // persist after redirects. no_restriction must be used
  294. await Promise.all([
  295. sess.cookies.set({
  296. url: serverUrl,
  297. name: 'wild_cookie',
  298. sameSite: 'no_restriction',
  299. value: cookie127Val
  300. }), sess.cookies.set({
  301. url: localhostUrl,
  302. name: 'wild_cookie',
  303. sameSite: 'no_restriction',
  304. value: cookieLocalVal
  305. })
  306. ]);
  307. const urlRequest = net.request({
  308. url: serverUrl,
  309. session: sess,
  310. ...extraOptions
  311. });
  312. urlRequest.on('redirect', (status, method, url, headers) => {
  313. // The initial redirect response should have received the 127 value here
  314. expect(headers['x-cookie'][0]).to.equal(`wild_cookie=${cookie127Val}`);
  315. urlRequest.followRedirect();
  316. });
  317. const response = await getResponse(urlRequest);
  318. // We expect the server to have received the localhost value here
  319. // The original request was to a 127.0.0.1 URL
  320. // That request would have the cookie127Val cookie attached
  321. // The request is then redirect to a localhost URL (different site)
  322. // Because we are using the session cookie store it should do the safe / secure thing
  323. // and attach the cookies for the new target domain
  324. expect(response.headers['x-cookie']).to.equal(`wild_cookie=${cookieLocalVal}`);
  325. });
  326. });
  327. }
  328. it('should be able correctly filter out cookies that are secure', async () => {
  329. const sess = session.fromPartition(`cookie-tests-${Math.random()}`);
  330. await Promise.all([
  331. sess.cookies.set({
  332. url: 'https://electronjs.org',
  333. domain: 'electronjs.org',
  334. name: 'cookie1',
  335. value: '1',
  336. secure: true
  337. }),
  338. sess.cookies.set({
  339. url: 'https://electronjs.org',
  340. domain: 'electronjs.org',
  341. name: 'cookie2',
  342. value: '2',
  343. secure: false
  344. })
  345. ]);
  346. const secureCookies = await sess.cookies.get({
  347. secure: true
  348. });
  349. expect(secureCookies).to.have.lengthOf(1);
  350. expect(secureCookies[0].name).to.equal('cookie1');
  351. const cookies = await sess.cookies.get({
  352. secure: false
  353. });
  354. expect(cookies).to.have.lengthOf(1);
  355. expect(cookies[0].name).to.equal('cookie2');
  356. });
  357. it('throws when an invalid domain is passed', async () => {
  358. const sess = session.fromPartition(`cookie-tests-${Math.random()}`);
  359. await expect(sess.cookies.set({
  360. url: 'https://electronjs.org',
  361. domain: 'wssss.iamabaddomain.fun',
  362. name: 'cookie1'
  363. })).to.eventually.be.rejectedWith(/Failed to set cookie with an invalid domain attribute/);
  364. });
  365. it('should be able correctly filter out cookies that are session', async () => {
  366. const sess = session.fromPartition(`cookie-tests-${Math.random()}`);
  367. await Promise.all([
  368. sess.cookies.set({
  369. url: 'https://electronjs.org',
  370. domain: 'electronjs.org',
  371. name: 'cookie1',
  372. value: '1'
  373. }),
  374. sess.cookies.set({
  375. url: 'https://electronjs.org',
  376. domain: 'electronjs.org',
  377. name: 'cookie2',
  378. value: '2',
  379. expirationDate: Math.round(Date.now() / 1000) + 10000
  380. })
  381. ]);
  382. const sessionCookies = await sess.cookies.get({
  383. session: true
  384. });
  385. expect(sessionCookies).to.have.lengthOf(1);
  386. expect(sessionCookies[0].name).to.equal('cookie1');
  387. const cookies = await sess.cookies.get({
  388. session: false
  389. });
  390. expect(cookies).to.have.lengthOf(1);
  391. expect(cookies[0].name).to.equal('cookie2');
  392. });
  393. it('should be able correctly filter out cookies that are httpOnly', async () => {
  394. const sess = session.fromPartition(`cookie-tests-${Math.random()}`);
  395. await Promise.all([
  396. sess.cookies.set({
  397. url: 'https://electronjs.org',
  398. domain: 'electronjs.org',
  399. name: 'cookie1',
  400. value: '1',
  401. httpOnly: true
  402. }),
  403. sess.cookies.set({
  404. url: 'https://electronjs.org',
  405. domain: 'electronjs.org',
  406. name: 'cookie2',
  407. value: '2',
  408. httpOnly: false
  409. })
  410. ]);
  411. const httpOnlyCookies = await sess.cookies.get({
  412. httpOnly: true
  413. });
  414. expect(httpOnlyCookies).to.have.lengthOf(1);
  415. expect(httpOnlyCookies[0].name).to.equal('cookie1');
  416. const cookies = await sess.cookies.get({
  417. httpOnly: false
  418. });
  419. expect(cookies).to.have.lengthOf(1);
  420. expect(cookies[0].name).to.equal('cookie2');
  421. });
  422. describe('webRequest', () => {
  423. afterEach(() => {
  424. session.defaultSession.webRequest.onBeforeRequest(null);
  425. });
  426. it('Should throw when invalid filters are passed', () => {
  427. expect(() => {
  428. session.defaultSession.webRequest.onBeforeRequest(
  429. { urls: ['*://www.googleapis.com'] },
  430. (details, callback) => { callback({ cancel: false }); }
  431. );
  432. }).to.throw('Invalid url pattern *://www.googleapis.com: Empty path.');
  433. expect(() => {
  434. session.defaultSession.webRequest.onBeforeRequest(
  435. { urls: ['*://www.googleapis.com/', '*://blahblah.dev'] },
  436. (details, callback) => { callback({ cancel: false }); }
  437. );
  438. }).to.throw('Invalid url pattern *://blahblah.dev: Empty path.');
  439. });
  440. it('Should not throw when valid filters are passed', () => {
  441. expect(() => {
  442. session.defaultSession.webRequest.onBeforeRequest(
  443. { urls: ['*://www.googleapis.com/'] },
  444. (details, callback) => { callback({ cancel: false }); }
  445. );
  446. }).to.not.throw();
  447. });
  448. it('Requests should be intercepted by webRequest module', async () => {
  449. const requestUrl = '/requestUrl';
  450. const redirectUrl = '/redirectUrl';
  451. let requestIsRedirected = false;
  452. const serverUrl = await respondOnce.toURL(redirectUrl, (request, response) => {
  453. requestIsRedirected = true;
  454. response.end();
  455. });
  456. let requestIsIntercepted = false;
  457. session.defaultSession.webRequest.onBeforeRequest(
  458. (details, callback) => {
  459. if (details.url === `${serverUrl}${requestUrl}`) {
  460. requestIsIntercepted = true;
  461. callback({
  462. redirectURL: `${serverUrl}${redirectUrl}`
  463. });
  464. } else {
  465. callback({
  466. cancel: false
  467. });
  468. }
  469. });
  470. const urlRequest = net.request(`${serverUrl}${requestUrl}`);
  471. const response = await getResponse(urlRequest);
  472. expect(response.statusCode).to.equal(200);
  473. await collectStreamBody(response);
  474. expect(requestIsRedirected).to.be.true('The server should receive a request to the forward URL');
  475. expect(requestIsIntercepted).to.be.true('The request should be intercepted by the webRequest module');
  476. });
  477. it('should to able to create and intercept a request using a custom session object', async () => {
  478. const requestUrl = '/requestUrl';
  479. const redirectUrl = '/redirectUrl';
  480. const customPartitionName = `custom-partition-${Math.random()}`;
  481. let requestIsRedirected = false;
  482. const serverUrl = await respondOnce.toURL(redirectUrl, (request, response) => {
  483. requestIsRedirected = true;
  484. response.end();
  485. });
  486. session.defaultSession.webRequest.onBeforeRequest(() => {
  487. expect.fail('Request should not be intercepted by the default session');
  488. });
  489. const customSession = session.fromPartition(customPartitionName, { cache: false });
  490. let requestIsIntercepted = false;
  491. customSession.webRequest.onBeforeRequest((details, callback) => {
  492. if (details.url === `${serverUrl}${requestUrl}`) {
  493. requestIsIntercepted = true;
  494. callback({
  495. redirectURL: `${serverUrl}${redirectUrl}`
  496. });
  497. } else {
  498. callback({
  499. cancel: false
  500. });
  501. }
  502. });
  503. const urlRequest = net.request({
  504. url: `${serverUrl}${requestUrl}`,
  505. session: customSession
  506. });
  507. const response = await getResponse(urlRequest);
  508. expect(response.statusCode).to.equal(200);
  509. await collectStreamBody(response);
  510. expect(requestIsRedirected).to.be.true('The server should receive a request to the forward URL');
  511. expect(requestIsIntercepted).to.be.true('The request should be intercepted by the webRequest module');
  512. });
  513. it('should to able to create and intercept a request using a custom partition name', async () => {
  514. const requestUrl = '/requestUrl';
  515. const redirectUrl = '/redirectUrl';
  516. const customPartitionName = `custom-partition-${Math.random()}`;
  517. let requestIsRedirected = false;
  518. const serverUrl = await respondOnce.toURL(redirectUrl, (request, response) => {
  519. requestIsRedirected = true;
  520. response.end();
  521. });
  522. session.defaultSession.webRequest.onBeforeRequest(() => {
  523. expect.fail('Request should not be intercepted by the default session');
  524. });
  525. const customSession = session.fromPartition(customPartitionName, { cache: false });
  526. let requestIsIntercepted = false;
  527. customSession.webRequest.onBeforeRequest((details, callback) => {
  528. if (details.url === `${serverUrl}${requestUrl}`) {
  529. requestIsIntercepted = true;
  530. callback({
  531. redirectURL: `${serverUrl}${redirectUrl}`
  532. });
  533. } else {
  534. callback({
  535. cancel: false
  536. });
  537. }
  538. });
  539. const urlRequest = net.request({
  540. url: `${serverUrl}${requestUrl}`,
  541. partition: customPartitionName
  542. });
  543. const response = await getResponse(urlRequest);
  544. expect(response.statusCode).to.equal(200);
  545. await collectStreamBody(response);
  546. expect(requestIsRedirected).to.be.true('The server should receive a request to the forward URL');
  547. expect(requestIsIntercepted).to.be.true('The request should be intercepted by the webRequest module');
  548. });
  549. it('triggers webRequest handlers when bypassCustomProtocolHandlers', async () => {
  550. let webRequestDetails: Electron.OnBeforeRequestListenerDetails | null = null;
  551. const serverUrl = await respondOnce.toSingleURL((req, res) => res.end('hi'));
  552. session.defaultSession.webRequest.onBeforeRequest((details, cb) => {
  553. webRequestDetails = details;
  554. cb({});
  555. });
  556. const body = await net.fetch(serverUrl, { bypassCustomProtocolHandlers: true }).then(r => r.text());
  557. expect(body).to.equal('hi');
  558. expect(webRequestDetails).to.have.property('url', serverUrl);
  559. });
  560. });
  561. it('should throw if given an invalid session option', () => {
  562. expect(() => {
  563. net.request({
  564. url: 'https://foo',
  565. session: 1 as any
  566. });
  567. }).to.throw('`session` should be an instance of the Session class');
  568. });
  569. it('should throw if given an invalid partition option', () => {
  570. expect(() => {
  571. net.request({
  572. url: 'https://foo',
  573. partition: 1 as any
  574. });
  575. }).to.throw('`partition` should be a string');
  576. });
  577. });
  578. describe('net.fetch', () => {
  579. it('should be able to use a session cookie store', async () => {
  580. const serverUrl = await respondOnce.toSingleURL((request, response) => {
  581. response.statusCode = 200;
  582. response.statusMessage = 'OK';
  583. response.setHeader('x-cookie', request.headers.cookie!);
  584. response.end();
  585. });
  586. const sess = session.fromPartition(`cookie-tests-${Math.random()}`);
  587. const cookieVal = `${Date.now()}`;
  588. await sess.cookies.set({
  589. url: serverUrl,
  590. name: 'wild_cookie',
  591. value: cookieVal
  592. });
  593. const response = await sess.fetch(serverUrl, {
  594. credentials: 'include'
  595. });
  596. expect(response.headers.get('x-cookie')).to.equal(`wild_cookie=${cookieVal}`);
  597. });
  598. });
  599. });