// Flags: --inspect=0 --experimental-network-inspection 'use strict'; const common = require('../common'); common.skipIfInspectorDisabled(); const { NodeInstance } = require('../common/inspector-helper'); const test = require('node:test'); const assert = require('node:assert'); const path = require('path'); const fs = require('fs'); const resourceUrl = 'http://localhost:3000/app.js'; const resourcePath = path.join(__dirname, '../fixtures/inspector-network-resource/app.js.map'); const resourceText = fs.readFileSync(resourcePath, 'utf8'); const embedPath = resourcePath.replace(/\\/g, '\\\\').replace(/'/g, "\\'"); const script = ` const { NetworkResources } = require('node:inspector'); const fs = require('fs'); NetworkResources.put('${resourceUrl}', fs.readFileSync('${embedPath}', 'utf8')); console.log('Network resource loaded:', '${resourceUrl}'); debugger; `; async function setupSessionAndPauseAtEvalLastLine(script) { const instance = new NodeInstance([ '--inspect-wait=0', '--experimental-inspector-network-resource', ], script); const session = await instance.connectInspectorSession(); await session.send({ method: 'NodeRuntime.enable' }); await session.waitForNotification('NodeRuntime.waitingForDebugger'); await session.send({ method: 'Runtime.enable' }); await session.send({ method: 'Debugger.enable' }); await session.send({ method: 'Runtime.runIfWaitingForDebugger' }); await session.waitForNotification('Debugger.paused'); return { instance, session }; } test('should load and stream a static network resource using loadNetworkResource and IO.read', async () => { const { session } = await setupSessionAndPauseAtEvalLastLine(script); const { resource } = await session.send({ method: 'Network.loadNetworkResource', params: { url: resourceUrl }, }); assert(resource.success, 'Resource should be loaded successfully'); assert(resource.stream, 'Resource should have a stream handle'); let result = await session.send({ method: 'IO.read', params: { handle: resource.stream } }); let data = result.data; let eof = result.eof; let content = ''; while (!eof) { content += data; result = await session.send({ method: 'IO.read', params: { handle: resource.stream } }); data = result.data; eof = result.eof; } content += data; assert.strictEqual(content, resourceText); await session.send({ method: 'IO.close', params: { handle: resource.stream } }); await session.send({ method: 'Debugger.resume' }); await session.waitForDisconnect(); }); test('should return success: false for missing resource', async () => { const { session } = await setupSessionAndPauseAtEvalLastLine(script); const { resource } = await session.send({ method: 'Network.loadNetworkResource', params: { url: 'http://localhost:3000/does-not-exist.js' }, }); assert.strictEqual(resource.success, false); assert(!resource.stream, 'No stream should be returned for missing resource'); await session.send({ method: 'Debugger.resume' }); await session.waitForDisconnect(); }); test('should error or return empty for wrong stream id', async () => { const { session } = await setupSessionAndPauseAtEvalLastLine(script); const { resource } = await session.send({ method: 'Network.loadNetworkResource', params: { url: resourceUrl }, }); assert(resource.success); const bogus = '999999'; const result = await session.send({ method: 'IO.read', params: { handle: bogus } }); assert(result.eof, 'Should be eof for bogus stream id'); assert.strictEqual(result.data, ''); await session.send({ method: 'IO.close', params: { handle: resource.stream } }); await session.send({ method: 'Debugger.resume' }); await session.waitForDisconnect(); }); test('should support IO.read with size and offset', async () => { const { session } = await setupSessionAndPauseAtEvalLastLine(script); const { resource } = await session.send({ method: 'Network.loadNetworkResource', params: { url: resourceUrl }, }); assert(resource.success); assert(resource.stream); let result = await session.send({ method: 'IO.read', params: { handle: resource.stream, size: 5 } }); assert.strictEqual(result.data, resourceText.slice(0, 5)); result = await session.send({ method: 'IO.read', params: { handle: resource.stream, offset: 5, size: 5 } }); assert.strictEqual(result.data, resourceText.slice(5, 10)); result = await session.send({ method: 'IO.read', params: { handle: resource.stream, offset: 10 } }); assert.strictEqual(result.data, resourceText.slice(10)); await session.send({ method: 'IO.close', params: { handle: resource.stream } }); await session.send({ method: 'Debugger.resume' }); await session.waitForDisconnect(); }); test('should load resource put from another thread', async () => { const workerScript = ` console.log('this is worker thread'); debugger; `; const script = ` const { NetworkResources } = require('node:inspector'); const fs = require('fs'); NetworkResources.put('${resourceUrl}', fs.readFileSync('${embedPath}', 'utf8')); const { Worker } = require('worker_threads'); const worker = new Worker(\`${workerScript}\`, {eval: true}); `; const instance = new NodeInstance([ '--experimental-inspector-network-resource', '--experimental-worker-inspection', '--inspect-brk=0', ], script); const session = await instance.connectInspectorSession(); await setupInspector(session); await session.waitForNotification('Debugger.paused'); await session.send({ method: 'Debugger.resume' }); await session.waitForNotification('Target.targetCreated'); await session.send({ method: 'Target.setAutoAttach', params: { autoAttach: true, waitForDebuggerOnStart: true } }); let sessionId; await session.waitForNotification((notification) => { if (notification.method === 'Target.attachedToTarget') { sessionId = notification.params.sessionId; return true; } return false; }); await setupInspector(session, sessionId); await session.waitForNotification('Debugger.paused'); const { resource } = await session.send({ method: 'Network.loadNetworkResource', params: { url: resourceUrl, sessionId }, }); assert(resource.success, 'Resource should be loaded successfully'); assert(resource.stream, 'Resource should have a stream handle'); let result = await session.send({ method: 'IO.read', params: { handle: resource.stream, sessionId } }); let data = result.data; let eof = result.eof; let content = ''; while (!eof) { content += data; result = await session.send({ method: 'IO.read', params: { handle: resource.stream, sessionId } }); data = result.data; eof = result.eof; } content += data; assert.strictEqual(content, resourceText); await session.send({ method: 'IO.close', params: { handle: resource.stream, sessionId } }); await session.send({ method: 'Debugger.resume', sessionId }); await session.waitForDisconnect(); async function setupInspector(session, sessionId) { await session.send({ method: 'NodeRuntime.enable', sessionId }); await session.waitForNotification('NodeRuntime.waitingForDebugger'); await session.send({ method: 'Runtime.enable', sessionId }); await session.send({ method: 'Debugger.enable', sessionId }); await session.send({ method: 'Runtime.runIfWaitingForDebugger', sessionId }); } });