From 3ac3d9c5694b9d1b8272e309a4298c85108e0ce6 Mon Sep 17 00:00:00 2001 From: Khang Hoang Date: Sat, 13 Jul 2019 12:49:50 -0700 Subject: [PATCH 01/13] Add network tab --- front_end/ndb.json | 28 +++-- front_end/ndb/NdbMain.js | 96 +++++++++++++--- front_end/ndb/module.json | 9 +- lib/preload/ndb/httpMonkeyPatching.js | 153 ++++++++++++++++++++++++++ services/ndd_service.js | 22 +++- 5 files changed, 277 insertions(+), 31 deletions(-) create mode 100644 lib/preload/ndb/httpMonkeyPatching.js diff --git a/front_end/ndb.json b/front_end/ndb.json index bccce14b..79b509ab 100644 --- a/front_end/ndb.json +++ b/front_end/ndb.json @@ -1,15 +1,21 @@ { - "modules" : [ - { "name": "ndb_sdk", "type": "autostart" }, - { "name": "ndb", "type": "autostart" }, - { "name": "layer_viewer" }, - { "name": "timeline_model" }, - { "name": "timeline" }, - { "name": "product_registry" }, - { "name": "mobile_throttling" }, - { "name": "ndb_ui" }, - { "name": "xterm" } - ], + "modules": [ + { "name": "ndb_sdk", "type": "autostart" }, + { "name": "ndb", "type": "autostart" }, + { "name": "layer_viewer" }, + { "name": "timeline_model" }, + { "name": "timeline" }, + { "name": "product_registry" }, + { "name": "mobile_throttling" }, + { "name": "ndb_ui" }, + { "name": "xterm" }, + { "name": "emulation", "type": "autostart" }, + { "name": "inspector_main", "type": "autostart" }, + { "name": "mobile_throttling", "type": "autostart" }, + { "name": "cookie_table" }, + { "name": "har_importer" }, + { "name": "network" } + ], "extends": "shell", "has_html": true } diff --git a/front_end/ndb/NdbMain.js b/front_end/ndb/NdbMain.js index f8204af0..1d96b3b8 100644 --- a/front_end/ndb/NdbMain.js +++ b/front_end/ndb/NdbMain.js @@ -5,14 +5,20 @@ */ Ndb.nodeExecPath = function() { - if (!Ndb._nodeExecPathPromise) - Ndb._nodeExecPathPromise = Ndb.backend.which('node').then(result => result.resolvedPath); + if (!Ndb._nodeExecPathPromise) { + Ndb._nodeExecPathPromise = Ndb.backend + .which('node') + .then(result => result.resolvedPath); + } return Ndb._nodeExecPathPromise; }; Ndb.npmExecPath = function() { - if (!Ndb._npmExecPathPromise) - Ndb._npmExecPathPromise = Ndb.backend.which('npm').then(result => result.resolvedPath); + if (!Ndb._npmExecPathPromise) { + Ndb._npmExecPathPromise = Ndb.backend + .which('npm') + .then(result => result.resolvedPath); + } return Ndb._npmExecPathPromise; }; @@ -27,39 +33,60 @@ Ndb.processInfo = function() { */ Ndb.NdbMain = class extends Common.Object { /** - * @override - */ + * @override + */ async run() { InspectorFrontendAPI.setUseSoftMenu(true); document.title = 'ndb'; - Common.moduleSetting('blackboxInternalScripts').addChangeListener(Ndb.NdbMain._calculateBlackboxState); + Common.moduleSetting('blackboxInternalScripts').addChangeListener( + Ndb.NdbMain._calculateBlackboxState + ); Ndb.NdbMain._calculateBlackboxState(); const setting = Persistence.isolatedFileSystemManager.workspaceFolderExcludePatternSetting(); setting.set(Ndb.NdbMain._defaultExcludePattern().join('|')); - Ndb.nodeProcessManager = await Ndb.NodeProcessManager.create(SDK.targetManager); + Ndb.nodeProcessManager = await Ndb.NodeProcessManager.create( + SDK.targetManager + ); - const {cwd} = await Ndb.processInfo(); + const { cwd } = await Ndb.processInfo(); await Ndb.nodeProcessManager.addFileSystem(cwd); // TODO(ak239): we do not want to create this model for workers, so we need a way to add custom capabilities. - SDK.SDKModel.register(NdbSdk.NodeWorkerModel, SDK.Target.Capability.JS, true); - SDK.SDKModel.register(NdbSdk.NodeRuntimeModel, SDK.Target.Capability.JS, true); + SDK.SDKModel.register( + NdbSdk.NodeWorkerModel, + SDK.Target.Capability.JS, + true + ); + SDK.SDKModel.register( + NdbSdk.NodeRuntimeModel, + SDK.Target.Capability.JS, + true + ); await new Promise(resolve => SDK.initMainConnection(resolve)); - SDK.targetManager.createTarget('', ls`Root`, SDK.Target.Type.Browser, null); + SDK.targetManager.createTarget( + '', + ls`Root`, + SDK.Target.Type.Browser, + null + ); + if (Common.moduleSetting('autoStartMain').get()) { const main = await Ndb.mainConfiguration(); if (main) { - if (main.prof) - await Ndb.nodeProcessManager.profile(main.execPath, main.args); - else - Ndb.nodeProcessManager.debug(main.execPath, main.args); + if (main.prof) { + await Ndb.nodeProcessManager.profile( + main.execPath, + main.args + ); + } else {Ndb.nodeProcessManager.debug(main.execPath, main.args);} } } Ndb.nodeProcessManager.startRepl(); } + static _defaultExcludePattern() { const defaultCommonExcludedFolders = [ '/bower_components/', '/\\.devtools', '/\\.git/', '/\\.sass-cache/', '/\\.hg/', '/\\.idea/', @@ -125,7 +152,6 @@ Ndb.mainConfiguration = async() => { prof }; }; - /** * @implements {UI.ContextMenu.Provider} * @unrestricted @@ -172,9 +198,21 @@ Ndb.NodeProcessManager = class extends Common.Object { static async create(targetManager) { const manager = new Ndb.NodeProcessManager(targetManager); manager._service = await Ndb.backend.createService('ndd_service.js', rpc.handle(manager)); + InspectorFrontendHost.sendMessageToBackend = manager.sendMessageToBackend.bind(manager); + return manager; } + /** + * @param {object} message + * + * @return {Promise} void + */ + async sendMessageToBackend(message) { + if (this._service && this._service.sendMessage) + return this._service.sendMessage(message); + } + env() { return this._service.env(); } @@ -246,6 +284,30 @@ Ndb.NodeProcessManager = class extends Common.Object { } } + sendLoadingFinished({ type, payload }) { + SDK._mainConnection._onMessage(JSON.stringify({ + method: 'Network.loadingFinished', + params: payload + })); + } + + responseToFrontEnd(id, result) { + InspectorFrontendHost.events.dispatchEventToListeners( + InspectorFrontendHostAPI.Events.DispatchMessage, + { + id, + result + } + ); + } + + sendNetworkData({ type, payload }) { + SDK._mainConnection._onMessage(JSON.stringify({ + method: type, + params: payload + })); + } + async terminalData(stream, data) { const content = await(await fetch(`data:application/octet-stream;base64,${data}`)).text(); if (content.startsWith('Debugger listening on') || content.startsWith('Debugger attached.') || content.startsWith('Waiting for the debugger to disconnect...')) diff --git a/front_end/ndb/module.json b/front_end/ndb/module.json index 4113c13c..baff7549 100644 --- a/front_end/ndb/module.json +++ b/front_end/ndb/module.json @@ -52,7 +52,14 @@ "className": "Ndb.ContextMenuProvider" } ], - "dependencies": ["common", "sdk", "ndb_sdk", "bindings", "persistence", "components"], + "dependencies": [ + "common", + "sdk", + "ndb_sdk", + "bindings", + "persistence", + "components" + ], "scripts": [ "InspectorFrontendHostOverrides.js", "Connection.js", diff --git a/lib/preload/ndb/httpMonkeyPatching.js b/lib/preload/ndb/httpMonkeyPatching.js new file mode 100644 index 00000000..930bca1f --- /dev/null +++ b/lib/preload/ndb/httpMonkeyPatching.js @@ -0,0 +1,153 @@ +const zlib = require('zlib'); +const http = require('http'); +const https = require('https'); + +const initTime = process.hrtime(); + +// DT requires us to use relative time in a strange format (xxx.xxx) +const getTime = () => { + const diff = process.hrtime(initTime); + + return diff[0] + diff[1] / 1e9; +}; + +const formatRequestHeaders = req => { + if (!req.headers) return {}; + return Object.keys(req.headers).reduce((acc, k) => { + if (typeof req.headers[k] === 'string') acc[k] = req.headers[k]; + return acc; + }, {}); +}; + +const formatResponseHeaders = res => { + if (!res.headers) return {}; + return Object.keys(res.headers).reduce((acc, k) => { + if (typeof res.headers[k] === 'string') acc[k] = res.headers[k]; + return acc; + }, {}); +}; + +const getMineType = mimeType => { + // nasty hack for ASF + if (mimeType === 'OPENJSON') + return 'application/json;charset=UTF-8'; + + + return mimeType; +}; + +const cacheRequests = {}; +let id = 1; +const getId = () => id++; + +const callbackWrapper = (callback, req) => res => { + const requestId = getId(); + res.req.__requestId = requestId; + + process.send({ + payload: { + requestId: requestId, + loaderId: requestId, + documentURL: req.href, + request: { + url: req.href, + method: req.method, + headers: formatRequestHeaders(req), + mixedContentType: 'none', + initialPriority: 'VeryHigh', + referrerPolicy: 'no-referrer-when-downgrade', + postData: req.body + }, + timestamp: getTime(), + wallTime: Date.now(), + initiator: { + type: 'other' + }, + type: 'Document' + }, + type: 'Network.requestWillBeSent' + }); + + const encoding = res.headers['content-encoding']; + let rawData = []; + + const onEnd = function() { + rawData = Buffer.concat(rawData); + rawData = rawData.toString('base64'); + + cacheRequests[res.req.__requestId] = { + ...res, + __rawData: rawData, + base64Encoded: true + }; + const payload = { + id: res.req.__requestId, + requestId: res.req.__requestId, + loaderId: res.req.__requestId, + base64Encoded: true, + data: cacheRequests[res.req.__requestId].__rawData, + timestamp: getTime(), + type: 'XHR', + encodedDataLength: 100, + response: { + url: req.href, + status: res.statusCode, + statusText: res.statusText, + // set-cookie prop in the header has value as an array + // for example: ["__cfduid=dbfe006ef71658bf4dba321343c227f9a15449556…20:29 GMT; path=/; domain=.typicode.com; HttpOnly"] + headers: formatResponseHeaders(res), + mimeType: getMineType( + res.headers['content-encoding'] || + res.headers['content-type'] + ), + requestHeaders: formatRequestHeaders(req) + } + }; + + // Send the response back. + process.send({ payload: payload, type: 'Network.responseReceived' }); + process.send({ payload: payload, type: 'Network.loadingFinished' }); + }; + + if (encoding === 'gzip' || encoding === 'x-gzip') { + const gunzip = zlib.createGunzip(); + res.pipe(gunzip); + + gunzip.on('data', function(data) { + rawData.push(data); + }); + gunzip.on('end', onEnd); + } else { + res.on('data', chunk => { + rawData.push(chunk); + }); + res.on('end', onEnd); + } + + callback && callback(res); +}; + +const originHTTPRequest = http.request; +http.request = function wrapMethodRequest(req, callback) { + const request = originHTTPRequest.call( + this, + req, + callbackWrapper(callback, req) + ); + return request; +}; + +const originHTTPSRequest = https.request; +https.request = function wrapMethodRequest(req, callback) { + const request = originHTTPSRequest.call( + this, + req, + callbackWrapper(callback, req) + ); + const originWrite = request.write.bind(request); + request.write = data => { + req.body = data.toString(); + originWrite(data); + }; + return request; +}; diff --git a/services/ndd_service.js b/services/ndd_service.js index 41beb06f..bfdb5f30 100644 --- a/services/ndd_service.js +++ b/services/ndd_service.js @@ -23,6 +23,8 @@ function silentRpcErrors(error) { process.on('uncaughtException', silentRpcErrors); process.on('unhandledRejection', silentRpcErrors); +const catchedRequests = {}; + const DebugState = { WS_OPEN: 1, WS_ERROR: 2, @@ -134,24 +136,40 @@ class NddService { } env() { + const pathToHttpPatch = path.resolve(__dirname, '..', './lib/preload/ndb/httpMonkeyPatching.js'); + return { - NODE_OPTIONS: `--require ndb/preload.js`, + NODE_OPTIONS: `--require ndb/preload.js --require ${pathToHttpPatch}`, NODE_PATH: `${process.env.NODE_PATH || ''}${path.delimiter}${path.join(__dirname, '..', 'lib', 'preload')}`, NDD_IPC: this._pipe }; } + sendMessage(rawMessage) { + const message = JSON.parse(rawMessage); + // send message to frontend directly + // (eg: getResponseBody) + const { base64Encoded, data } = catchedRequests[message.params.requestId]; + this._frontend.responseToFrontEnd(message.id, { base64Encoded, body: data }); + } + async debug(execPath, args, options) { const env = this.env(); if (options.data) env.NDD_DATA = options.data; + const p = spawn(execPath, args, { cwd: options.cwd, env: { ...process.env, ...env }, - stdio: options.ignoreOutput ? 'ignore' : ['inherit', 'pipe', 'pipe'], + stdio: options.ignoreOutput ? ['ignore', 'ignore', 'ignore', 'ipc'] : ['pipe', 'pipe', 'pipe', 'ipc'], windowsHide: true }); if (!options.ignoreOutput) { + p.on('message', ({ type, payload }) => { + if (!(type && payload)) return; + catchedRequests[payload.id] = payload; + this._frontend.sendNetworkData({ type, payload }); + }); p.stderr.on('data', data => { if (process.connected) this._frontend.terminalData('stderr', data.toString('base64')); From 04f071a9a266efd39fc09094a01e7f4fd3d90b02 Mon Sep 17 00:00:00 2001 From: Khang Hoang Date: Sun, 14 Jul 2019 01:52:49 -0700 Subject: [PATCH 02/13] un-format NdbMain.js --- front_end/ndb/NdbMain.js | 114 ++++++++++++++------------------------- 1 file changed, 41 insertions(+), 73 deletions(-) diff --git a/front_end/ndb/NdbMain.js b/front_end/ndb/NdbMain.js index 1d96b3b8..1405eeaa 100644 --- a/front_end/ndb/NdbMain.js +++ b/front_end/ndb/NdbMain.js @@ -5,20 +5,14 @@ */ Ndb.nodeExecPath = function() { - if (!Ndb._nodeExecPathPromise) { - Ndb._nodeExecPathPromise = Ndb.backend - .which('node') - .then(result => result.resolvedPath); - } + if (!Ndb._nodeExecPathPromise) + Ndb._nodeExecPathPromise = Ndb.backend.which('node').then(result => result.resolvedPath); return Ndb._nodeExecPathPromise; }; Ndb.npmExecPath = function() { - if (!Ndb._npmExecPathPromise) { - Ndb._npmExecPathPromise = Ndb.backend - .which('npm') - .then(result => result.resolvedPath); - } + if (!Ndb._npmExecPathPromise) + Ndb._npmExecPathPromise = Ndb.backend.which('npm').then(result => result.resolvedPath); return Ndb._npmExecPathPromise; }; @@ -33,60 +27,39 @@ Ndb.processInfo = function() { */ Ndb.NdbMain = class extends Common.Object { /** - * @override - */ + * @override + */ async run() { InspectorFrontendAPI.setUseSoftMenu(true); document.title = 'ndb'; - Common.moduleSetting('blackboxInternalScripts').addChangeListener( - Ndb.NdbMain._calculateBlackboxState - ); + Common.moduleSetting('blackboxInternalScripts').addChangeListener(Ndb.NdbMain._calculateBlackboxState); Ndb.NdbMain._calculateBlackboxState(); const setting = Persistence.isolatedFileSystemManager.workspaceFolderExcludePatternSetting(); setting.set(Ndb.NdbMain._defaultExcludePattern().join('|')); - Ndb.nodeProcessManager = await Ndb.NodeProcessManager.create( - SDK.targetManager - ); + Ndb.nodeProcessManager = await Ndb.NodeProcessManager.create(SDK.targetManager); - const { cwd } = await Ndb.processInfo(); + const {cwd} = await Ndb.processInfo(); await Ndb.nodeProcessManager.addFileSystem(cwd); // TODO(ak239): we do not want to create this model for workers, so we need a way to add custom capabilities. - SDK.SDKModel.register( - NdbSdk.NodeWorkerModel, - SDK.Target.Capability.JS, - true - ); - SDK.SDKModel.register( - NdbSdk.NodeRuntimeModel, - SDK.Target.Capability.JS, - true - ); + SDK.SDKModel.register(NdbSdk.NodeWorkerModel, SDK.Target.Capability.JS, true); + SDK.SDKModel.register(NdbSdk.NodeRuntimeModel, SDK.Target.Capability.JS, true); await new Promise(resolve => SDK.initMainConnection(resolve)); - SDK.targetManager.createTarget( - '', - ls`Root`, - SDK.Target.Type.Browser, - null - ); - + SDK.targetManager.createTarget('', ls`Root`, SDK.Target.Type.Browser, null); if (Common.moduleSetting('autoStartMain').get()) { const main = await Ndb.mainConfiguration(); if (main) { - if (main.prof) { - await Ndb.nodeProcessManager.profile( - main.execPath, - main.args - ); - } else {Ndb.nodeProcessManager.debug(main.execPath, main.args);} + if (main.prof) + await Ndb.nodeProcessManager.profile(main.execPath, main.args); + else + Ndb.nodeProcessManager.debug(main.execPath, main.args); } } Ndb.nodeProcessManager.startRepl(); } - static _defaultExcludePattern() { const defaultCommonExcludedFolders = [ '/bower_components/', '/\\.devtools', '/\\.git/', '/\\.sass-cache/', '/\\.hg/', '/\\.idea/', @@ -152,6 +125,7 @@ Ndb.mainConfiguration = async() => { prof }; }; + /** * @implements {UI.ContextMenu.Provider} * @unrestricted @@ -199,20 +173,38 @@ Ndb.NodeProcessManager = class extends Common.Object { const manager = new Ndb.NodeProcessManager(targetManager); manager._service = await Ndb.backend.createService('ndd_service.js', rpc.handle(manager)); InspectorFrontendHost.sendMessageToBackend = manager.sendMessageToBackend.bind(manager); - return manager; } - /** - * @param {object} message - * - * @return {Promise} void - */ async sendMessageToBackend(message) { if (this._service && this._service.sendMessage) return this._service.sendMessage(message); } + sendLoadingFinished({ type, payload }) { + SDK._mainConnection._onMessage(JSON.stringify({ + method: 'Network.loadingFinished', + params: payload + })); + } + + responseToFrontEnd(id, result) { + InspectorFrontendHost.events.dispatchEventToListeners( + InspectorFrontendHostAPI.Events.DispatchMessage, + { + id, + result + } + ); + } + + sendNetworkData({ type, payload }) { + SDK._mainConnection._onMessage(JSON.stringify({ + method: type, + params: payload + })); + } + env() { return this._service.env(); } @@ -284,30 +276,6 @@ Ndb.NodeProcessManager = class extends Common.Object { } } - sendLoadingFinished({ type, payload }) { - SDK._mainConnection._onMessage(JSON.stringify({ - method: 'Network.loadingFinished', - params: payload - })); - } - - responseToFrontEnd(id, result) { - InspectorFrontendHost.events.dispatchEventToListeners( - InspectorFrontendHostAPI.Events.DispatchMessage, - { - id, - result - } - ); - } - - sendNetworkData({ type, payload }) { - SDK._mainConnection._onMessage(JSON.stringify({ - method: type, - params: payload - })); - } - async terminalData(stream, data) { const content = await(await fetch(`data:application/octet-stream;base64,${data}`)).text(); if (content.startsWith('Debugger listening on') || content.startsWith('Debugger attached.') || content.startsWith('Waiting for the debugger to disconnect...')) From d3577320841fcf3864c1d363a410d63ebca2fd14 Mon Sep 17 00:00:00 2001 From: Khang Hoang Date: Sun, 14 Jul 2019 02:26:23 -0700 Subject: [PATCH 03/13] could not monkey patch http/https --- front_end/ndb/NdbMain.js | 20 ++++++++++++++++++++ lib/backend.js | 6 ++++++ services/ndd_service.js | 4 +--- 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/front_end/ndb/NdbMain.js b/front_end/ndb/NdbMain.js index 1405eeaa..ee8dd032 100644 --- a/front_end/ndb/NdbMain.js +++ b/front_end/ndb/NdbMain.js @@ -238,6 +238,26 @@ Ndb.NodeProcessManager = class extends Common.Object { info.id, userFriendlyName(info), SDK.Target.Type.Node, this._targetManager.targetById(info.ppid) || this._targetManager.mainTarget(), undefined, false, connection); target[NdbSdk.connectionSymbol] = connection; + + try { + // this doesnt work + // target.runtimeAgent().invoke_evaluate({ + // expression: ` + // const zlib = require('http'); + // console.log('foo'); + // ` + // }); + // + // but this does + // target.runtimeAgent().invoke_evaluate({ + // expression: ` + // console.log('foo'); + // ` + // }); + } catch(err) { + console.log(err); + } + await this.addFileSystem(info.cwd, info.scriptName); if (info.scriptName) { const scriptURL = Common.ParsedURL.platformPathToURL(info.scriptName); diff --git a/lib/backend.js b/lib/backend.js index 53e4011b..68552db5 100644 --- a/lib/backend.js +++ b/lib/backend.js @@ -48,6 +48,12 @@ class Backend { opn(url); } + async httpMonkeyPatchingSource() { + const pathToHttpPatch = path.resolve(__dirname, '..', './lib/preload/ndb/httpMonkeyPatching.js'); + const content = await fsReadFile(pathToHttpPatch, 'utf8'); + return content; + } + pkg() { // TODO(ak239spb): implement it as decorations over package.json file. try { diff --git a/services/ndd_service.js b/services/ndd_service.js index bfdb5f30..2753e60b 100644 --- a/services/ndd_service.js +++ b/services/ndd_service.js @@ -136,10 +136,8 @@ class NddService { } env() { - const pathToHttpPatch = path.resolve(__dirname, '..', './lib/preload/ndb/httpMonkeyPatching.js'); - return { - NODE_OPTIONS: `--require ndb/preload.js --require ${pathToHttpPatch}`, + NODE_OPTIONS: `--require ndb/preload.js`, NODE_PATH: `${process.env.NODE_PATH || ''}${path.delimiter}${path.join(__dirname, '..', 'lib', 'preload')}`, NDD_IPC: this._pipe }; From 33e6242c72b9a80f03b2d2fd0a3dbb780420140b Mon Sep 17 00:00:00 2001 From: Khang Hoang Date: Sun, 14 Jul 2019 14:38:07 -0700 Subject: [PATCH 04/13] use another approach to money patch http/https request --- front_end/ndb/NdbMain.js | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/front_end/ndb/NdbMain.js b/front_end/ndb/NdbMain.js index ee8dd032..da6f8c1a 100644 --- a/front_end/ndb/NdbMain.js +++ b/front_end/ndb/NdbMain.js @@ -239,24 +239,10 @@ Ndb.NodeProcessManager = class extends Common.Object { this._targetManager.targetById(info.ppid) || this._targetManager.mainTarget(), undefined, false, connection); target[NdbSdk.connectionSymbol] = connection; - try { - // this doesnt work - // target.runtimeAgent().invoke_evaluate({ - // expression: ` - // const zlib = require('http'); - // console.log('foo'); - // ` - // }); - // - // but this does - // target.runtimeAgent().invoke_evaluate({ - // expression: ` - // console.log('foo'); - // ` - // }); - } catch(err) { - console.log(err); - } + target.runtimeAgent().invoke_evaluate({ + expression: await Ndb.backend.httpMonkeyPatchingSource(), + includeCommandLineAPI: true + }); await this.addFileSystem(info.cwd, info.scriptName); if (info.scriptName) { From 1ed024a963daf2141b22ff25aed6c2c65121a224 Mon Sep 17 00:00:00 2001 From: Khang Hoang Date: Sun, 14 Jul 2019 15:42:19 -0700 Subject: [PATCH 05/13] Refactor the logic to send data from backend to frontend --- front_end/ndb/NdbMain.js | 31 ++++++++++++++++++++++----- lib/preload/ndb/httpMonkeyPatching.js | 25 ++++++++++++++++++--- services/ndd_service.js | 14 ++++++------ 3 files changed, 55 insertions(+), 15 deletions(-) diff --git a/front_end/ndb/NdbMain.js b/front_end/ndb/NdbMain.js index da6f8c1a..4725d57e 100644 --- a/front_end/ndb/NdbMain.js +++ b/front_end/ndb/NdbMain.js @@ -172,14 +172,14 @@ Ndb.NodeProcessManager = class extends Common.Object { static async create(targetManager) { const manager = new Ndb.NodeProcessManager(targetManager); manager._service = await Ndb.backend.createService('ndd_service.js', rpc.handle(manager)); - InspectorFrontendHost.sendMessageToBackend = manager.sendMessageToBackend.bind(manager); + // InspectorFrontendHost.sendMessageToBackend = manager.sendMessageToBackend.bind(manager); return manager; } - async sendMessageToBackend(message) { - if (this._service && this._service.sendMessage) - return this._service.sendMessage(message); - } + // async sendMessageToBackend(message) { + // if (this._service && this._service.sendMessage) + // return this._service.sendMessage(message); + // } sendLoadingFinished({ type, payload }) { SDK._mainConnection._onMessage(JSON.stringify({ @@ -244,6 +244,27 @@ Ndb.NodeProcessManager = class extends Common.Object { includeCommandLineAPI: true }); + setInterval(async() => { + const message = await target.runtimeAgent().invoke_evaluate({ + expression: 'process._getNetworkMessages()', + awaitPromise: true + }); + + if (!message.result) return; + const arrMessages = JSON.parse(message.result.value); + + for (const mes of arrMessages) { + const { type, payload } = mes; + + if (type) { + SDK._mainConnection._onMessage(JSON.stringify({ + method: type, + params: payload + })); + } + } + }, 200); + await this.addFileSystem(info.cwd, info.scriptName); if (info.scriptName) { const scriptURL = Common.ParsedURL.platformPathToURL(info.scriptName); diff --git a/lib/preload/ndb/httpMonkeyPatching.js b/lib/preload/ndb/httpMonkeyPatching.js index 930bca1f..eecfe642 100644 --- a/lib/preload/ndb/httpMonkeyPatching.js +++ b/lib/preload/ndb/httpMonkeyPatching.js @@ -40,11 +40,30 @@ const cacheRequests = {}; let id = 1; const getId = () => id++; +let messages = []; +let messageAdded = null; + +// this function is our instrumentation, to report anything to frontend - call it instead of process.send +function reportMessage(message) { + messages.push(message); + if (messageAdded) { + setTimeout(messageAdded, 0); + messageAdded = null; + } +} + +// this function should be called from frontend in the loop using `target.runtimeAgent().invoke_evaluate` +process._getNetworkMessages = async function() { + if (!messages.length) + await new Promise(resolve => messageAdded = resolve); + return JSON.stringify(messages.splice(0)); +}; + const callbackWrapper = (callback, req) => res => { const requestId = getId(); res.req.__requestId = requestId; - process.send({ + reportMessage({ payload: { requestId: requestId, loaderId: requestId, @@ -105,8 +124,8 @@ const callbackWrapper = (callback, req) => res => { }; // Send the response back. - process.send({ payload: payload, type: 'Network.responseReceived' }); - process.send({ payload: payload, type: 'Network.loadingFinished' }); + reportMessage({ payload: payload, type: 'Network.responseReceived' }) + reportMessage({ payload: payload, type: 'Network.loadingFinished' }); }; if (encoding === 'gzip' || encoding === 'x-gzip') { diff --git a/services/ndd_service.js b/services/ndd_service.js index 2753e60b..c0dad369 100644 --- a/services/ndd_service.js +++ b/services/ndd_service.js @@ -143,13 +143,13 @@ class NddService { }; } - sendMessage(rawMessage) { - const message = JSON.parse(rawMessage); - // send message to frontend directly - // (eg: getResponseBody) - const { base64Encoded, data } = catchedRequests[message.params.requestId]; - this._frontend.responseToFrontEnd(message.id, { base64Encoded, body: data }); - } + // sendMessage(rawMessage) { + // const message = JSON.parse(rawMessage); + // // send message to frontend directly + // // (eg: getResponseBody) + // const { base64Encoded, data } = catchedRequests[message.params.requestId]; + // this._frontend.responseToFrontEnd(message.id, { base64Encoded, body: data }); + // } async debug(execPath, args, options) { const env = this.env(); From be3c73cdb2bdcc9231bb58c676a51dffe5b95c12 Mon Sep 17 00:00:00 2001 From: Khang Hoang Date: Sun, 14 Jul 2019 20:27:31 -0700 Subject: [PATCH 06/13] Refactor code --- .../ndb/InspectorFrontendHostOverrides.js | 31 ++++++++++++- front_end/ndb/NdbMain.js | 45 +++++++++---------- lib/preload/ndb/httpMonkeyPatching.js | 34 +++++++++++++- services/ndd_service.js | 14 +++--- 4 files changed, 91 insertions(+), 33 deletions(-) diff --git a/front_end/ndb/InspectorFrontendHostOverrides.js b/front_end/ndb/InspectorFrontendHostOverrides.js index 70158e63..6bae7c60 100644 --- a/front_end/ndb/InspectorFrontendHostOverrides.js +++ b/front_end/ndb/InspectorFrontendHostOverrides.js @@ -4,7 +4,7 @@ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -(function(){ +(async function(){ InspectorFrontendHost.getPreferences = async function(callback) { [Ndb.backend] = await carlo.loadParams(); const prefs = { @@ -30,4 +30,33 @@ callback({statusCode: 404}); } }; + + InspectorFrontendHost.sendMessageToBackend = async rawMessage => { + const parsedMes = JSON.parse(rawMessage); + if (parsedMes.method !== 'Network.getResponseBody') + return; + + const mes = await target.runtimeAgent().invoke_evaluate({ + expression: `process._sendMessage(${JSON.stringify(JSON.parse(rawMessage))})`, + awaitPromise: true + }); + + if (!mes.result) return; + try { + const [id, result] = mes.result.value; + if (result) { + InspectorFrontendHost.events.dispatchEventToListeners( + InspectorFrontendHostAPI.Events.DispatchMessage, + { + id, + result + } + ); + } + } catch (err) { + console.log(err); + } + }; + + })(); diff --git a/front_end/ndb/NdbMain.js b/front_end/ndb/NdbMain.js index 4725d57e..04f63230 100644 --- a/front_end/ndb/NdbMain.js +++ b/front_end/ndb/NdbMain.js @@ -172,7 +172,6 @@ Ndb.NodeProcessManager = class extends Common.Object { static async create(targetManager) { const manager = new Ndb.NodeProcessManager(targetManager); manager._service = await Ndb.backend.createService('ndd_service.js', rpc.handle(manager)); - // InspectorFrontendHost.sendMessageToBackend = manager.sendMessageToBackend.bind(manager); return manager; } @@ -181,29 +180,29 @@ Ndb.NodeProcessManager = class extends Common.Object { // return this._service.sendMessage(message); // } - sendLoadingFinished({ type, payload }) { - SDK._mainConnection._onMessage(JSON.stringify({ - method: 'Network.loadingFinished', - params: payload - })); - } - - responseToFrontEnd(id, result) { - InspectorFrontendHost.events.dispatchEventToListeners( - InspectorFrontendHostAPI.Events.DispatchMessage, - { - id, - result - } - ); - } + // sendLoadingFinished({ type, payload }) { + // SDK._mainConnection._onMessage(JSON.stringify({ + // method: 'Network.loadingFinished', + // params: payload + // })); + // } +// + // responseToFrontEnd(id, result) { + // InspectorFrontendHost.events.dispatchEventToListeners( + // InspectorFrontendHostAPI.Events.DispatchMessage, + // { + // id, + // result + // } + // ); + // } - sendNetworkData({ type, payload }) { - SDK._mainConnection._onMessage(JSON.stringify({ - method: type, - params: payload - })); - } + // sendNetworkData({ type, payload }) { + // SDK._mainConnection._onMessage(JSON.stringify({ + // method: type, + // params: payload + // })); + // } env() { return this._service.env(); diff --git a/lib/preload/ndb/httpMonkeyPatching.js b/lib/preload/ndb/httpMonkeyPatching.js index eecfe642..e0a31dd0 100644 --- a/lib/preload/ndb/httpMonkeyPatching.js +++ b/lib/preload/ndb/httpMonkeyPatching.js @@ -36,7 +36,7 @@ const getMineType = mimeType => { return mimeType; }; -const cacheRequests = {}; +let cacheRequests = {}; let id = 1; const getId = () => id++; @@ -59,6 +59,33 @@ process._getNetworkMessages = async function() { return JSON.stringify(messages.splice(0)); }; +process._sendMessage = async function(rawMessage) { + return new Promise(resolve => { + setTimeout(() => { + console.log({ rawMessage }); + const message = rawMessage; + console.log({ message }); + // send message to frontend directly + // (eg: getResponseBody) + console.log({ cacheRequests }); + console.log({ foo: cacheRequests[message.params.requestId] }); + + if (!cacheRequests[message.params.requestId]) { + resolve(JSON.stringify({})); + return; + } + + if (message.method === 'Network.getResponseBody') { + const { base64Encoded, data } = cacheRequests[message.params.requestId]; + + console.log({ cacheRequests }); + console.log({ data }); + resolve(JSON.stringify([message.id, { base64Encoded, body: data }])); + } + }, 0); + }); +}; + const callbackWrapper = (callback, req) => res => { const requestId = getId(); res.req.__requestId = requestId; @@ -99,6 +126,9 @@ const callbackWrapper = (callback, req) => res => { __rawData: rawData, base64Encoded: true }; + + console.log({ bar: cacheRequests[res.req.__requestId] }); + const payload = { id: res.req.__requestId, requestId: res.req.__requestId, @@ -124,7 +154,7 @@ const callbackWrapper = (callback, req) => res => { }; // Send the response back. - reportMessage({ payload: payload, type: 'Network.responseReceived' }) + reportMessage({ payload: payload, type: 'Network.responseReceived' }); reportMessage({ payload: payload, type: 'Network.loadingFinished' }); }; diff --git a/services/ndd_service.js b/services/ndd_service.js index c0dad369..085205fa 100644 --- a/services/ndd_service.js +++ b/services/ndd_service.js @@ -23,7 +23,7 @@ function silentRpcErrors(error) { process.on('uncaughtException', silentRpcErrors); process.on('unhandledRejection', silentRpcErrors); -const catchedRequests = {}; +// const catchedRequests = {}; const DebugState = { WS_OPEN: 1, @@ -159,15 +159,15 @@ class NddService { const p = spawn(execPath, args, { cwd: options.cwd, env: { ...process.env, ...env }, - stdio: options.ignoreOutput ? ['ignore', 'ignore', 'ignore', 'ipc'] : ['pipe', 'pipe', 'pipe', 'ipc'], + stdio: options.ignoreOutput ? 'ignore' : ['pipe', 'pipe', 'pipe'], windowsHide: true }); if (!options.ignoreOutput) { - p.on('message', ({ type, payload }) => { - if (!(type && payload)) return; - catchedRequests[payload.id] = payload; - this._frontend.sendNetworkData({ type, payload }); - }); + // p.on('message', ({ type, payload }) => { + // if (!(type && payload)) return; + // catchedRequests[payload.id] = payload; + // this._frontend.sendNetworkData({ type, payload }); + // }); p.stderr.on('data', data => { if (process.connected) this._frontend.terminalData('stderr', data.toString('base64')); From 9b51ded8ed36c2892410241c92bd27390022ee69 Mon Sep 17 00:00:00 2001 From: Khang Hoang Date: Sun, 14 Jul 2019 20:37:27 -0700 Subject: [PATCH 07/13] Refactor code --- .../ndb/InspectorFrontendHostOverrides.js | 20 ++++++++++++++++ lib/preload/ndb/httpMonkeyPatching.js | 24 ++++--------------- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/front_end/ndb/InspectorFrontendHostOverrides.js b/front_end/ndb/InspectorFrontendHostOverrides.js index 6bae7c60..58179133 100644 --- a/front_end/ndb/InspectorFrontendHostOverrides.js +++ b/front_end/ndb/InspectorFrontendHostOverrides.js @@ -58,5 +58,25 @@ } }; + while (true) { + const message = await target.runtimeAgent().invoke_evaluate({ + expression: 'process._getNetworkMessages()', + awaitPromise: true + }); + + if (!message.result) return; + const arrMessages = JSON.parse(message.result.value); + + for (const mes of arrMessages) { + const { type, payload } = mes; + + if (type) { + SDK._mainConnection._onMessage(JSON.stringify({ + method: type, + params: payload + })); + } + } + } })(); diff --git a/lib/preload/ndb/httpMonkeyPatching.js b/lib/preload/ndb/httpMonkeyPatching.js index e0a31dd0..a494e6af 100644 --- a/lib/preload/ndb/httpMonkeyPatching.js +++ b/lib/preload/ndb/httpMonkeyPatching.js @@ -43,7 +43,6 @@ const getId = () => id++; let messages = []; let messageAdded = null; -// this function is our instrumentation, to report anything to frontend - call it instead of process.send function reportMessage(message) { messages.push(message); if (messageAdded) { @@ -52,7 +51,6 @@ function reportMessage(message) { } } -// this function should be called from frontend in the loop using `target.runtimeAgent().invoke_evaluate` process._getNetworkMessages = async function() { if (!messages.length) await new Promise(resolve => messageAdded = resolve); @@ -61,20 +59,10 @@ process._getNetworkMessages = async function() { process._sendMessage = async function(rawMessage) { return new Promise(resolve => { - setTimeout(() => { - console.log({ rawMessage }); - const message = rawMessage; - console.log({ message }); - // send message to frontend directly - // (eg: getResponseBody) - console.log({ cacheRequests }); - console.log({ foo: cacheRequests[message.params.requestId] }); - - if (!cacheRequests[message.params.requestId]) { - resolve(JSON.stringify({})); - return; - } - + const message = rawMessage; + if (!cacheRequests[message.params.requestId]) { + resolve(JSON.stringify({})); + } else { if (message.method === 'Network.getResponseBody') { const { base64Encoded, data } = cacheRequests[message.params.requestId]; @@ -82,7 +70,7 @@ process._sendMessage = async function(rawMessage) { console.log({ data }); resolve(JSON.stringify([message.id, { base64Encoded, body: data }])); } - }, 0); + } }); }; @@ -127,8 +115,6 @@ const callbackWrapper = (callback, req) => res => { base64Encoded: true }; - console.log({ bar: cacheRequests[res.req.__requestId] }); - const payload = { id: res.req.__requestId, requestId: res.req.__requestId, From 77b7cfedd9966df3213e666d73a16a8ac2a7690c Mon Sep 17 00:00:00 2001 From: Khang Hoang Date: Sun, 14 Jul 2019 20:40:00 -0700 Subject: [PATCH 08/13] Remove unused code --- front_end/ndb/NdbMain.js | 29 ----------------------------- front_end/ndb/module.json | 9 +-------- services/ndd_service.js | 13 ------------- 3 files changed, 1 insertion(+), 50 deletions(-) diff --git a/front_end/ndb/NdbMain.js b/front_end/ndb/NdbMain.js index 04f63230..f5c9a2c0 100644 --- a/front_end/ndb/NdbMain.js +++ b/front_end/ndb/NdbMain.js @@ -175,35 +175,6 @@ Ndb.NodeProcessManager = class extends Common.Object { return manager; } - // async sendMessageToBackend(message) { - // if (this._service && this._service.sendMessage) - // return this._service.sendMessage(message); - // } - - // sendLoadingFinished({ type, payload }) { - // SDK._mainConnection._onMessage(JSON.stringify({ - // method: 'Network.loadingFinished', - // params: payload - // })); - // } -// - // responseToFrontEnd(id, result) { - // InspectorFrontendHost.events.dispatchEventToListeners( - // InspectorFrontendHostAPI.Events.DispatchMessage, - // { - // id, - // result - // } - // ); - // } - - // sendNetworkData({ type, payload }) { - // SDK._mainConnection._onMessage(JSON.stringify({ - // method: type, - // params: payload - // })); - // } - env() { return this._service.env(); } diff --git a/front_end/ndb/module.json b/front_end/ndb/module.json index baff7549..4113c13c 100644 --- a/front_end/ndb/module.json +++ b/front_end/ndb/module.json @@ -52,14 +52,7 @@ "className": "Ndb.ContextMenuProvider" } ], - "dependencies": [ - "common", - "sdk", - "ndb_sdk", - "bindings", - "persistence", - "components" - ], + "dependencies": ["common", "sdk", "ndb_sdk", "bindings", "persistence", "components"], "scripts": [ "InspectorFrontendHostOverrides.js", "Connection.js", diff --git a/services/ndd_service.js b/services/ndd_service.js index 085205fa..b0db0212 100644 --- a/services/ndd_service.js +++ b/services/ndd_service.js @@ -143,14 +143,6 @@ class NddService { }; } - // sendMessage(rawMessage) { - // const message = JSON.parse(rawMessage); - // // send message to frontend directly - // // (eg: getResponseBody) - // const { base64Encoded, data } = catchedRequests[message.params.requestId]; - // this._frontend.responseToFrontEnd(message.id, { base64Encoded, body: data }); - // } - async debug(execPath, args, options) { const env = this.env(); if (options.data) @@ -163,11 +155,6 @@ class NddService { windowsHide: true }); if (!options.ignoreOutput) { - // p.on('message', ({ type, payload }) => { - // if (!(type && payload)) return; - // catchedRequests[payload.id] = payload; - // this._frontend.sendNetworkData({ type, payload }); - // }); p.stderr.on('data', data => { if (process.connected) this._frontend.terminalData('stderr', data.toString('base64')); From f99b51a172b70ffb26cefba751df70c6f6666964 Mon Sep 17 00:00:00 2001 From: Khang Hoang Date: Sun, 14 Jul 2019 20:42:50 -0700 Subject: [PATCH 09/13] Remove this and the network tab no longer shows the requests --- front_end/ndb/NdbMain.js | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/front_end/ndb/NdbMain.js b/front_end/ndb/NdbMain.js index f5c9a2c0..abd39659 100644 --- a/front_end/ndb/NdbMain.js +++ b/front_end/ndb/NdbMain.js @@ -214,27 +214,6 @@ Ndb.NodeProcessManager = class extends Common.Object { includeCommandLineAPI: true }); - setInterval(async() => { - const message = await target.runtimeAgent().invoke_evaluate({ - expression: 'process._getNetworkMessages()', - awaitPromise: true - }); - - if (!message.result) return; - const arrMessages = JSON.parse(message.result.value); - - for (const mes of arrMessages) { - const { type, payload } = mes; - - if (type) { - SDK._mainConnection._onMessage(JSON.stringify({ - method: type, - params: payload - })); - } - } - }, 200); - await this.addFileSystem(info.cwd, info.scriptName); if (info.scriptName) { const scriptURL = Common.ParsedURL.platformPathToURL(info.scriptName); From 3d7113d1046533f9d28d98fa8a0b9f27d2c39c4a Mon Sep 17 00:00:00 2001 From: Khang Hoang Date: Sun, 14 Jul 2019 20:43:58 -0700 Subject: [PATCH 10/13] undo the change --- services/ndd_service.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/services/ndd_service.js b/services/ndd_service.js index b0db0212..41beb06f 100644 --- a/services/ndd_service.js +++ b/services/ndd_service.js @@ -23,8 +23,6 @@ function silentRpcErrors(error) { process.on('uncaughtException', silentRpcErrors); process.on('unhandledRejection', silentRpcErrors); -// const catchedRequests = {}; - const DebugState = { WS_OPEN: 1, WS_ERROR: 2, @@ -147,11 +145,10 @@ class NddService { const env = this.env(); if (options.data) env.NDD_DATA = options.data; - const p = spawn(execPath, args, { cwd: options.cwd, env: { ...process.env, ...env }, - stdio: options.ignoreOutput ? 'ignore' : ['pipe', 'pipe', 'pipe'], + stdio: options.ignoreOutput ? 'ignore' : ['inherit', 'pipe', 'pipe'], windowsHide: true }); if (!options.ignoreOutput) { From db2de6eadaa2566ef9216184434b0c650e24d2d4 Mon Sep 17 00:00:00 2001 From: Khang Hoang Date: Sun, 28 Jul 2019 20:14:21 -0700 Subject: [PATCH 11/13] feat(interceptor): proof-of-concept for network interception --- front_end/ndb/Connection.js | 40 +++++++++++++++++++++++ front_end/ndb/NdbMain.js | 3 ++ front_end/ndb/NetworkInterceptor.js | 50 +++++++++++++++++++++++++++++ front_end/ndb/module.json | 1 + 4 files changed, 94 insertions(+) create mode 100644 front_end/ndb/NetworkInterceptor.js diff --git a/front_end/ndb/Connection.js b/front_end/ndb/Connection.js index a4865f57..88dff0f3 100644 --- a/front_end/ndb/Connection.js +++ b/front_end/ndb/Connection.js @@ -1,8 +1,35 @@ +Ndb.ConnectionInterceptor = class { + constructor() { + this._onMessage = null; + } + + /** + * @param {string} message + * @return {boolean} + */ + sendRawMessage(message) { + throw new Error('Not implemented'); + } + + setOnMessage(onMessage) { + this._onMessage = onMessage; + } + + dispatchMessage(message) { + if (this._onMessage) + this._onMessage(message); + } + + disconnect() { + } +}; + Ndb.Connection = class { constructor(channel) { this._onMessage = null; this._onDisconnect = null; this._channel = channel; + this._interceptors = []; } static async create(channel) { @@ -16,6 +43,8 @@ Ndb.Connection = class { */ setOnMessage(onMessage) { this._onMessage = onMessage; + for (const interceptor of this._interceptors) + interceptor.setOnMessage(this._onMessage); } /** @@ -29,14 +58,25 @@ Ndb.Connection = class { * @param {string} message */ sendRawMessage(message) { + for (const interceptor of this._interceptors) { + if (interceptor.sendRawMessage(message)) + return; + } this._channel.send(message); } + addInterceptor(interceptor) { + this._interceptors.push(interceptor); + interceptor.setOnMessage(this._onMessage); + } + /** * @return {!Promise} */ disconnect() { this._channel.close(); + for (const interceptor of this._interceptors) + interceptor.disconnect(); } /** diff --git a/front_end/ndb/NdbMain.js b/front_end/ndb/NdbMain.js index abd39659..ecbe7d87 100644 --- a/front_end/ndb/NdbMain.js +++ b/front_end/ndb/NdbMain.js @@ -204,9 +204,12 @@ Ndb.NodeProcessManager = class extends Common.Object { async detected(info, channel) { const connection = await Ndb.Connection.create(channel); + const networkInterceptor = new Ndb.NetworkInterceptor(); + connection.addInterceptor(networkInterceptor); const target = this._targetManager.createTarget( info.id, userFriendlyName(info), SDK.Target.Type.Node, this._targetManager.targetById(info.ppid) || this._targetManager.mainTarget(), undefined, false, connection); + networkInterceptor.setTarget(target); target[NdbSdk.connectionSymbol] = connection; target.runtimeAgent().invoke_evaluate({ diff --git a/front_end/ndb/NetworkInterceptor.js b/front_end/ndb/NetworkInterceptor.js new file mode 100644 index 00000000..e70ee4a6 --- /dev/null +++ b/front_end/ndb/NetworkInterceptor.js @@ -0,0 +1,50 @@ +Ndb.NetworkInterceptor = class extends Ndb.ConnectionInterceptor { + constructor() { + super(); + this._buffer = []; + } + + setTarget(target) { + this._target = target; + for (const message of this._buffer.splice(0)) + this._sendRawMessage(message); + this._listen(); + } + + sendRawMessage(message) { + const parsed = JSON.parse(message); + if (parsed.method.startsWith('Network.')) { + this._sendRawMessage(message); + return true; + } + return false; + } + + disconnect() { + this._target = null; + } + + _sendRawMessage(message) { + if (this._target) { + this._target.runtimeAgent().invoke_evaluate({ + expression: `process._sendNetworkCommand(${message})` + }); + } else { + this._buffer.push(message); + } + } + + async _listen() { + while (this._target) { + const {result: { value: messages }} = await this._target.runtimeAgent().invoke_evaluate({ + expression: `process._fetchNetworkMessages()`, + awaitPromise: true, + returnByValue: true + }); + if (!messages || typeof messages !== 'array') + continue; + for (const message of messages) + this.dispatchMessage(message); + } + } +}; diff --git a/front_end/ndb/module.json b/front_end/ndb/module.json index 4113c13c..83175367 100644 --- a/front_end/ndb/module.json +++ b/front_end/ndb/module.json @@ -56,6 +56,7 @@ "scripts": [ "InspectorFrontendHostOverrides.js", "Connection.js", + "NetworkInterceptor.js", "FileSystem.js", "NdbMain.js" ] From 74ae0e59b12161602b17a76300114df9d73ef6af Mon Sep 17 00:00:00 2001 From: Khang Hoang Date: Sat, 27 Jul 2019 15:17:50 -0700 Subject: [PATCH 12/13] it works now --- front_end/ndb/FileSystem.js | 3 +- .../ndb/InspectorFrontendHostOverrides.js | 94 +++++++++--------- front_end/ndb/NdbMain.js | 9 +- front_end/ndb/NetworkInterceptor.js | 97 +++++++++++++++---- lib/preload/ndb/httpMonkeyPatching.js | 14 ++- 5 files changed, 139 insertions(+), 78 deletions(-) diff --git a/front_end/ndb/FileSystem.js b/front_end/ndb/FileSystem.js index f43df7a8..55a99d94 100644 --- a/front_end/ndb/FileSystem.js +++ b/front_end/ndb/FileSystem.js @@ -77,7 +77,8 @@ Ndb.FileSystem = class extends Persistence.PlatformFileSystem { */ getMetadata(path) { // This method should never be called as long as we are matching using file urls. - throw new Error('not implemented'); + // throw new Error('not implemented'); + return Promise.resolve(''); } /** diff --git a/front_end/ndb/InspectorFrontendHostOverrides.js b/front_end/ndb/InspectorFrontendHostOverrides.js index 58179133..b96fd0cc 100644 --- a/front_end/ndb/InspectorFrontendHostOverrides.js +++ b/front_end/ndb/InspectorFrontendHostOverrides.js @@ -31,52 +31,52 @@ } }; - InspectorFrontendHost.sendMessageToBackend = async rawMessage => { - const parsedMes = JSON.parse(rawMessage); - if (parsedMes.method !== 'Network.getResponseBody') - return; - - const mes = await target.runtimeAgent().invoke_evaluate({ - expression: `process._sendMessage(${JSON.stringify(JSON.parse(rawMessage))})`, - awaitPromise: true - }); - - if (!mes.result) return; - try { - const [id, result] = mes.result.value; - if (result) { - InspectorFrontendHost.events.dispatchEventToListeners( - InspectorFrontendHostAPI.Events.DispatchMessage, - { - id, - result - } - ); - } - } catch (err) { - console.log(err); - } - }; - - while (true) { - const message = await target.runtimeAgent().invoke_evaluate({ - expression: 'process._getNetworkMessages()', - awaitPromise: true - }); - - if (!message.result) return; - const arrMessages = JSON.parse(message.result.value); - - for (const mes of arrMessages) { - const { type, payload } = mes; - - if (type) { - SDK._mainConnection._onMessage(JSON.stringify({ - method: type, - params: payload - })); - } - } - } + // InspectorFrontendHost.sendMessageToBackend = rawMessage => { + // const parsedMes = JSON.parse(rawMessage); + // if (parsedMes.method !== 'Network.getResponseBody') + // return; + // + // const mes = await target.runtimeAgent().invoke_evaluate({ + // expression: `process._sendMessage(${JSON.stringify(JSON.parse(rawMessage))})`, + // awaitPromise: true + // }); + // + // if (!mes.result) return; + // try { + // const [id, result] = mes.result.value; + // if (result) { + // InspectorFrontendHost.events.dispatchEventToListeners( + // InspectorFrontendHostAPI.Events.DispatchMessage, + // { + // id, + // result + // } + // ); + // } + // } catch (err) { + // console.log(err); + // } + // }; + // + // while (true) { + // const message = await target.runtimeAgent().invoke_evaluate({ + // expression: 'process._getNetworkMessages()', + // awaitPromise: true + // }); + // + // if (!message.result) return; + // const arrMessages = JSON.parse(message.result.value); + // + // for (const mes of arrMessages) { + // const { type, payload } = mes; + // + // if (type) { + // SDK._mainConnection._onMessage(JSON.stringify({ + // method: type, + // params: payload + // })); + // } + // } + // } })(); diff --git a/front_end/ndb/NdbMain.js b/front_end/ndb/NdbMain.js index ecbe7d87..de29ea1b 100644 --- a/front_end/ndb/NdbMain.js +++ b/front_end/ndb/NdbMain.js @@ -204,19 +204,20 @@ Ndb.NodeProcessManager = class extends Common.Object { async detected(info, channel) { const connection = await Ndb.Connection.create(channel); - const networkInterceptor = new Ndb.NetworkInterceptor(); - connection.addInterceptor(networkInterceptor); const target = this._targetManager.createTarget( info.id, userFriendlyName(info), SDK.Target.Type.Node, this._targetManager.targetById(info.ppid) || this._targetManager.mainTarget(), undefined, false, connection); - networkInterceptor.setTarget(target); - target[NdbSdk.connectionSymbol] = connection; target.runtimeAgent().invoke_evaluate({ expression: await Ndb.backend.httpMonkeyPatchingSource(), includeCommandLineAPI: true }); + const networkInterceptor = new Ndb.NetworkInterceptor(); + connection.addInterceptor(networkInterceptor); + networkInterceptor.setTarget(target); + target[NdbSdk.connectionSymbol] = connection; + await this.addFileSystem(info.cwd, info.scriptName); if (info.scriptName) { const scriptURL = Common.ParsedURL.platformPathToURL(info.scriptName); diff --git a/front_end/ndb/NetworkInterceptor.js b/front_end/ndb/NetworkInterceptor.js index e70ee4a6..3a2b8a49 100644 --- a/front_end/ndb/NetworkInterceptor.js +++ b/front_end/ndb/NetworkInterceptor.js @@ -2,6 +2,7 @@ Ndb.NetworkInterceptor = class extends Ndb.ConnectionInterceptor { constructor() { super(); this._buffer = []; + this._cacheRequests = []; } setTarget(target) { @@ -20,31 +21,85 @@ Ndb.NetworkInterceptor = class extends Ndb.ConnectionInterceptor { return false; } + setOnMessage(onMessage) { + this._onMessage = onMessage; + } + + dispatchMessage(message) { + if (this._onMessage) this._onMessage(message); + } + disconnect() { this._target = null; } - _sendRawMessage(message) { - if (this._target) { - this._target.runtimeAgent().invoke_evaluate({ - expression: `process._sendNetworkCommand(${message})` - }); - } else { - this._buffer.push(message); - } - } + _sendRawMessage(rawMessage) {} - async _listen() { - while (this._target) { - const {result: { value: messages }} = await this._target.runtimeAgent().invoke_evaluate({ - expression: `process._fetchNetworkMessages()`, - awaitPromise: true, - returnByValue: true - }); - if (!messages || typeof messages !== 'array') - continue; - for (const message of messages) - this.dispatchMessage(message); - } + _listen() { + InspectorFrontendHost.sendMessageToBackend = rawMessage => { + const message = JSON.parse(rawMessage); + + const request = this._cacheRequests.filter(res => { + if ( + res.type === 'Network.getResponseBody' && + res.payload.requestId === message.params.requestId + ) + return res; + })[0]; + + if (request) { + InspectorFrontendHost.events.dispatchEventToListeners( + InspectorFrontendHostAPI.Events.DispatchMessage, + { + id: message.id, + result: { + base64Encoded: true, + body: request.payload.data + } + } + ); + } + }; + + // we need to setTimeout here because the httpMonkeyPatchingSource is loaded + // after this script + setTimeout(async() => { + while (this._target) { + try { + const raw = await this._target + .runtimeAgent() + .invoke_evaluate({ + expression: `process._fetchNetworkMessages()`, + awaitPromise: true, + returnByValue: true + }); + + const { + result: { value: messages } + } = raw; + + if (!messages) return; + + // messages is array-like + const messagesArr = Array.from(JSON.parse(messages)); + + for (const message of messagesArr) { + const { type, payload } = message; + this._cacheRequests.push(message); + + // this is on the way back, this way doesn't work + if (type !== 'Network.getResponseBody') { + // but this does + SDK._mainConnection._onMessage(JSON.stringify({ + method: type, + params: payload + })); + } + } + } catch (err) { + console.log({ err }); + } + } + }, 0); } }; diff --git a/lib/preload/ndb/httpMonkeyPatching.js b/lib/preload/ndb/httpMonkeyPatching.js index a494e6af..84ba7051 100644 --- a/lib/preload/ndb/httpMonkeyPatching.js +++ b/lib/preload/ndb/httpMonkeyPatching.js @@ -36,11 +36,11 @@ const getMineType = mimeType => { return mimeType; }; -let cacheRequests = {}; +const cacheRequests = {}; let id = 1; const getId = () => id++; -let messages = []; +const messages = []; let messageAdded = null; function reportMessage(message) { @@ -51,19 +51,22 @@ function reportMessage(message) { } } -process._getNetworkMessages = async function() { +process._fetchNetworkMessages = async function() { if (!messages.length) await new Promise(resolve => messageAdded = resolve); return JSON.stringify(messages.splice(0)); }; -process._sendMessage = async function(rawMessage) { +process._sendNetworkCommand = async function(rawMessage) { return new Promise(resolve => { - const message = rawMessage; + const message = JSON.parse(rawMessage); + console.log({ cacheRequests }); + console.log({ cacheRequests: cacheRequests[message.params.requestId] }); if (!cacheRequests[message.params.requestId]) { resolve(JSON.stringify({})); } else { if (message.method === 'Network.getResponseBody') { + console.log({ message }); const { base64Encoded, data } = cacheRequests[message.params.requestId]; console.log({ cacheRequests }); @@ -142,6 +145,7 @@ const callbackWrapper = (callback, req) => res => { // Send the response back. reportMessage({ payload: payload, type: 'Network.responseReceived' }); reportMessage({ payload: payload, type: 'Network.loadingFinished' }); + reportMessage({ payload: payload, type: 'Network.getResponseBody' }); }; if (encoding === 'gzip' || encoding === 'x-gzip') { From 971761ba36e62fe132f2df18e06f1ba0059efc76 Mon Sep 17 00:00:00 2001 From: Khang Hoang Date: Sun, 28 Jul 2019 20:04:24 -0700 Subject: [PATCH 13/13] polish the code --- front_end/ndb/NetworkInterceptor.js | 72 +++++++++++++---------------- 1 file changed, 33 insertions(+), 39 deletions(-) diff --git a/front_end/ndb/NetworkInterceptor.js b/front_end/ndb/NetworkInterceptor.js index 3a2b8a49..57abb7a7 100644 --- a/front_end/ndb/NetworkInterceptor.js +++ b/front_end/ndb/NetworkInterceptor.js @@ -35,7 +35,7 @@ Ndb.NetworkInterceptor = class extends Ndb.ConnectionInterceptor { _sendRawMessage(rawMessage) {} - _listen() { + async _listen() { InspectorFrontendHost.sendMessageToBackend = rawMessage => { const message = JSON.parse(rawMessage); @@ -61,45 +61,39 @@ Ndb.NetworkInterceptor = class extends Ndb.ConnectionInterceptor { } }; - // we need to setTimeout here because the httpMonkeyPatchingSource is loaded - // after this script - setTimeout(async() => { - while (this._target) { - try { - const raw = await this._target - .runtimeAgent() - .invoke_evaluate({ - expression: `process._fetchNetworkMessages()`, - awaitPromise: true, - returnByValue: true - }); - - const { - result: { value: messages } - } = raw; - - if (!messages) return; - - // messages is array-like - const messagesArr = Array.from(JSON.parse(messages)); - - for (const message of messagesArr) { - const { type, payload } = message; - this._cacheRequests.push(message); - - // this is on the way back, this way doesn't work - if (type !== 'Network.getResponseBody') { - // but this does - SDK._mainConnection._onMessage(JSON.stringify({ - method: type, - params: payload - })); - } - } - } catch (err) { - console.log({ err }); + while (this._target) { + const rawResponse = await this._target + .runtimeAgent() + .invoke_evaluate({ + expression: `process._fetchNetworkMessages()`, + awaitPromise: true, + returnByValue: true + }); + + if (!rawResponse || !rawResponse.result) return; + + const { + result: { value: messages } + } = rawResponse; + + if (!messages) return; + + // messages is array-like + const messagesArr = Array.from(JSON.parse(messages)); + + for (const message of messagesArr) { + const { type, payload } = message; + this._cacheRequests.push(message); + + // this is on the way back, this way doesn't work + if (type !== 'Network.getResponseBody') { + // but this does + SDK._mainConnection._onMessage(JSON.stringify({ + method: type, + params: payload + })); } } - }, 0); + } } };