const assert = require('node:assert/strict') const { createServer } = require('node:http') const { app, net, session } = require('electron') const { mkdtemp, rm } = require('node:fs/promises') const { join } = require('node:path') const { tmpdir } = require('node:os') const { fetchNetwork } = require('../dist-electron/electron/network-fetch.js') const { PackageStore } = require('../dist-electron/electron/package-store.js') const { buildScreenPackage } = require('../dist-electron/electron/screen-package.js') const { prepareFetchHeaders } = require('../dist-electron/electron/response-headers.js') app.whenReady().then(async () => { prepareFetchHeaders(session.defaultSession) const requests = [] const bytes = Buffer.from('RIFFtest-WAVE') const directory = await mkdtemp(join(tmpdir(), 'screen-network-')) const server = createServer((request, response) => { requests.push(request.url) const headers = { 'X-Name': Buffer.from('пример').toString('latin1') } const path = new URL(request.url, 'http://localhost').pathname assert.equal(request.headers.authorization, undefined) assert.equal(request.headers.cookie, undefined) if (path === '/api/echo') { const parts = [] request.on('data', (chunk) => parts.push(chunk)) request.on('end', () => { response.writeHead(200, { 'Content-Type': 'application/json' }) response.end(Buffer.concat(parts)) }) } else if (path === '/redirect') { response.writeHead(302, { ...headers, Location: `/${encodeURIComponent('пример')}` }) response.end() } else if (path === '/absolute') { response.writeHead(307, { Location: `http://127.0.0.1:${server.address().port}/file.wav` }) response.end() } else if (path === '/unsafe') { response.writeHead(302, { Location: 'file:///tmp/secret.wav' }) response.end() } else if (path === '/waiting') { return } else if (path === '/stream') { response.writeHead(200, { 'Content-Type': 'audio/wav' }) response.write(bytes) } else if (path === '/broken') { response.writeHead(200, { 'Content-Type': 'audio/wav' }) response.write(bytes) setTimeout(() => response.destroy(), 20) } else if (path === '/file.wav' || path.startsWith('/%')) { response.writeHead(200, { 'Content-Type': 'audio/wav' }) response.end(bytes) } else if (path === '/status') { response.writeHead(200, Buffer.from('Привет').toString('latin1'), { 'Content-Type': 'text/plain' }) response.end('ok') } else { response.writeHead(200, { ...headers, 'Content-Type': 'text/plain' }) response.end('ok') } }) try { await new Promise((resolve, reject) => { server.once('error', reject) server.listen(0, '127.0.0.1', resolve) }) const url = `http://127.0.0.1:${server.address().port}` const response = await net.fetch(url) assert.equal(response.headers.get('x-name'), encodeURIComponent('пример')) assert.equal(response.headers.get('content-type'), 'text/plain') assert.equal(await response.text(), 'ok') const echo = await fetchNetwork(`${url}/api/echo`, { method: 'POST', body: JSON.stringify({ text: 'Привет' }), headers: { 'Content-Type': 'application/json' }, redirect: 'error', credentials: 'omit' }) assert.deepEqual(await echo.json(), { text: 'Привет' }) const followed = await net.fetch(`${url}/redirect`) assert.deepEqual(Buffer.from(await followed.arrayBuffer()), bytes) const unicodeStatus = await fetchNetwork(`${url}/status`) assert.equal(unicodeStatus.status, 200) assert.equal(await unicodeStatus.text(), 'ok') const manual = await fetchNetwork(`${url}/redirect`, { redirect: 'manual', credentials: 'omit' }) assert.equal(manual.status, 302) assert.ok(manual.headers.get('location').endsWith(encodeURIComponent('пример'))) const store = new PackageStore(directory, { fetch: fetchNetwork, wait: async () => {} }) await store.initialize() const description = (base, name) => buildScreenPackage(base, 7, { game: { game: { ID: 7, name: 'Network smoke', scenario: { story: { introduction: { audio: `${base}/${name}` } } } } }, plan: { plan: { steps: [] } }, music: {} }) for (const name of ['redirect', 'absolute']) { const pkg = await store.download(description(url, name)) assert.equal(pkg.files.length, 1) assert.equal(pkg.files[0].size, bytes.length) } await assert.rejects(store.download(description(url, 'unsafe')), /адрес|редирект/) const aborted = new AbortController() aborted.abort() await assert.rejects(fetchNetwork(`${url}/waiting`, { redirect: 'manual', credentials: 'omit', signal: aborted.signal })) await assert.rejects(fetchNetwork(`${url}/waiting`, { redirect: 'manual', credentials: 'omit', signal: AbortSignal.timeout(30) })) const streamingAbort = new AbortController() const streaming = await fetchNetwork(`${url}/stream`, { redirect: 'manual', credentials: 'omit', signal: streamingAbort.signal }) const reader = streaming.body.getReader() assert.deepEqual(Buffer.from((await reader.read()).value), bytes) streamingAbort.abort() await assert.rejects(reader.read()) const brokenTimeout = AbortSignal.timeout(1000) const broken = await fetchNetwork(`${url}/broken`, { redirect: 'manual', credentials: 'omit', signal: brokenTimeout }) await assert.rejects(broken.arrayBuffer()) assert.equal(brokenTimeout.aborted, false) await session.defaultSession.setProxy({ proxyRules: `http=127.0.0.1:${server.address().port}`, proxyBypassRules: '<-loopback>' }) const proxied = await net.fetch('http://headers-proxy.invalid/example') assert.equal(await proxied.text(), 'ok') assert.ok(requests.includes('http://headers-proxy.invalid/example')) const proxiedPackage = await store.download(description('http://headers-proxy.invalid', 'redirect')) assert.equal(proxiedPackage.files[0].size, bytes.length) assert.ok(requests.includes('http://headers-proxy.invalid/redirect')) await rm(directory, { recursive: true, force: true }) console.log('SCREEN_NETWORK_HEADERS_PASS') app.exit(0) } catch (error) { console.error(error) app.exit(1) } finally { server.close() } })