From 6d0222059cfeac8bd290980e531ba58f4c481e3a Mon Sep 17 00:00:00 2001 From: cola119 Date: Tue, 2 Jul 2024 23:02:12 +0900 Subject: [PATCH 01/17] inspector: add initial support for network inspection --- lib/inspector.js | 8 + lib/internal/process/pre_execution.js | 57 ++++++ src/inspector/network_agent.cc | 99 +++++++++ src/inspector/network_agent.h | 49 +++++ src/inspector/node_inspector.gypi | 8 + src/inspector/node_network_agent.cc | 99 +++++++++ src/inspector/node_network_agent.h | 45 +++++ src/inspector/node_protocol.pdl | 106 ++++++++++ src/inspector/node_string.cc | 7 +- src/inspector/node_string.h | 1 + src/inspector_agent.cc | 37 ++++ src/inspector_agent.h | 3 + src/inspector_js_api.cc | 16 ++ .../test-inspector-network-domain.mjs | 188 ++++++++++++++++++ 14 files changed, 722 insertions(+), 1 deletion(-) create mode 100644 src/inspector/network_agent.cc create mode 100644 src/inspector/network_agent.h create mode 100644 src/inspector/node_network_agent.cc create mode 100644 src/inspector/node_network_agent.h create mode 100644 test/parallel/test-inspector-network-domain.mjs diff --git a/lib/inspector.js b/lib/inspector.js index e51bcf2f3cd977..3aeb92533b8d7b 100644 --- a/lib/inspector.js +++ b/lib/inspector.js @@ -42,6 +42,7 @@ const { isEnabled, waitForDebugger, console, + emitProtocolEvent, } = internalBinding('inspector'); class Session extends EventEmitter { @@ -188,6 +189,12 @@ function inspectorWaitForDebugger() { throw new ERR_INSPECTOR_NOT_ACTIVE(); } +function inspectorEmitProtocolEvent(eventName, params) { + validateString(eventName, 'eventName'); + validateObject(params, 'params'); + emitProtocolEvent(eventName, JSONStringify(params)); +} + module.exports = { open: inspectorOpen, close: _debugEnd, @@ -195,4 +202,5 @@ module.exports = { waitForDebugger: inspectorWaitForDebugger, console, Session, + emitProtocolEvent: inspectorEmitProtocolEvent, }; diff --git a/lib/internal/process/pre_execution.js b/lib/internal/process/pre_execution.js index 27df0a9440a03c..c840979c11b19a 100644 --- a/lib/internal/process/pre_execution.js +++ b/lib/internal/process/pre_execution.js @@ -3,6 +3,7 @@ const { ArrayPrototypeForEach, Date, + DateNow, DatePrototypeGetDate, DatePrototypeGetFullYear, DatePrototypeGetHours, @@ -98,6 +99,7 @@ function prepareExecution(options) { const mainEntry = patchProcessObject(expandArgv1); setupTraceCategoryState(); setupInspectorHooks(); + setupNetworkInspection(); setupNavigator(); setupWarningHandler(); setupWebStorage(); @@ -438,6 +440,61 @@ function setupInspectorHooks() { } } +function setupNetworkInspection() { + if (!internalBinding('inspector').isEnabled()) { + return; + } + const dc = require('diagnostics_channel'); + const { emitProtocolEvent } = require('inspector'); + + let requestId = 0; + const getNextRequestId = () => `node-network-event-${++requestId}`; + + dc.subscribe('http.client.request.start', ({ request }) => { + const url = `${request.protocol}//${request.host}${request.path}`; + const wallTime = DateNow(); + const timestamp = wallTime / 1000; + request._inspectorRequestId = getNextRequestId(); + emitProtocolEvent('NodeNetwork.requestWillBeSent', { + requestId: request._inspectorRequestId, + timestamp, + wallTime, + request: { + url, + method: request.method, + }, + }); + }); + dc.subscribe('http.client.response.finish', ({ request, response }) => { + if (typeof request._inspectorRequestId !== 'string') { + return; + } + emitProtocolEvent('NodeNetwork.responseReceived', { + requestId: request._inspectorRequestId, + timestamp: DateNow() / 1000, + }); + let responseString = ''; + const onData = (chunk) => { + emitProtocolEvent('NodeNetwork.dataReceived', { + requestId: request._inspectorRequestId, + timestamp: DateNow() / 1000, + dataLength: chunk.length, + }); + responseString += chunk.toString(); + }; + response.on('data', onData); + response.on('end', () => { + emitProtocolEvent('NodeNetwork.loadingFinished', { + requestId: request._inspectorRequestId, + response: responseString, + timestamp: DateNow() / 1000, + encodedDataLength: responseString.length, + }); + response.removeListener('data', onData); + }); + }); +} + // In general deprecations are initialized wherever the APIs are implemented, // this is used to deprecate APIs implemented in C++ where the deprecation // utilities are not easily accessible. diff --git a/src/inspector/network_agent.cc b/src/inspector/network_agent.cc new file mode 100644 index 00000000000000..fe6cd1b0af9cf4 --- /dev/null +++ b/src/inspector/network_agent.cc @@ -0,0 +1,99 @@ +#include "network_agent.h" + +namespace node { +namespace inspector { +namespace protocol { + +std::unique_ptr Request(const String& url, + const String& method) { + return Network::Request::create().setUrl(url).setMethod(method).build(); +} + +NetworkAgent::NetworkAgent() { + event_notifier_map_["requestWillBeSent"] = &NetworkAgent::requestWillBeSent; + event_notifier_map_["responseReceived"] = &NetworkAgent::responseReceived; + event_notifier_map_["dataReceived"] = &NetworkAgent::dataReceived; + event_notifier_map_["loadingFinished"] = &NetworkAgent::loadingFinished; +} + +void NetworkAgent::emitNotification( + const String& event, std::unique_ptr params) { + auto it = event_notifier_map_.find(event); + if (it != event_notifier_map_.end()) { + (this->*(it->second))(std::move(params)); + } +} + +void NetworkAgent::Wire(UberDispatcher* dispatcher) { + frontend_ = std::make_unique(dispatcher->channel()); + Network::Dispatcher::wire(dispatcher, this); +} + +DispatchResponse NetworkAgent::getResponseBody(const String& in_requestId, + String* out_body) { + auto it = request_id_to_response_.find(in_requestId); + if (it != request_id_to_response_.end()) { + *out_body = it->second; + request_id_to_response_.erase(it); + } + return DispatchResponse::OK(); +} + +void NetworkAgent::requestWillBeSent( + std::unique_ptr params) { + String request_id; + params->getString("requestId", &request_id); + double timestamp; + params->getDouble("timestamp", ×tamp); + double wall_time; + params->getDouble("wallTime", &wall_time); + auto request = params->getObject("request"); + String url; + request->getString("url", &url); + String method; + request->getString("method", &method); + + frontend_->requestWillBeSent( + request_id, Request(url, method), timestamp, wall_time); +} + +void NetworkAgent::responseReceived( + std::unique_ptr params) { + String request_id; + params->getString("requestId", &request_id); + double timestamp; + params->getDouble("timestamp", ×tamp); + + frontend_->responseReceived(request_id, timestamp); +} + +void NetworkAgent::dataReceived( + std::unique_ptr params) { + String request_id; + params->getString("requestId", &request_id); + double timestamp; + params->getDouble("timestamp", ×tamp); + int data_length; + params->getInteger("dataLength", &data_length); + + frontend_->dataReceived(request_id, timestamp, data_length); +} + +void NetworkAgent::loadingFinished( + std::unique_ptr params) { + String request_id; + params->getString("requestId", &request_id); + double timestamp; + params->getDouble("timestamp", ×tamp); + int encoded_data_length; + params->getInteger("encodedDataLength", &encoded_data_length); + String response; + params->getString("response", &response); + + request_id_to_response_[request_id] = response; + frontend_->loadingFinished(request_id, timestamp, encoded_data_length); +} + +} // namespace protocol +} // namespace inspector +} // namespace node diff --git a/src/inspector/network_agent.h b/src/inspector/network_agent.h new file mode 100644 index 00000000000000..4c1f01d5baac1a --- /dev/null +++ b/src/inspector/network_agent.h @@ -0,0 +1,49 @@ +#ifndef SRC_INSPECTOR_NETWORK_AGENT_H_ +#define SRC_INSPECTOR_NETWORK_AGENT_H_ + +#include "node/inspector/protocol/Network.h" +#include "v8.h" + +#include + +namespace node { + +namespace inspector { +namespace protocol { + +std::unique_ptr Request(const String& url, + const String& method); + +class NetworkAgent : public Network::Backend { + public: + NetworkAgent(); + + void Wire(UberDispatcher* dispatcher); + + void emitNotification(const String& event, + std::unique_ptr params); + + DispatchResponse getResponseBody(const String& in_requestId, + String* out_body) override; + + void requestWillBeSent(std::unique_ptr params); + + void responseReceived(std::unique_ptr params); + + void dataReceived(std::unique_ptr params); + + void loadingFinished(std::unique_ptr params); + + private: + std::shared_ptr frontend_; + std::unordered_map request_id_to_response_; + using EventNotifier = + void (NetworkAgent::*)(std::unique_ptr); + std::unordered_map event_notifier_map_; +}; + +} // namespace protocol +} // namespace inspector +} // namespace node + +#endif // SRC_INSPECTOR_NETWORK_AGENT_H_ diff --git a/src/inspector/node_inspector.gypi b/src/inspector/node_inspector.gypi index a2dfdcb42db196..0fb3d81762c433 100644 --- a/src/inspector/node_inspector.gypi +++ b/src/inspector/node_inspector.gypi @@ -23,6 +23,10 @@ 'src/inspector/tracing_agent.h', 'src/inspector/worker_agent.cc', 'src/inspector/worker_agent.h', + 'src/inspector/network_agent.cc', + 'src/inspector/network_agent.h', + 'src/inspector/node_network_agent.cc', + 'src/inspector/node_network_agent.h', 'src/inspector/worker_inspector.cc', 'src/inspector/worker_inspector.h', ], @@ -36,6 +40,10 @@ '<(SHARED_INTERMEDIATE_DIR)/src/node/inspector/protocol/NodeTracing.h', '<(SHARED_INTERMEDIATE_DIR)/src/node/inspector/protocol/NodeRuntime.cpp', '<(SHARED_INTERMEDIATE_DIR)/src/node/inspector/protocol/NodeRuntime.h', + '<(SHARED_INTERMEDIATE_DIR)/src/node/inspector/protocol/Network.cpp', + '<(SHARED_INTERMEDIATE_DIR)/src/node/inspector/protocol/Network.h', + '<(SHARED_INTERMEDIATE_DIR)/src/node/inspector/protocol/NodeNetwork.cpp', + '<(SHARED_INTERMEDIATE_DIR)/src/node/inspector/protocol/NodeNetwork.h', ], 'node_protocol_files': [ '<(protocol_tool_path)/lib/Allocator_h.template', diff --git a/src/inspector/node_network_agent.cc b/src/inspector/node_network_agent.cc new file mode 100644 index 00000000000000..9d55211db4cbfe --- /dev/null +++ b/src/inspector/node_network_agent.cc @@ -0,0 +1,99 @@ +#include "node_network_agent.h" +#include "network_agent.h" + +namespace node { +namespace inspector { +namespace protocol { + +NodeNetworkAgent::NodeNetworkAgent(Environment* env) : env_(env) { + event_notifier_map_["requestWillBeSent"] = + &NodeNetworkAgent::requestWillBeSent; + event_notifier_map_["responseReceived"] = &NodeNetworkAgent::responseReceived; + event_notifier_map_["dataReceived"] = &NodeNetworkAgent::dataReceived; + event_notifier_map_["loadingFinished"] = &NodeNetworkAgent::loadingFinished; +} + +void NodeNetworkAgent::emitNotification( + const String& event, std::unique_ptr params) { + auto it = event_notifier_map_.find(event); + if (it != event_notifier_map_.end()) { + (this->*(it->second))(std::move(params)); + } +} + +void NodeNetworkAgent::Wire(UberDispatcher* dispatcher) { + frontend_ = std::make_unique(dispatcher->channel()); + NodeNetwork::Dispatcher::wire(dispatcher, this); +} + +void NodeNetworkAgent::requestWillBeSent( + std::unique_ptr params) { + String request_id; + params->getString("requestId", &request_id); + double timestamp; + params->getDouble("timestamp", ×tamp); + double wall_time; + params->getDouble("wallTime", &wall_time); + auto request = params->getObject("request"); + String url; + request->getString("url", &url); + String method; + request->getString("method", &method); + + frontend_->requestWillBeSent( + request_id, Request(url, method), timestamp, wall_time); + + env_->inspector_agent()->EmitProtocolEvent( + protocol::StringUtil::ToStringView("Network.requestWillBeSent"), + protocol::StringUtil::ToStringView(params->toJSONString())); +} + +void NodeNetworkAgent::responseReceived( + std::unique_ptr params) { + String request_id; + params->getString("requestId", &request_id); + double timestamp; + params->getDouble("timestamp", ×tamp); + + frontend_->responseReceived(request_id, timestamp); + + env_->inspector_agent()->EmitProtocolEvent( + protocol::StringUtil::ToStringView("Network.responseReceived"), + protocol::StringUtil::ToStringView(params->toJSONString())); +} + +void NodeNetworkAgent::dataReceived( + std::unique_ptr params) { + String request_id; + params->getString("requestId", &request_id); + double timestamp; + params->getDouble("timestamp", ×tamp); + int data_length; + params->getInteger("dataLength", &data_length); + + frontend_->dataReceived(request_id, timestamp, data_length); + + env_->inspector_agent()->EmitProtocolEvent( + protocol::StringUtil::ToStringView("Network.dataReceived"), + protocol::StringUtil::ToStringView(params->toJSONString())); +} + +void NodeNetworkAgent::loadingFinished( + std::unique_ptr params) { + String request_id; + params->getString("requestId", &request_id); + double timestamp; + params->getDouble("timestamp", ×tamp); + int encoded_data_length; + params->getInteger("encodedDataLength", &encoded_data_length); + + frontend_->loadingFinished(request_id, timestamp, encoded_data_length); + + env_->inspector_agent()->EmitProtocolEvent( + protocol::StringUtil::ToStringView("Network.loadingFinished"), + protocol::StringUtil::ToStringView(params->toJSONString())); +} + +} // namespace protocol +} // namespace inspector +} // namespace node diff --git a/src/inspector/node_network_agent.h b/src/inspector/node_network_agent.h new file mode 100644 index 00000000000000..98d25311f03489 --- /dev/null +++ b/src/inspector/node_network_agent.h @@ -0,0 +1,45 @@ +#ifndef SRC_INSPECTOR_NODE_NETWORK_AGENT_H_ +#define SRC_INSPECTOR_NODE_NETWORK_AGENT_H_ + +#include "env.h" +#include "node/inspector/protocol/NodeNetwork.h" +#include "v8.h" + +#include + +namespace node { +class Environment; + +namespace inspector { +namespace protocol { + +class NodeNetworkAgent : public NodeNetwork::Backend { + public: + explicit NodeNetworkAgent(Environment* env); + + void emitNotification(const String& event, + std::unique_ptr params); + + void Wire(UberDispatcher* dispatcher); + + void requestWillBeSent(std::unique_ptr params); + + void responseReceived(std::unique_ptr params); + + void dataReceived(std::unique_ptr params); + + void loadingFinished(std::unique_ptr params); + + private: + Environment* env_; + std::shared_ptr frontend_; + using EventNotifier = + void (NodeNetworkAgent::*)(std::unique_ptr); + std::unordered_map event_notifier_map_; +}; + +} // namespace protocol +} // namespace inspector +} // namespace node + +#endif // SRC_INSPECTOR_NODE_NETWORK_AGENT_H_ diff --git a/src/inspector/node_protocol.pdl b/src/inspector/node_protocol.pdl index d8a873de263f23..8c7a162f4831bd 100644 --- a/src/inspector/node_protocol.pdl +++ b/src/inspector/node_protocol.pdl @@ -98,6 +98,112 @@ experimental domain NodeWorker SessionID sessionId string message +experimental domain NodeNetwork + depends on Network + + # Fired when page is about to send HTTP request. + event requestWillBeSent + parameters + # Request identifier. + Network.RequestId requestId + # Request data. + Network.Request request + # Timestamp. + Network.MonotonicTime timestamp + # Timestamp. + Network.TimeSinceEpoch wallTime + + # Fired when HTTP response is available. + event responseReceived + parameters + # Request identifier. + Network.RequestId requestId + # Timestamp. + Network.MonotonicTime timestamp + + event dataReceived + parameters + # Request identifier. + Network.RequestId requestId + # Timestamp. + Network.MonotonicTime timestamp + # Data chunk length. + integer dataLength + + event loadingFinished + parameters + # Request identifier. + Network.RequestId requestId + # Timestamp. + Network.MonotonicTime timestamp + # Total number of bytes received for this request. + number encodedDataLength + +# Partial support for Network domain of ChromeDevTools Protocol. +# https://chromedevtools.github.io/devtools-protocol/tot/Network +experimental domain Network + # Unique request identifier. + type RequestId extends string + + # UTC time in seconds, counted from January 1, 1970. + type TimeSinceEpoch extends number + + # Monotonically increasing time in seconds since an arbitrary point in the past. + type MonotonicTime extends number + + # HTTP request data. + type Request extends object + properties + string url + string method + + # Returns content served for the given request. + command getResponseBody + parameters + # Identifier of the network request to get content for. + RequestId requestId + returns + # Response body. + string body + + # Fired when page is about to send HTTP request. + event requestWillBeSent + parameters + # Request identifier. + RequestId requestId + # Request data. + Request request + # Timestamp. + MonotonicTime timestamp + # Timestamp. + TimeSinceEpoch wallTime + + # Fired when HTTP response is available. + event responseReceived + parameters + # Request identifier. + RequestId requestId + # Timestamp. + MonotonicTime timestamp + + event dataReceived + parameters + # Request identifier. + RequestId requestId + # Timestamp. + MonotonicTime timestamp + # Data chunk length. + integer dataLength + + event loadingFinished + parameters + # Request identifier. + RequestId requestId + # Timestamp. + MonotonicTime timestamp + # Total number of bytes received for this request. + number encodedDataLength + # Support for inspecting node process state. experimental domain NodeRuntime # Enable the NodeRuntime events except by `NodeRuntime.waitingForDisconnect`. diff --git a/src/inspector/node_string.cc b/src/inspector/node_string.cc index 7960971a094fd4..1b62eb9c22ca28 100644 --- a/src/inspector/node_string.cc +++ b/src/inspector/node_string.cc @@ -81,10 +81,15 @@ String StringViewToUtf8(v8_inspector::StringView view) { return String(buffer.out(), utf8_length); } +v8_inspector::StringView ToStringView(const std::string& str) { + return v8_inspector::StringView(reinterpret_cast(str.data()), + str.size()); +} + String fromDouble(double d) { std::ostringstream stream; stream.imbue(std::locale::classic()); // Ignore current locale - stream << d; + stream << std::fixed << d; return stream.str(); } diff --git a/src/inspector/node_string.h b/src/inspector/node_string.h index 369041841ef96d..7c1d755bbb68b4 100644 --- a/src/inspector/node_string.h +++ b/src/inspector/node_string.h @@ -62,6 +62,7 @@ String fromDouble(double d); double toDouble(const char* buffer, size_t length, bool* ok); String StringViewToUtf8(v8_inspector::StringView view); +v8_inspector::StringView ToStringView(const std::string& str); // NOLINTNEXTLINE(runtime/references) void builderAppendQuotedString(StringBuilder& builder, const std::string_view); diff --git a/src/inspector_agent.cc b/src/inspector_agent.cc index 4cab7dea04379c..55e952d99f3541 100644 --- a/src/inspector_agent.cc +++ b/src/inspector_agent.cc @@ -2,6 +2,8 @@ #include "env-inl.h" #include "inspector/main_thread_interface.h" +#include "inspector/network_agent.h" +#include "inspector/node_network_agent.h" #include "inspector/node_string.h" #include "inspector/runtime_agent.h" #include "inspector/tracing_agent.h" @@ -231,6 +233,10 @@ class ChannelImpl final : public v8_inspector::V8Inspector::Channel, } runtime_agent_ = std::make_unique(); runtime_agent_->Wire(node_dispatcher_.get()); + network_agent_ = std::make_unique(); + network_agent_->Wire(node_dispatcher_.get()); + node_network_agent_ = std::make_unique(env); + node_network_agent_->Wire(node_dispatcher_.get()); } ~ChannelImpl() override { @@ -242,6 +248,23 @@ class ChannelImpl final : public v8_inspector::V8Inspector::Channel, } runtime_agent_->disable(); runtime_agent_.reset(); // Dispose before the dispatchers + network_agent_.reset(); // Dispose before the dispatchers + node_network_agent_.reset(); // Dispose before the dispatchers + } + + void emitNotificationFromBackend(const StringView& event, + const StringView& params) { + std::unique_ptr value = + protocol::DictionaryValue::cast( + protocol::StringUtil::parseJSON(params)); + std::string raw_event = protocol::StringUtil::StringViewToUtf8(event); + std::string domain_name = raw_event.substr(0, raw_event.find('.')); + std::string event_name = raw_event.substr(raw_event.find('.') + 1); + if (domain_name == "Network") { + network_agent_->emitNotification(event_name, std::move(value)); + } else if (domain_name == "NodeNetwork") { + node_network_agent_->emitNotification(event_name, std::move(value)); + } } void dispatchProtocolMessage(const StringView& message) { @@ -335,6 +358,8 @@ class ChannelImpl final : public v8_inspector::V8Inspector::Channel, std::unique_ptr runtime_agent_; std::unique_ptr tracing_agent_; std::unique_ptr worker_agent_; + std::unique_ptr network_agent_; + std::unique_ptr node_network_agent_; std::unique_ptr delegate_; std::unique_ptr session_; std::unique_ptr node_dispatcher_; @@ -631,6 +656,12 @@ class NodeInspectorClient : public V8InspectorClient { return retaining_context; } + void emitNotification(const StringView& event, const StringView& params) { + for (const auto& id_channel : channels_) { + id_channel.second->emitNotificationFromBackend(event, params); + } + } + std::shared_ptr getThreadHandle() { if (!interface_) { interface_ = std::make_shared( @@ -844,6 +875,12 @@ std::unique_ptr Agent::ConnectToMainThread( prevent_shutdown); } +void Agent::EmitProtocolEvent(const StringView& event, + const StringView& params) { + if (!IsListening()) return; + client_->emitNotification(event, params); +} + void Agent::WaitForDisconnect() { THROW_IF_INSUFFICIENT_PERMISSIONS(parent_env_, permission::PermissionScope::kInspector, diff --git a/src/inspector_agent.h b/src/inspector_agent.h index 725275e43c7135..1aeb3239d5938d 100644 --- a/src/inspector_agent.h +++ b/src/inspector_agent.h @@ -69,6 +69,9 @@ class Agent { void ReportUncaughtException(v8::Local error, v8::Local message); + void EmitProtocolEvent(const v8_inspector::StringView& event, + const v8_inspector::StringView& params); + // Async stack traces instrumentation. void AsyncTaskScheduled(const v8_inspector::StringView& taskName, void* task, bool recurring); diff --git a/src/inspector_js_api.cc b/src/inspector_js_api.cc index 5700f8c5efc698..4aa8d5ac385273 100644 --- a/src/inspector_js_api.cc +++ b/src/inspector_js_api.cc @@ -270,6 +270,18 @@ static void RegisterAsyncHookWrapper(const FunctionCallbackInfo& args) { enable_function, disable_function); } +void EmitProtocolEvent(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + CHECK(args[0]->IsString()); + Local eventName = args[0].As(); + CHECK(args[1]->IsString()); + Local params = args[1].As(); + + env->inspector_agent()->EmitProtocolEvent( + ToProtocolString(env->isolate(), eventName)->string(), + ToProtocolString(env->isolate(), params)->string()); +} + void IsEnabled(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); args.GetReturnValue().Set(env->inspector_agent()->IsListening()); @@ -355,6 +367,8 @@ void Initialize(Local target, Local unused, SetMethod(context, target, "registerAsyncHook", RegisterAsyncHookWrapper); SetMethodNoSideEffect(context, target, "isEnabled", IsEnabled); + SetMethod(context, target, "emitProtocolEvent", EmitProtocolEvent); + Local console_string = FIXED_ONE_BYTE_STRING(isolate, "console"); // Grab the console from the binding object and expose those to our binding @@ -388,6 +402,8 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) { registry->Register(RegisterAsyncHookWrapper); registry->Register(IsEnabled); + registry->Register(EmitProtocolEvent); + registry->Register(JSBindingsConnection::New); registry->Register(JSBindingsConnection::Dispatch); registry->Register(JSBindingsConnection::Disconnect); diff --git a/test/parallel/test-inspector-network-domain.mjs b/test/parallel/test-inspector-network-domain.mjs new file mode 100644 index 00000000000000..07e861bf0d20b0 --- /dev/null +++ b/test/parallel/test-inspector-network-domain.mjs @@ -0,0 +1,188 @@ +// Flags: --inspect=0 + +import * as common from '../common/index.mjs'; + +common.skipIfInspectorDisabled(); + +import assert from 'node:assert'; +import * as fixtures from '../common/fixtures.mjs'; +import http from 'node:http'; +import https from 'node:https'; +import inspector from 'node:inspector'; + +const session = new inspector.Session(); +session.connect(); + +const httpServer = http.createServer((req, res) => { + const path = req.url; + switch (path) { + case '/hello-world': + res.writeHead(200); + res.end('hello world\n'); + break; + default: + assert(false, `Unexpected path: ${path}`); + } +}); + +const httpsServer = https.createServer({ + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem') +}, (req, res) => { + const path = req.url; + switch (path) { + case '/hello-world': + res.writeHead(200); + res.end('hello world\n'); + break; + default: + assert(false, `Unexpected path: ${path}`); + } +}); + +const terminate = () => { + session.disconnect(); + httpServer.close(); + httpsServer.close(); + inspector.close(); +}; + +const testHttpGet = () => new Promise((resolve, reject) => { + let nodeNetworkEventFinished = false; + let networkEventFinished = false; + + session.on('NodeNetwork.requestWillBeSent', common.mustCall(({ params }) => { + assert.ok(params.requestId.startsWith('node-network-event-')); + assert.strictEqual(params.request.url, 'http://127.0.0.1/hello-world'); + assert.strictEqual(params.request.method, 'GET'); + assert.strictEqual(typeof params.timestamp, 'number'); + assert.strictEqual(typeof params.wallTime, 'number'); + })); + session.on('NodeNetwork.responseReceived', common.mustCall(({ params }) => { + assert.ok(params.requestId.startsWith('node-network-event-')); + assert.strictEqual(typeof params.timestamp, 'number'); + })); + session.on('NodeNetwork.dataReceived', common.mustCall(({ params }) => { + assert.ok(params.requestId.startsWith('node-network-event-')); + assert.strictEqual(typeof params.timestamp, 'number'); + assert.strictEqual(typeof params.dataLength, 'number'); + })); + session.on('NodeNetwork.loadingFinished', common.mustCall(({ params }) => { + assert.ok(params.requestId.startsWith('node-network-event-')); + assert.strictEqual(typeof params.timestamp, 'number'); + nodeNetworkEventFinished = true; + if (networkEventFinished) { + resolve(); + } + })); + + session.on('Network.requestWillBeSent', common.mustCall(({ params }) => { + assert.ok(params.requestId.startsWith('node-network-event-')); + assert.strictEqual(params.request.url, 'http://127.0.0.1/hello-world'); + assert.strictEqual(params.request.method, 'GET'); + assert.strictEqual(typeof params.timestamp, 'number'); + assert.strictEqual(typeof params.wallTime, 'number'); + })); + session.on('Network.responseReceived', common.mustCall(({ params }) => { + assert.ok(params.requestId.startsWith('node-network-event-')); + assert.strictEqual(typeof params.timestamp, 'number'); + })); + session.on('Network.dataReceived', common.mustCall(({ params }) => { + assert.ok(params.requestId.startsWith('node-network-event-')); + assert.strictEqual(typeof params.timestamp, 'number'); + assert.strictEqual(typeof params.dataLength, 'number'); + })); + session.on('Network.loadingFinished', common.mustCall(({ params }) => { + assert.ok(params.requestId.startsWith('node-network-event-')); + assert.strictEqual(typeof params.timestamp, 'number'); + networkEventFinished = true; + if (nodeNetworkEventFinished) { + resolve(); + } + })); + + http.get({ + host: '127.0.0.1', + port: httpServer.address().port, + path: '/hello-world', + }, common.mustCall()); +}); + +const testHttpsGet = () => new Promise((resolve, reject) => { + let nodeNetworkEventFinished = false; + let networkEventFinished = false; + + session.on('NodeNetwork.requestWillBeSent', common.mustCall(({ params }) => { + assert.ok(params.requestId.startsWith('node-network-event-')); + assert.strictEqual(params.request.url, 'https://127.0.0.1/hello-world'); + assert.strictEqual(params.request.method, 'GET'); + assert.strictEqual(typeof params.timestamp, 'number'); + assert.strictEqual(typeof params.wallTime, 'number'); + })); + session.on('NodeNetwork.responseReceived', common.mustCall(({ params }) => { + assert.ok(params.requestId.startsWith('node-network-event-')); + assert.strictEqual(typeof params.timestamp, 'number'); + })); + session.on('NodeNetwork.dataReceived', common.mustCall(({ params }) => { + assert.ok(params.requestId.startsWith('node-network-event-')); + assert.strictEqual(typeof params.timestamp, 'number'); + assert.strictEqual(typeof params.dataLength, 'number'); + })); + session.on('NodeNetwork.loadingFinished', common.mustCall(({ params }) => { + assert.ok(params.requestId.startsWith('node-network-event-')); + assert.strictEqual(typeof params.timestamp, 'number'); + nodeNetworkEventFinished = true; + if (networkEventFinished) { + resolve(); + } + })); + + session.on('Network.requestWillBeSent', common.mustCall(({ params }) => { + assert.ok(params.requestId.startsWith('node-network-event-')); + assert.strictEqual(params.request.url, 'https://127.0.0.1/hello-world'); + assert.strictEqual(params.request.method, 'GET'); + assert.strictEqual(typeof params.timestamp, 'number'); + assert.strictEqual(typeof params.wallTime, 'number'); + })); + session.on('Network.responseReceived', common.mustCall(({ params }) => { + assert.ok(params.requestId.startsWith('node-network-event-')); + assert.strictEqual(typeof params.timestamp, 'number'); + })); + session.on('Network.dataReceived', common.mustCall(({ params }) => { + assert.ok(params.requestId.startsWith('node-network-event-')); + assert.strictEqual(typeof params.timestamp, 'number'); + assert.strictEqual(typeof params.dataLength, 'number'); + })); + session.on('Network.loadingFinished', common.mustCall(({ params }) => { + assert.ok(params.requestId.startsWith('node-network-event-')); + assert.strictEqual(typeof params.timestamp, 'number'); + networkEventFinished = true; + if (nodeNetworkEventFinished) { + resolve(); + } + })); + + https.get({ + host: '127.0.0.1', + port: httpsServer.address().port, + path: '/hello-world', + rejectUnauthorized: false, + }, common.mustCall()); +}); + +const test = async () => { + await testHttpGet(); + session.removeAllListeners(); + await testHttpsGet(); + session.removeAllListeners(); +}; + +httpServer.listen(0, () => { + httpsServer.listen(0, () => { + test().then(common.mustCall()).catch(() => { + assert.fail(); + }).finally(() => { + terminate(); + }); + }); +}); From 5cfddc302b696e8b8a1c0a80f0df2810d11f687f Mon Sep 17 00:00:00 2001 From: cola119 Date: Wed, 3 Jul 2024 17:38:12 +0900 Subject: [PATCH 02/17] inspector: add a document and a test for `inspector.emitProtocolEvent` --- doc/api/inspector.md | 28 +++++++++++ lib/inspector.js | 6 ++- .../test-inspector-emit-protocol-event.mjs | 46 +++++++++++++++++++ 3 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 test/parallel/test-inspector-emit-protocol-event.mjs diff --git a/doc/api/inspector.md b/doc/api/inspector.md index 014581e7dc9c56..4568dd0631489b 100644 --- a/doc/api/inspector.md +++ b/doc/api/inspector.md @@ -429,6 +429,34 @@ require('node:inspector').console.log('a message'); The inspector console does not have API parity with Node.js console. +### `inspector.emitProtocolEvent(eventName[, params])` + + + +> Stability: 1 - Experimental + +* `eventName` {string} +* `params` {Object} + +Emits a protocol event with the specified `eventName` and optional `params`. This function allows you to send +custom events that conform to the DevTools protocol, facilitating integration with debugging and inspection tools. +The emitted events can be captured by connected DevTools Frontend instances, such as Chrome DevTools. + +```js +inspector.emitProtocolEvent('NodeNetwork.requestWillBeSent', { + requestId: 'request-id-1', + timestamp: Date.now() / 1000, + wallTime: Date.now(), + request: { + url: 'https://nodejs.org/en', + method: 'GET', + } +}); +``` + ### `inspector.open([port[, host[, wait]]])` + +> Stability: 1 - Experimental + +Enable experimental support for the network inspection with Chrome DevTools. + ### `--experimental-permission` - -> Stability: 1 - Experimental - -* `eventName` {string} -* `params` {Object} - -This feature is only available with the `--experimental-network-inspection` flag enabled. - -Emits a protocol event with the specified `eventName` and optional `params`. This function allows you to send -custom events that conform to the DevTools protocol, facilitating integration with debugging and inspection tools. -The emitted events can be captured by connected DevTools Frontend instances, such as Chrome DevTools. - -```js -inspector.emitProtocolEvent('NodeNetwork.requestWillBeSent', { - requestId: 'request-id-1', - timestamp: Date.now() / 1000, - wallTime: Date.now(), - request: { - url: 'https://nodejs.org/en', - method: 'GET', - } -}); -``` - ### `inspector.open([port[, host[, wait]]])` + +> Stability: 1 - Experimental + +* `params` {Object} + +This feature is only available with the `--experimental-network-inspection` flag enabled. + +Emits the `NodeNetwork.requestWillBeSent` event. This event indicates that the application is +about to send an HTTP request. + +### `inspector.NodeNetwork.responseReceived([params])` + + + +> Stability: 1 - Experimental + +* `params` {Object} + +This feature is only available with the `--experimental-network-inspection` flag enabled. + +Emits the `NodeNetwork.responseReceived` event. This event indicates that HTTP response is +available. + +### `inspector.NodeNetwork.loadingFinished([params])` + + + +> Stability: 1 - Experimental + +* `params` {Object} + +This feature is only available with the `--experimental-network-inspection` flag enabled. + +Emits the `NodeNetwork.loadingFinished` event. This event indicates that HTTP request has +finished loading. + ## Support of breakpoints The Chrome DevTools Protocol [`Debugger` domain][] allows an diff --git a/lib/inspector.js b/lib/inspector.js index 4f4df6978f4a63..729ebe5ca81156 100644 --- a/lib/inspector.js +++ b/lib/inspector.js @@ -189,7 +189,7 @@ function inspectorWaitForDebugger() { throw new ERR_INSPECTOR_NOT_ACTIVE(); } -function inspectorEmitProtocolEvent(eventName, params) { +function emit(eventName, params) { validateString(eventName, 'eventName'); if (params) { validateObject(params, 'params'); @@ -197,6 +197,12 @@ function inspectorEmitProtocolEvent(eventName, params) { emitProtocolEvent(eventName, JSONStringify(params ?? {})); } +const NodeNetwork = { + requestWillBeSent: (params) => emit('NodeNetwork.requestWillBeSent', params), + responseReceived: (params) => emit('NodeNetwork.responseReceived', params), + loadingFinished: (params) => emit('NodeNetwork.loadingFinished', params), +}; + module.exports = { open: inspectorOpen, close: _debugEnd, @@ -204,5 +210,5 @@ module.exports = { waitForDebugger: inspectorWaitForDebugger, console, Session, - emitProtocolEvent: inspectorEmitProtocolEvent, + NodeNetwork, }; diff --git a/lib/internal/inspector_network_tracking.js b/lib/internal/inspector_network_tracking.js index f8d38d72f680fe..b1bf4396a3e612 100644 --- a/lib/internal/inspector_network_tracking.js +++ b/lib/internal/inspector_network_tracking.js @@ -9,7 +9,7 @@ const { } = require('internal/options'); let dc; -let emitProtocolEvent; +let NodeNetwork; let requestId = 0; const getNextRequestId = () => `node-network-event-${++requestId}`; @@ -19,7 +19,7 @@ function onClientRequestStart({ request }) { const wallTime = DateNow(); const timestamp = wallTime / 1000; request._inspectorRequestId = getNextRequestId(); - emitProtocolEvent('NodeNetwork.requestWillBeSent', { + NodeNetwork.requestWillBeSent({ requestId: request._inspectorRequestId, timestamp, wallTime, @@ -35,11 +35,11 @@ function onClientResponseFinish({ request }) { return; } const timestamp = DateNow() / 1000; - emitProtocolEvent('NodeNetwork.responseReceived', { + NodeNetwork.responseReceived({ requestId: request._inspectorRequestId, timestamp, }); - emitProtocolEvent('NodeNetwork.loadingFinished', { + NodeNetwork.loadingFinished({ requestId: request._inspectorRequestId, timestamp, }); @@ -52,8 +52,8 @@ function enable() { if (!dc) { dc = require('diagnostics_channel'); } - if (!emitProtocolEvent) { - emitProtocolEvent = require('inspector').emitProtocolEvent; + if (!NodeNetwork) { + NodeNetwork = require('inspector').NodeNetwork; } dc.subscribe('http.client.request.start', onClientRequestStart); dc.subscribe('http.client.response.finish', onClientResponseFinish); diff --git a/src/inspector/network_agent.cc b/src/inspector/network_agent.cc index 491fe3f07c205d..3a4dc27d6a9a5d 100644 --- a/src/inspector/network_agent.cc +++ b/src/inspector/network_agent.cc @@ -17,6 +17,7 @@ NetworkAgent::NetworkAgent(Environment* env) : enabled_(false), env_(env) { void NetworkAgent::emitNotification( const String& event, std::unique_ptr params) { + if (!enabled_) return; auto it = event_notifier_map_.find(event); if (it != event_notifier_map_.end()) { (this->*(it->second))(std::move(params)); diff --git a/src/inspector/node_network_agent.cc b/src/inspector/node_network_agent.cc index 8f67b75d7bbc8b..a3d25c66318cae 100644 --- a/src/inspector/node_network_agent.cc +++ b/src/inspector/node_network_agent.cc @@ -15,6 +15,7 @@ NodeNetworkAgent::NodeNetworkAgent(Environment* env) void NodeNetworkAgent::emitNotification( const String& event, std::unique_ptr params) { + if (!enabled_) return; auto it = event_notifier_map_.find(event); if (it != event_notifier_map_.end()) { (this->*(it->second))(std::move(params)); diff --git a/src/inspector_agent.cc b/src/inspector_agent.cc index df5cec7fad6975..10f14acc1a6c1d 100644 --- a/src/inspector_agent.cc +++ b/src/inspector_agent.cc @@ -266,6 +266,8 @@ class ChannelImpl final : public v8_inspector::V8Inspector::Channel, network_agent_->emitNotification(event_name, std::move(value)); } else if (domain_name == "NodeNetwork") { node_network_agent_->emitNotification(event_name, std::move(value)); + } else { + UNREACHABLE("Unknown domain for emitNotificationFromBackend"); } } @@ -284,8 +286,17 @@ class ChannelImpl final : public v8_inspector::V8Inspector::Channel, Utf8ToStringView(method)->string())) { session_->dispatchProtocolMessage(message); } else { - node_dispatcher_->dispatch(call_id, method, std::move(value), - raw_message); + node_dispatcher_->dispatch( + call_id, method, std::move(value), raw_message); + // Since the `Network` domain is not directly accessible to inspector + // module users, this allows for them to enable/disable the `Network` + // domain via the `NodeNetwork` domain. + if (method == "NodeNetwork.enable") { + network_agent_->enable(); + } + if (method == "NodeNetwork.disable") { + network_agent_->disable(); + } } } diff --git a/test/parallel/test-inspector-emit-protocol-event.mjs b/test/parallel/test-inspector-emit-protocol-event.mjs index 3a39e294ae92e9..cdbdf312202395 100644 --- a/test/parallel/test-inspector-emit-protocol-event.mjs +++ b/test/parallel/test-inspector-emit-protocol-event.mjs @@ -4,43 +4,76 @@ import * as common from '../common/index.mjs'; common.skipIfInspectorDisabled(); -import inspector from 'node:inspector'; +import inspector from 'node:inspector/promises'; import assert from 'node:assert'; +const EXPECTED_EVENTS = { + NodeNetwork: [ + { + name: 'requestWillBeSent', + params: { + requestId: 'request-id-1', + request: { + url: 'https://nodejs.org/en', + method: 'GET' + }, + timestamp: 1000, + wallTime: 1000, + } + }, + { + name: 'responseReceived', + params: { + requestId: 'request-id-1', + timestamp: 1000, + } + }, + { + name: 'loadingFinished', + params: { + requestId: 'request-id-1', + timestamp: 1000, + } + }, + ] +}; + +// Check that all domains and events are present in the inspector object. +for (const [domain, events] of Object.entries(EXPECTED_EVENTS)) { + if (!(domain in inspector)) { + assert.fail(`Expected domain ${domain} to be present in inspector`); + } + const actualEventNames = Object.keys(inspector[domain]); + const expectedEventNames = events.map((event) => event.name); + assert.deepStrictEqual(actualEventNames, expectedEventNames, `Expected ${domain} to have events ${expectedEventNames}, but got ${actualEventNames}`); +} + +// Check that all events throw when called with a non-object argument. +for (const [domain, events] of Object.entries(EXPECTED_EVENTS)) { + for (const event of events) { + assert.throws(() => inspector[domain][event.name]('params'), { + name: 'TypeError', + code: 'ERR_INVALID_ARG_TYPE', + message: 'The "params" argument must be of type object. Received type string (\'params\')' + }); + } +} + const session = new inspector.Session(); session.connect(); -assert.throws(() => inspector.emitProtocolEvent(123), { - name: 'TypeError', - code: 'ERR_INVALID_ARG_TYPE', - message: 'The "eventName" argument must be of type string. Received type number (123)' -}); - -assert.throws(() => inspector.emitProtocolEvent('NodeNetwork.requestWillBeSent', 'params'), { - name: 'TypeError', - code: 'ERR_INVALID_ARG_TYPE', - message: 'The "params" argument must be of type object. Received type string (\'params\')' -}); - -session.on('NodeNetwork.requestWillBeSent', common.mustCall(({ params }) => { - assert.deepStrictEqual(params, { - requestId: 'request-id-1', - request: { - url: 'https://nodejs.org/en', - method: 'GET' - }, - timestamp: 1000, - wallTime: 1000, - }); -})); - -inspector.emitProtocolEvent('NodeNetwork.requestWillBeSent', { - requestId: 'request-id-1', - request: { - url: 'https://nodejs.org/en', - method: 'GET' - }, - timestamp: 1000, - wallTime: 1000, - unknown: 'unknown' -}); +// Check that all events emit the expected parameters. +await session.post('NodeNetwork.enable'); +for (const [domain, events] of Object.entries(EXPECTED_EVENTS)) { + for (const event of events) { + session.on(`${domain}.${event.name}`, common.mustCall(({ params }) => { + assert.deepStrictEqual(params, event.params); + })); + inspector[domain][event.name](event.params); + } +} + +// Check tht no events are emitted after disabling the domain. +await session.post('NodeNetwork.disable'); +session.on('NodeNetwork.requestWillBeSent', common.mustNotCall()); +inspector.NodeNetwork.requestWillBeSent({}); From 54f6650cfcd3950fa2989dd412e062e5b5d3474f Mon Sep 17 00:00:00 2001 From: cola119 Date: Wed, 10 Jul 2024 23:33:41 +0900 Subject: [PATCH 07/17] test: workaround for `skipIfInspectorDisabled` issue in without-ssl build --- ... => test-inspector-emit-protocol-event.js} | 44 +++++++++++-------- ...n.mjs => test-inspector-network-domain.js} | 14 +++--- 2 files changed, 32 insertions(+), 26 deletions(-) rename test/parallel/{test-inspector-emit-protocol-event.mjs => test-inspector-emit-protocol-event.js} (62%) rename test/parallel/{test-inspector-network-domain.mjs => test-inspector-network-domain.js} (95%) diff --git a/test/parallel/test-inspector-emit-protocol-event.mjs b/test/parallel/test-inspector-emit-protocol-event.js similarity index 62% rename from test/parallel/test-inspector-emit-protocol-event.mjs rename to test/parallel/test-inspector-emit-protocol-event.js index cdbdf312202395..1e0d3770797dd6 100644 --- a/test/parallel/test-inspector-emit-protocol-event.mjs +++ b/test/parallel/test-inspector-emit-protocol-event.js @@ -1,11 +1,11 @@ // Flags: --inspect=0 --experimental-network-inspection - -import * as common from '../common/index.mjs'; +'use strict'; +const common = require('../common'); common.skipIfInspectorDisabled(); -import inspector from 'node:inspector/promises'; -import assert from 'node:assert'; +const inspector = require('node:inspector/promises'); +const assert = require('node:assert'); const EXPECTED_EVENTS = { NodeNetwork: [ @@ -59,21 +59,27 @@ for (const [domain, events] of Object.entries(EXPECTED_EVENTS)) { } } -const session = new inspector.Session(); -session.connect(); +const runAsyncTest = async () => { + const session = new inspector.Session(); + session.connect(); -// Check that all events emit the expected parameters. -await session.post('NodeNetwork.enable'); -for (const [domain, events] of Object.entries(EXPECTED_EVENTS)) { - for (const event of events) { - session.on(`${domain}.${event.name}`, common.mustCall(({ params }) => { - assert.deepStrictEqual(params, event.params); - })); - inspector[domain][event.name](event.params); + // Check that all events emit the expected parameters. + await session.post('NodeNetwork.enable'); + for (const [domain, events] of Object.entries(EXPECTED_EVENTS)) { + for (const event of events) { + session.on(`${domain}.${event.name}`, common.mustCall(({ params }) => { + assert.deepStrictEqual(params, event.params); + })); + inspector[domain][event.name](event.params); + } } -} -// Check tht no events are emitted after disabling the domain. -await session.post('NodeNetwork.disable'); -session.on('NodeNetwork.requestWillBeSent', common.mustNotCall()); -inspector.NodeNetwork.requestWillBeSent({}); + // Check tht no events are emitted after disabling the domain. + await session.post('NodeNetwork.disable'); + session.on('NodeNetwork.requestWillBeSent', common.mustNotCall()); + inspector.NodeNetwork.requestWillBeSent({}); +}; + +runAsyncTest().then(common.mustCall()).catch((e) => { + assert.fail(e); +}); diff --git a/test/parallel/test-inspector-network-domain.mjs b/test/parallel/test-inspector-network-domain.js similarity index 95% rename from test/parallel/test-inspector-network-domain.mjs rename to test/parallel/test-inspector-network-domain.js index 742ab428b64241..f24f6b4d180f01 100644 --- a/test/parallel/test-inspector-network-domain.mjs +++ b/test/parallel/test-inspector-network-domain.js @@ -1,14 +1,14 @@ // Flags: --inspect=0 --experimental-network-inspection - -import * as common from '../common/index.mjs'; +'use strict'; +const common = require('../common'); common.skipIfInspectorDisabled(); -import assert from 'node:assert'; -import * as fixtures from '../common/fixtures.mjs'; -import http from 'node:http'; -import https from 'node:https'; -import inspector from 'node:inspector/promises'; +const assert = require('node:assert'); +const fixtures = require('../common/fixtures'); +const http = require('node:http'); +const https = require('node:https'); +const inspector = require('node:inspector/promises'); const session = new inspector.Session(); session.connect(); From afc4dbb66b02ae57d7d3935989ab131205fbe615 Mon Sep 17 00:00:00 2001 From: cola119 Date: Wed, 10 Jul 2024 23:34:43 +0900 Subject: [PATCH 08/17] cli: remove `--experimental-network-inspection` from NODE_OPTIONS --- doc/api/cli.md | 1 - src/node_options.cc | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/doc/api/cli.md b/doc/api/cli.md index c8089d05ca55c8..e31f61adf2cd21 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -2858,7 +2858,6 @@ one is included in the list below. * `--experimental-loader` * `--experimental-modules` * `--experimental-network-imports` -* `--experimental-network-inspection` * `--experimental-permission` * `--experimental-print-required-tla` * `--experimental-require-module` diff --git a/src/node_options.cc b/src/node_options.cc index 0b17f59dec3c49..2b20ba16ab3232 100644 --- a/src/node_options.cc +++ b/src/node_options.cc @@ -593,8 +593,7 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() { &EnvironmentOptions::cpu_prof_dir); AddOption("--experimental-network-inspection", "experimental network inspection support", - &EnvironmentOptions::experimental_network_inspection, - kAllowedInEnvvar); + &EnvironmentOptions::experimental_network_inspection); AddOption( "--heap-prof", "Start the V8 heap profiler on start up, and write the heap profile " From 658318d87e69fdb2c502558a81c89f4354f456b0 Mon Sep 17 00:00:00 2001 From: cola119 Date: Thu, 11 Jul 2024 00:23:32 +0900 Subject: [PATCH 09/17] inspector: correctly enable/disable `NodeNetwork` domain via `Network` domain --- src/inspector_agent.cc | 27 ++++++++++++------- .../parallel/test-inspector-network-domain.js | 22 ++++++++++----- 2 files changed, 33 insertions(+), 16 deletions(-) diff --git a/src/inspector_agent.cc b/src/inspector_agent.cc index 10f14acc1a6c1d..33095757fea774 100644 --- a/src/inspector_agent.cc +++ b/src/inspector_agent.cc @@ -288,15 +288,24 @@ class ChannelImpl final : public v8_inspector::V8Inspector::Channel, } else { node_dispatcher_->dispatch( call_id, method, std::move(value), raw_message); - // Since the `Network` domain is not directly accessible to inspector - // module users, this allows for them to enable/disable the `Network` - // domain via the `NodeNetwork` domain. - if (method == "NodeNetwork.enable") { - network_agent_->enable(); - } - if (method == "NodeNetwork.disable") { - network_agent_->disable(); - } + } + // Since the `Network` domain is not directly accessible to inspector + // module users, this allows for them to enable/disable the `Network` + // domain via the `NodeNetwork` domain. + if (method == "NodeNetwork.enable") { + network_agent_->enable(); + } + if (method == "NodeNetwork.disable") { + network_agent_->disable(); + } + // Since DevTools Frontend sends commands in the `Network` domain only, + // we need to enable/disable the `NodeNetwork` domain via the `Network` + // domain. + if (method == "Network.enable") { + node_network_agent_->enable(); + } + if (method == "Network.disable") { + node_network_agent_->disable(); } } diff --git a/test/parallel/test-inspector-network-domain.js b/test/parallel/test-inspector-network-domain.js index f24f6b4d180f01..65d884afcc135e 100644 --- a/test/parallel/test-inspector-network-domain.js +++ b/test/parallel/test-inspector-network-domain.js @@ -150,21 +150,29 @@ const testHttpsGet = () => new Promise((resolve, reject) => { }, common.mustCall()); }); -const test = async () => { - await session.post('NodeNetwork.enable'); +const testNetworkInspection = async () => { await testHttpGet(); session.removeAllListeners(); await testHttpsGet(); session.removeAllListeners(); - await session.post('NodeNetwork.disable'); }; httpServer.listen(0, () => { - httpsServer.listen(0, () => { - test().then(common.mustCall()).catch((e) => { + httpsServer.listen(0, async () => { + try { + await session.post('NodeNetwork.enable'); + await testNetworkInspection() + await session.post('NodeNetwork.disable'); + + // ChromeDevTools sends a 'Network.enable' command to enable network inspection. + // This test ensures that the 'Network.enable' command correctly enables network inspection. + await session.post('Network.enable'); + await testNetworkInspection() + await session.post('Network.disable'); + } catch (e) { assert.fail(e); - }).finally(() => { + } finally { terminate(); - }); + } }); }); From 877e68cbc31d0f5f9f2a9db0d701ad37f7bc1586 Mon Sep 17 00:00:00 2001 From: cola119 Date: Thu, 11 Jul 2024 00:25:00 +0900 Subject: [PATCH 10/17] inspector: fix the failure of `tools/test.py --worker` --- src/inspector_agent.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/inspector_agent.cc b/src/inspector_agent.cc index 33095757fea774..1960734a71bf67 100644 --- a/src/inspector_agent.cc +++ b/src/inspector_agent.cc @@ -899,8 +899,7 @@ std::unique_ptr Agent::ConnectToMainThread( void Agent::EmitProtocolEvent(const StringView& event, const StringView& params) { - if (!IsListening() || !env()->options()->experimental_network_inspection) - return; + if (!env()->options()->experimental_network_inspection) return; client_->emitNotification(event, params); } From 5c058f9bf4c3d202aad623cceda67364f7dde400 Mon Sep 17 00:00:00 2001 From: cola119 Date: Thu, 11 Jul 2024 00:33:24 +0900 Subject: [PATCH 11/17] lint fix --- test/parallel/test-inspector-network-domain.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/parallel/test-inspector-network-domain.js b/test/parallel/test-inspector-network-domain.js index 65d884afcc135e..94d79797c9f03d 100644 --- a/test/parallel/test-inspector-network-domain.js +++ b/test/parallel/test-inspector-network-domain.js @@ -161,13 +161,13 @@ httpServer.listen(0, () => { httpsServer.listen(0, async () => { try { await session.post('NodeNetwork.enable'); - await testNetworkInspection() + await testNetworkInspection(); await session.post('NodeNetwork.disable'); // ChromeDevTools sends a 'Network.enable' command to enable network inspection. // This test ensures that the 'Network.enable' command correctly enables network inspection. await session.post('Network.enable'); - await testNetworkInspection() + await testNetworkInspection(); await session.post('Network.disable'); } catch (e) { assert.fail(e); From 0bf5221700c62a2f02f9f8cfb808ff28605b0d96 Mon Sep 17 00:00:00 2001 From: cola119 Date: Thu, 11 Jul 2024 10:28:28 +0900 Subject: [PATCH 12/17] inspector: check `--experimental-network-inspection` in `pre_execution.js` --- lib/internal/inspector_network_tracking.js | 10 ---------- lib/internal/process/pre_execution.js | 2 +- test/parallel/test-bootstrap-modules.js | 1 - 3 files changed, 1 insertion(+), 12 deletions(-) diff --git a/lib/internal/inspector_network_tracking.js b/lib/internal/inspector_network_tracking.js index b1bf4396a3e612..4f164d2c41df56 100644 --- a/lib/internal/inspector_network_tracking.js +++ b/lib/internal/inspector_network_tracking.js @@ -4,10 +4,6 @@ const { DateNow, } = primordials; -const { - getOptionValue, -} = require('internal/options'); - let dc; let NodeNetwork; @@ -46,9 +42,6 @@ function onClientResponseFinish({ request }) { } function enable() { - if (!getOptionValue('--experimental-network-inspection')) { - return; - } if (!dc) { dc = require('diagnostics_channel'); } @@ -60,9 +53,6 @@ function enable() { } function disable() { - if (!getOptionValue('--experimental-network-inspection')) { - return; - } dc.unsubscribe('http.client.request.start', onClientRequestStart); dc.unsubscribe('http.client.response.finish', onClientResponseFinish); } diff --git a/lib/internal/process/pre_execution.js b/lib/internal/process/pre_execution.js index bb791159c11a70..652256f28e5ea4 100644 --- a/lib/internal/process/pre_execution.js +++ b/lib/internal/process/pre_execution.js @@ -440,7 +440,7 @@ function setupInspectorHooks() { } function setupNetworkInspection() { - if (internalBinding('config').hasInspector) { + if (internalBinding('config').hasInspector && getOptionValue('--experimental-network-inspection')) { const { enable, disable, diff --git a/test/parallel/test-bootstrap-modules.js b/test/parallel/test-bootstrap-modules.js index 875e0a4a18dbd9..71e2854f8e2d47 100644 --- a/test/parallel/test-bootstrap-modules.js +++ b/test/parallel/test-bootstrap-modules.js @@ -163,7 +163,6 @@ if (process.features.inspector) { expected.beforePreExec.add('Internal Binding inspector'); expected.beforePreExec.add('NativeModule internal/util/inspector'); expected.atRunTime.add('NativeModule internal/inspector_async_hook'); - expected.atRunTime.add('NativeModule internal/inspector_network_tracking'); } // This is loaded if the test is run with NODE_V8_COVERAGE. From 4103e26eb9d79a25ae7a53c0b67a6d5701910545 Mon Sep 17 00:00:00 2001 From: cola119 Date: Thu, 11 Jul 2024 18:43:27 +0900 Subject: [PATCH 13/17] inspector: refactor to define `NetworkInspector` --- src/inspector/network_agent.cc | 16 ++++----- src/inspector/network_agent.h | 10 +++--- src/inspector/network_inspector.cc | 53 +++++++++++++++++++++++++++++ src/inspector/network_inspector.h | 43 +++++++++++++++++++++++ src/inspector/node_inspector.gypi | 2 ++ src/inspector/node_network_agent.cc | 31 ++++++----------- src/inspector/node_network_agent.h | 10 +++--- src/inspector/node_string.cc | 5 --- src/inspector/node_string.h | 1 - src/inspector_agent.cc | 43 +++++------------------ 10 files changed, 132 insertions(+), 82 deletions(-) create mode 100644 src/inspector/network_inspector.cc create mode 100644 src/inspector/network_inspector.h diff --git a/src/inspector/network_agent.cc b/src/inspector/network_agent.cc index 3a4dc27d6a9a5d..de17ff0ecb2041 100644 --- a/src/inspector/network_agent.cc +++ b/src/inspector/network_agent.cc @@ -1,4 +1,5 @@ #include "network_agent.h" +#include "network_inspector.h" namespace node { namespace inspector { @@ -9,7 +10,8 @@ std::unique_ptr Request(const String& url, return Network::Request::create().setUrl(url).setMethod(method).build(); } -NetworkAgent::NetworkAgent(Environment* env) : enabled_(false), env_(env) { +NetworkAgent::NetworkAgent(NetworkInspector* inspector) + : inspector_(inspector) { event_notifier_map_["requestWillBeSent"] = &NetworkAgent::requestWillBeSent; event_notifier_map_["responseReceived"] = &NetworkAgent::responseReceived; event_notifier_map_["loadingFinished"] = &NetworkAgent::loadingFinished; @@ -17,7 +19,7 @@ NetworkAgent::NetworkAgent(Environment* env) : enabled_(false), env_(env) { void NetworkAgent::emitNotification( const String& event, std::unique_ptr params) { - if (!enabled_) return; + if (!inspector_->IsEnabled()) return; auto it = event_notifier_map_.find(event); if (it != event_notifier_map_.end()) { (this->*(it->second))(std::move(params)); @@ -30,18 +32,12 @@ void NetworkAgent::Wire(UberDispatcher* dispatcher) { } DispatchResponse NetworkAgent::enable() { - enabled_ = true; - if (auto agent = env_->inspector_agent()) { - agent->EnableNetworkTracking(); - } + inspector_->Enable(); return DispatchResponse::OK(); } DispatchResponse NetworkAgent::disable() { - enabled_ = false; - if (auto agent = env_->inspector_agent()) { - agent->DisableNetworkTracking(); - } + inspector_->Disable(); return DispatchResponse::OK(); } diff --git a/src/inspector/network_agent.h b/src/inspector/network_agent.h index 65a77410f68c53..e2ca447b6e9480 100644 --- a/src/inspector/network_agent.h +++ b/src/inspector/network_agent.h @@ -1,16 +1,15 @@ #ifndef SRC_INSPECTOR_NETWORK_AGENT_H_ #define SRC_INSPECTOR_NETWORK_AGENT_H_ -#include "env.h" #include "node/inspector/protocol/Network.h" -#include "v8.h" #include namespace node { -class Environment; namespace inspector { +class NetworkInspector; + namespace protocol { std::unique_ptr Request(const String& url, @@ -18,7 +17,7 @@ std::unique_ptr Request(const String& url, class NetworkAgent : public Network::Backend { public: - explicit NetworkAgent(Environment* env); + explicit NetworkAgent(NetworkInspector* inspector); void Wire(UberDispatcher* dispatcher); @@ -36,8 +35,7 @@ class NetworkAgent : public Network::Backend { void loadingFinished(std::unique_ptr params); private: - bool enabled_; - Environment* env_; + NetworkInspector* inspector_; std::shared_ptr frontend_; using EventNotifier = void (NetworkAgent::*)(std::unique_ptr); diff --git a/src/inspector/network_inspector.cc b/src/inspector/network_inspector.cc new file mode 100644 index 00000000000000..93fe5b85b05720 --- /dev/null +++ b/src/inspector/network_inspector.cc @@ -0,0 +1,53 @@ +#include "network_inspector.h" + +namespace node { +namespace inspector { + +NetworkInspector::NetworkInspector(Environment* env) + : enabled_(false), env_(env) { + network_agent_ = std::make_unique(this); + node_network_agent_ = std::make_unique(this); +} +NetworkInspector::~NetworkInspector() { + network_agent_.reset(); + node_network_agent_.reset(); +} + +void NetworkInspector::Wire(protocol::UberDispatcher* dispatcher) { + network_agent_->Wire(dispatcher); + node_network_agent_->Wire(dispatcher); +} + +bool NetworkInspector::canEmit(const std::string& domain) { + return domain == "Network" || domain == "NodeNetwork"; +} + +void NetworkInspector::emitNotification( + const std::string& domain, + const std::string& method, + std::unique_ptr params) { + if (domain == "Network") { + network_agent_->emitNotification(method, std::move(params)); + } else if (domain == "NodeNetwork") { + node_network_agent_->emitNotification(method, std::move(params)); + } else { + UNREACHABLE("Unknown domain"); + } +} + +void NetworkInspector::Enable() { + if (auto agent = env_->inspector_agent()) { + agent->EnableNetworkTracking(); + } + enabled_ = true; +} + +void NetworkInspector::Disable() { + if (auto agent = env_->inspector_agent()) { + agent->DisableNetworkTracking(); + } + enabled_ = false; +} + +} // namespace inspector +} // namespace node diff --git a/src/inspector/network_inspector.h b/src/inspector/network_inspector.h new file mode 100644 index 00000000000000..cd39bb73126dc5 --- /dev/null +++ b/src/inspector/network_inspector.h @@ -0,0 +1,43 @@ +#ifndef SRC_INSPECTOR_NETWORK_INSPECTOR_H_ +#define SRC_INSPECTOR_NETWORK_INSPECTOR_H_ + +#include "env.h" +#include "network_agent.h" +#include "node_network_agent.h" +#include "v8.h" + +#include + +namespace node { +class Environment; + +namespace inspector { + +class NetworkInspector { + public: + explicit NetworkInspector(Environment* env); + ~NetworkInspector(); + + void Wire(protocol::UberDispatcher* dispatcher); + + bool canEmit(const std::string& domain); + + void emitNotification(const std::string& domain, + const std::string& method, + std::unique_ptr params); + + void Enable(); + void Disable(); + bool IsEnabled() const { return enabled_; } + + private: + bool enabled_; + Environment* env_; + std::unique_ptr network_agent_; + std::unique_ptr node_network_agent_; +}; + +} // namespace inspector +} // namespace node + +#endif // SRC_INSPECTOR_NETWORK_INSPECTOR_H_ diff --git a/src/inspector/node_inspector.gypi b/src/inspector/node_inspector.gypi index 0fb3d81762c433..99e7176846b15d 100644 --- a/src/inspector/node_inspector.gypi +++ b/src/inspector/node_inspector.gypi @@ -23,6 +23,8 @@ 'src/inspector/tracing_agent.h', 'src/inspector/worker_agent.cc', 'src/inspector/worker_agent.h', + 'src/inspector/network_inspector.cc', + 'src/inspector/network_inspector.h', 'src/inspector/network_agent.cc', 'src/inspector/network_agent.h', 'src/inspector/node_network_agent.cc', diff --git a/src/inspector/node_network_agent.cc b/src/inspector/node_network_agent.cc index a3d25c66318cae..bf5716d9f87182 100644 --- a/src/inspector/node_network_agent.cc +++ b/src/inspector/node_network_agent.cc @@ -1,12 +1,13 @@ #include "node_network_agent.h" #include "network_agent.h" +#include "network_inspector.h" namespace node { namespace inspector { namespace protocol { -NodeNetworkAgent::NodeNetworkAgent(Environment* env) - : enabled_(false), env_(env) { +NodeNetworkAgent::NodeNetworkAgent(NetworkInspector* inspector) + : inspector_(inspector) { event_notifier_map_["requestWillBeSent"] = &NodeNetworkAgent::requestWillBeSent; event_notifier_map_["responseReceived"] = &NodeNetworkAgent::responseReceived; @@ -15,7 +16,7 @@ NodeNetworkAgent::NodeNetworkAgent(Environment* env) void NodeNetworkAgent::emitNotification( const String& event, std::unique_ptr params) { - if (!enabled_) return; + if (!inspector_->IsEnabled()) return; auto it = event_notifier_map_.find(event); if (it != event_notifier_map_.end()) { (this->*(it->second))(std::move(params)); @@ -28,18 +29,12 @@ void NodeNetworkAgent::Wire(UberDispatcher* dispatcher) { } DispatchResponse NodeNetworkAgent::enable() { - enabled_ = true; - if (auto agent = env_->inspector_agent()) { - agent->EnableNetworkTracking(); - } + inspector_->Enable(); return DispatchResponse::OK(); } DispatchResponse NodeNetworkAgent::disable() { - enabled_ = false; - if (auto agent = env_->inspector_agent()) { - agent->DisableNetworkTracking(); - } + inspector_->Disable(); return DispatchResponse::OK(); } @@ -60,9 +55,8 @@ void NodeNetworkAgent::requestWillBeSent( frontend_->requestWillBeSent( request_id, Request(url, method), timestamp, wall_time); - env_->inspector_agent()->EmitProtocolEvent( - protocol::StringUtil::ToStringView("Network.requestWillBeSent"), - protocol::StringUtil::ToStringView(params->toJSONString())); + inspector_->emitNotification( + "Network", "requestWillBeSent", std::move(params)); } void NodeNetworkAgent::responseReceived( @@ -74,9 +68,8 @@ void NodeNetworkAgent::responseReceived( frontend_->responseReceived(request_id, timestamp); - env_->inspector_agent()->EmitProtocolEvent( - protocol::StringUtil::ToStringView("Network.responseReceived"), - protocol::StringUtil::ToStringView(params->toJSONString())); + inspector_->emitNotification( + "Network", "responseReceived", std::move(params)); } void NodeNetworkAgent::loadingFinished( @@ -88,9 +81,7 @@ void NodeNetworkAgent::loadingFinished( frontend_->loadingFinished(request_id, timestamp); - env_->inspector_agent()->EmitProtocolEvent( - protocol::StringUtil::ToStringView("Network.loadingFinished"), - protocol::StringUtil::ToStringView(params->toJSONString())); + inspector_->emitNotification("Network", "loadingFinished", std::move(params)); } } // namespace protocol diff --git a/src/inspector/node_network_agent.h b/src/inspector/node_network_agent.h index 208ea7303fc8f3..6503ff8fef8b50 100644 --- a/src/inspector/node_network_agent.h +++ b/src/inspector/node_network_agent.h @@ -1,21 +1,20 @@ #ifndef SRC_INSPECTOR_NODE_NETWORK_AGENT_H_ #define SRC_INSPECTOR_NODE_NETWORK_AGENT_H_ -#include "env.h" #include "node/inspector/protocol/NodeNetwork.h" -#include "v8.h" #include namespace node { -class Environment; namespace inspector { +class NetworkInspector; + namespace protocol { class NodeNetworkAgent : public NodeNetwork::Backend { public: - explicit NodeNetworkAgent(Environment* env); + explicit NodeNetworkAgent(NetworkInspector* inspector); void emitNotification(const String& event, std::unique_ptr params); @@ -33,8 +32,7 @@ class NodeNetworkAgent : public NodeNetwork::Backend { void loadingFinished(std::unique_ptr params); private: - bool enabled_; - Environment* env_; + NetworkInspector* inspector_; std::shared_ptr frontend_; using EventNotifier = void (NodeNetworkAgent::*)(std::unique_ptr); diff --git a/src/inspector/node_string.cc b/src/inspector/node_string.cc index 1b62eb9c22ca28..c62e7ed30c4e19 100644 --- a/src/inspector/node_string.cc +++ b/src/inspector/node_string.cc @@ -81,11 +81,6 @@ String StringViewToUtf8(v8_inspector::StringView view) { return String(buffer.out(), utf8_length); } -v8_inspector::StringView ToStringView(const std::string& str) { - return v8_inspector::StringView(reinterpret_cast(str.data()), - str.size()); -} - String fromDouble(double d) { std::ostringstream stream; stream.imbue(std::locale::classic()); // Ignore current locale diff --git a/src/inspector/node_string.h b/src/inspector/node_string.h index 7c1d755bbb68b4..369041841ef96d 100644 --- a/src/inspector/node_string.h +++ b/src/inspector/node_string.h @@ -62,7 +62,6 @@ String fromDouble(double d); double toDouble(const char* buffer, size_t length, bool* ok); String StringViewToUtf8(v8_inspector::StringView view); -v8_inspector::StringView ToStringView(const std::string& str); // NOLINTNEXTLINE(runtime/references) void builderAppendQuotedString(StringBuilder& builder, const std::string_view); diff --git a/src/inspector_agent.cc b/src/inspector_agent.cc index 1960734a71bf67..bb39a0cb42a7be 100644 --- a/src/inspector_agent.cc +++ b/src/inspector_agent.cc @@ -2,8 +2,7 @@ #include "env-inl.h" #include "inspector/main_thread_interface.h" -#include "inspector/network_agent.h" -#include "inspector/node_network_agent.h" +#include "inspector/network_inspector.h" #include "inspector/node_string.h" #include "inspector/runtime_agent.h" #include "inspector/tracing_agent.h" @@ -233,10 +232,8 @@ class ChannelImpl final : public v8_inspector::V8Inspector::Channel, } runtime_agent_ = std::make_unique(); runtime_agent_->Wire(node_dispatcher_.get()); - network_agent_ = std::make_unique(env); - network_agent_->Wire(node_dispatcher_.get()); - node_network_agent_ = std::make_unique(env); - node_network_agent_->Wire(node_dispatcher_.get()); + network_inspector_ = std::make_unique(env); + network_inspector_->Wire(node_dispatcher_.get()); } ~ChannelImpl() override { @@ -248,10 +245,8 @@ class ChannelImpl final : public v8_inspector::V8Inspector::Channel, } runtime_agent_->disable(); runtime_agent_.reset(); // Dispose before the dispatchers - network_agent_->disable(); - network_agent_.reset(); // Dispose before the dispatchers - node_network_agent_->disable(); - node_network_agent_.reset(); // Dispose before the dispatchers + network_inspector_->Disable(); + network_inspector_.reset(); // Dispose before the dispatchers } void emitNotificationFromBackend(const StringView& event, @@ -262,10 +257,9 @@ class ChannelImpl final : public v8_inspector::V8Inspector::Channel, std::string raw_event = protocol::StringUtil::StringViewToUtf8(event); std::string domain_name = raw_event.substr(0, raw_event.find('.')); std::string event_name = raw_event.substr(raw_event.find('.') + 1); - if (domain_name == "Network") { - network_agent_->emitNotification(event_name, std::move(value)); - } else if (domain_name == "NodeNetwork") { - node_network_agent_->emitNotification(event_name, std::move(value)); + if (network_inspector_->canEmit(domain_name)) { + network_inspector_->emitNotification( + domain_name, event_name, std::move(value)); } else { UNREACHABLE("Unknown domain for emitNotificationFromBackend"); } @@ -289,24 +283,6 @@ class ChannelImpl final : public v8_inspector::V8Inspector::Channel, node_dispatcher_->dispatch( call_id, method, std::move(value), raw_message); } - // Since the `Network` domain is not directly accessible to inspector - // module users, this allows for them to enable/disable the `Network` - // domain via the `NodeNetwork` domain. - if (method == "NodeNetwork.enable") { - network_agent_->enable(); - } - if (method == "NodeNetwork.disable") { - network_agent_->disable(); - } - // Since DevTools Frontend sends commands in the `Network` domain only, - // we need to enable/disable the `NodeNetwork` domain via the `Network` - // domain. - if (method == "Network.enable") { - node_network_agent_->enable(); - } - if (method == "Network.disable") { - node_network_agent_->disable(); - } } void schedulePauseOnNextStatement(const std::string& reason) { @@ -380,8 +356,7 @@ class ChannelImpl final : public v8_inspector::V8Inspector::Channel, std::unique_ptr runtime_agent_; std::unique_ptr tracing_agent_; std::unique_ptr worker_agent_; - std::unique_ptr network_agent_; - std::unique_ptr node_network_agent_; + std::unique_ptr network_inspector_; std::unique_ptr delegate_; std::unique_ptr session_; std::unique_ptr node_dispatcher_; From 47f3ae93457bf4fb2776137c8d353d9d57a1e542 Mon Sep 17 00:00:00 2001 From: cola119 Date: Thu, 11 Jul 2024 19:51:33 +0900 Subject: [PATCH 14/17] Update docs --- doc/api/inspector.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/api/inspector.md b/doc/api/inspector.md index 50e16b558fe7d1..fa3ca4050d86f8 100644 --- a/doc/api/inspector.md +++ b/doc/api/inspector.md @@ -488,15 +488,15 @@ Blocks until a client (existing or connected later) has sent An exception will be thrown if there is no active inspector. -## Node.js-specific Protocol Events +## Node.js-specific protocol events -Node.js has dedicated DevTools protocols for debugging. The following methods emits a protocol +Node.js has dedicated DevTools protocols for debugging. The following methods emit a protocol event with optional `params`. These methods allow you to send custom events that conform to the DevTools protocol, facilitating integration with debugging and inspection tools. The emitted events can be captured by connected DevTools Frontend instances, such as Chrome DevTools. ```js -// `NodeNetwork.requestWillBeSent` event will be fired. +// The `NodeNetwork.requestWillBeSent` event will be fired. inspector.NodeNetwork.requestWillBeSent({ requestId: 'request-id-1', timestamp: Date.now() / 1000, From 030f025763893bac8d212b84f48624ef906e5154 Mon Sep 17 00:00:00 2001 From: cola119 Date: Fri, 12 Jul 2024 09:58:20 +0900 Subject: [PATCH 15/17] inspector: rename to `broadcastToFrontend` --- lib/inspector.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/inspector.js b/lib/inspector.js index 729ebe5ca81156..9e6448f36930b6 100644 --- a/lib/inspector.js +++ b/lib/inspector.js @@ -189,7 +189,7 @@ function inspectorWaitForDebugger() { throw new ERR_INSPECTOR_NOT_ACTIVE(); } -function emit(eventName, params) { +function broadcastToFrontend(eventName, params) { validateString(eventName, 'eventName'); if (params) { validateObject(params, 'params'); @@ -198,9 +198,9 @@ function emit(eventName, params) { } const NodeNetwork = { - requestWillBeSent: (params) => emit('NodeNetwork.requestWillBeSent', params), - responseReceived: (params) => emit('NodeNetwork.responseReceived', params), - loadingFinished: (params) => emit('NodeNetwork.loadingFinished', params), + requestWillBeSent: (params) => broadcastToFrontend('NodeNetwork.requestWillBeSent', params), + responseReceived: (params) => broadcastToFrontend('NodeNetwork.responseReceived', params), + loadingFinished: (params) => broadcastToFrontend('NodeNetwork.loadingFinished', params), }; module.exports = { From 1796860a1b6424a9452788fcc9d6c477d716b946 Mon Sep 17 00:00:00 2001 From: cola119 Date: Sat, 13 Jul 2024 21:12:59 +0900 Subject: [PATCH 16/17] Update docs --- doc/api/inspector.md | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/doc/api/inspector.md b/doc/api/inspector.md index fa3ca4050d86f8..05c3b08917b0d9 100644 --- a/doc/api/inspector.md +++ b/doc/api/inspector.md @@ -490,10 +490,11 @@ An exception will be thrown if there is no active inspector. ## Node.js-specific protocol events -Node.js has dedicated DevTools protocols for debugging. The following methods emit a protocol -event with optional `params`. These methods allow you to send custom events that conform to the -DevTools protocol, facilitating integration with debugging and inspection tools. The emitted -events can be captured by connected DevTools Frontend instances, such as Chrome DevTools. +Node.js extends the Chrome DevTools protocol with Node.js-specific protocol events. DevTools +frontends connected to a running Node.js instance can capture these events and display them +accordingly to facilitate debugging. +The following methods broadcast a Node.js-specific protocol event to all connected frontends. +The `params` passed to the methods can be optional, depending on the protocol. ```js // The `NodeNetwork.requestWillBeSent` event will be fired. @@ -521,8 +522,8 @@ added: This feature is only available with the `--experimental-network-inspection` flag enabled. -Emits the `NodeNetwork.requestWillBeSent` event. This event indicates that the application is -about to send an HTTP request. +Broadcasts the `NodeNetwork.requestWillBeSent` event to connected frontends. This event indicates that +the application is about to send an HTTP request. ### `inspector.NodeNetwork.responseReceived([params])` @@ -537,8 +538,8 @@ added: This feature is only available with the `--experimental-network-inspection` flag enabled. -Emits the `NodeNetwork.responseReceived` event. This event indicates that HTTP response is -available. +Broadcasts the `NodeNetwork.responseReceived` event to connected frontends. This event indicates that +HTTP response is available. ### `inspector.NodeNetwork.loadingFinished([params])` @@ -553,8 +554,8 @@ added: This feature is only available with the `--experimental-network-inspection` flag enabled. -Emits the `NodeNetwork.loadingFinished` event. This event indicates that HTTP request has -finished loading. +Broadcasts the `NodeNetwork.loadingFinished` event to connected frontends. This event indicates that +HTTP request has finished loading. ## Support of breakpoints From 1c6fc6ce3b1037dd421fbe1194531a81b2e5b013 Mon Sep 17 00:00:00 2001 From: cola119 Date: Thu, 18 Jul 2024 22:03:10 +0900 Subject: [PATCH 17/17] inspector: remove redundant `NodeNetwork` domain, consolidate with `Network` domain --- doc/api/inspector.md | 26 +++--- lib/inspector.js | 10 +-- lib/internal/inspector_network_tracking.js | 12 +-- src/inspector/network_inspector.cc | 7 +- src/inspector/network_inspector.h | 5 -- src/inspector/node_inspector.gypi | 4 - src/inspector/node_network_agent.cc | 89 ------------------- src/inspector/node_network_agent.h | 46 ---------- src/inspector/node_protocol.pdl | 36 -------- .../test-inspector-emit-protocol-event.js | 10 +-- .../parallel/test-inspector-network-domain.js | 62 +------------ 11 files changed, 32 insertions(+), 275 deletions(-) delete mode 100644 src/inspector/node_network_agent.cc delete mode 100644 src/inspector/node_network_agent.h diff --git a/doc/api/inspector.md b/doc/api/inspector.md index 05c3b08917b0d9..c1c391eb14889a 100644 --- a/doc/api/inspector.md +++ b/doc/api/inspector.md @@ -488,17 +488,17 @@ Blocks until a client (existing or connected later) has sent An exception will be thrown if there is no active inspector. -## Node.js-specific protocol events +## Integration with DevTools -Node.js extends the Chrome DevTools protocol with Node.js-specific protocol events. DevTools -frontends connected to a running Node.js instance can capture these events and display them -accordingly to facilitate debugging. -The following methods broadcast a Node.js-specific protocol event to all connected frontends. +The `node:inspector` module provides an API for integrating with devtools that support Chrome DevTools Protocol. +DevTools frontends connected to a running Node.js instance can capture protocol events emitted from the instance +and display them accordingly to facilitate debugging. +The following methods broadcast a protocol event to all connected frontends. The `params` passed to the methods can be optional, depending on the protocol. ```js -// The `NodeNetwork.requestWillBeSent` event will be fired. -inspector.NodeNetwork.requestWillBeSent({ +// The `Network.requestWillBeSent` event will be fired. +inspector.Network.requestWillBeSent({ requestId: 'request-id-1', timestamp: Date.now() / 1000, wallTime: Date.now(), @@ -509,7 +509,7 @@ inspector.NodeNetwork.requestWillBeSent({ }); ``` -### `inspector.NodeNetwork.requestWillBeSent([params])` +### `inspector.Network.requestWillBeSent([params])`