inspector: add Network.Initiator in inspector protocol
Add initiator stack trace in inspector network events, reflecting the location where the script created the request. The `http.client.request.created` event is closer to where user code creates the http request, and correctly reflects which script initiated the request. PR-URL: https://github.com/nodejs/node/pull/56805 Refs: https://github.com/nodejs/node/issues/53946 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Kohei Ueno <kohei.ueno119@gmail.com> Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
This commit is contained in:
parent
297a4dd339
commit
b18153598b
@ -44,11 +44,11 @@ const convertHeaderObject = (headers = {}) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When a client request starts, emit Network.requestWillBeSent event.
|
* When a client request is created, emit Network.requestWillBeSent event.
|
||||||
* https://chromedevtools.github.io/devtools-protocol/1-3/Network/#event-requestWillBeSent
|
* https://chromedevtools.github.io/devtools-protocol/1-3/Network/#event-requestWillBeSent
|
||||||
* @param {{ request: import('http').ClientRequest }} event
|
* @param {{ request: import('http').ClientRequest }} event
|
||||||
*/
|
*/
|
||||||
function onClientRequestStart({ request }) {
|
function onClientRequestCreated({ request }) {
|
||||||
request[kInspectorRequestId] = getNextRequestId();
|
request[kInspectorRequestId] = getNextRequestId();
|
||||||
|
|
||||||
const { 0: host, 1: headers } = convertHeaderObject(request.getHeaders());
|
const { 0: host, 1: headers } = convertHeaderObject(request.getHeaders());
|
||||||
@ -115,13 +115,13 @@ function onClientResponseFinish({ request, response }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function enable() {
|
function enable() {
|
||||||
dc.subscribe('http.client.request.start', onClientRequestStart);
|
dc.subscribe('http.client.request.created', onClientRequestCreated);
|
||||||
dc.subscribe('http.client.request.error', onClientRequestError);
|
dc.subscribe('http.client.request.error', onClientRequestError);
|
||||||
dc.subscribe('http.client.response.finish', onClientResponseFinish);
|
dc.subscribe('http.client.response.finish', onClientResponseFinish);
|
||||||
}
|
}
|
||||||
|
|
||||||
function disable() {
|
function disable() {
|
||||||
dc.unsubscribe('http.client.request.start', onClientRequestStart);
|
dc.unsubscribe('http.client.request.created', onClientRequestCreated);
|
||||||
dc.unsubscribe('http.client.request.error', onClientRequestError);
|
dc.unsubscribe('http.client.request.error', onClientRequestError);
|
||||||
dc.unsubscribe('http.client.response.finish', onClientResponseFinish);
|
dc.unsubscribe('http.client.response.finish', onClientResponseFinish);
|
||||||
}
|
}
|
||||||
|
@ -129,10 +129,10 @@ function enable() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function disable() {
|
function disable() {
|
||||||
dc.subscribe('undici:request:create', onClientRequestStart);
|
dc.unsubscribe('undici:request:create', onClientRequestStart);
|
||||||
dc.subscribe('undici:request:error', onClientRequestError);
|
dc.unsubscribe('undici:request:error', onClientRequestError);
|
||||||
dc.subscribe('undici:request:headers', onClientResponseHeaders);
|
dc.unsubscribe('undici:request:headers', onClientResponseHeaders);
|
||||||
dc.subscribe('undici:request:trailers', onClientResponseFinish);
|
dc.unsubscribe('undici:request:trailers', onClientResponseFinish);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
@ -29,8 +29,9 @@ std::unique_ptr<Network::Response> createResponse(
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkAgent::NetworkAgent(NetworkInspector* inspector)
|
NetworkAgent::NetworkAgent(NetworkInspector* inspector,
|
||||||
: inspector_(inspector) {
|
v8_inspector::V8Inspector* v8_inspector)
|
||||||
|
: inspector_(inspector), v8_inspector_(v8_inspector) {
|
||||||
event_notifier_map_["requestWillBeSent"] = &NetworkAgent::requestWillBeSent;
|
event_notifier_map_["requestWillBeSent"] = &NetworkAgent::requestWillBeSent;
|
||||||
event_notifier_map_["responseReceived"] = &NetworkAgent::responseReceived;
|
event_notifier_map_["responseReceived"] = &NetworkAgent::responseReceived;
|
||||||
event_notifier_map_["loadingFailed"] = &NetworkAgent::loadingFailed;
|
event_notifier_map_["loadingFailed"] = &NetworkAgent::loadingFailed;
|
||||||
@ -75,6 +76,13 @@ void NetworkAgent::requestWillBeSent(
|
|||||||
String method;
|
String method;
|
||||||
request->getString("method", &method);
|
request->getString("method", &method);
|
||||||
|
|
||||||
|
std::unique_ptr<Network::Initiator> initiator =
|
||||||
|
Network::Initiator::create()
|
||||||
|
.setType(Network::Initiator::TypeEnum::Script)
|
||||||
|
.setStack(
|
||||||
|
v8_inspector_->captureStackTrace(true)->buildInspectorObject(0))
|
||||||
|
.build();
|
||||||
|
|
||||||
ErrorSupport errors;
|
ErrorSupport errors;
|
||||||
errors.Push();
|
errors.Push();
|
||||||
errors.SetName("headers");
|
errors.SetName("headers");
|
||||||
@ -86,6 +94,7 @@ void NetworkAgent::requestWillBeSent(
|
|||||||
|
|
||||||
frontend_->requestWillBeSent(request_id,
|
frontend_->requestWillBeSent(request_id,
|
||||||
createRequest(url, method, std::move(headers)),
|
createRequest(url, method, std::move(headers)),
|
||||||
|
std::move(initiator),
|
||||||
timestamp,
|
timestamp,
|
||||||
wall_time);
|
wall_time);
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,8 @@ namespace protocol {
|
|||||||
|
|
||||||
class NetworkAgent : public Network::Backend {
|
class NetworkAgent : public Network::Backend {
|
||||||
public:
|
public:
|
||||||
explicit NetworkAgent(NetworkInspector* inspector);
|
explicit NetworkAgent(NetworkInspector* inspector,
|
||||||
|
v8_inspector::V8Inspector* v8_inspector);
|
||||||
|
|
||||||
void Wire(UberDispatcher* dispatcher);
|
void Wire(UberDispatcher* dispatcher);
|
||||||
|
|
||||||
@ -35,6 +36,7 @@ class NetworkAgent : public Network::Backend {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
NetworkInspector* inspector_;
|
NetworkInspector* inspector_;
|
||||||
|
v8_inspector::V8Inspector* v8_inspector_;
|
||||||
std::shared_ptr<Network::Frontend> frontend_;
|
std::shared_ptr<Network::Frontend> frontend_;
|
||||||
using EventNotifier =
|
using EventNotifier =
|
||||||
void (NetworkAgent::*)(std::unique_ptr<protocol::DictionaryValue>);
|
void (NetworkAgent::*)(std::unique_ptr<protocol::DictionaryValue>);
|
||||||
|
@ -3,9 +3,10 @@
|
|||||||
namespace node {
|
namespace node {
|
||||||
namespace inspector {
|
namespace inspector {
|
||||||
|
|
||||||
NetworkInspector::NetworkInspector(Environment* env)
|
NetworkInspector::NetworkInspector(Environment* env,
|
||||||
|
v8_inspector::V8Inspector* v8_inspector)
|
||||||
: enabled_(false), env_(env) {
|
: enabled_(false), env_(env) {
|
||||||
network_agent_ = std::make_unique<protocol::NetworkAgent>(this);
|
network_agent_ = std::make_unique<protocol::NetworkAgent>(this, v8_inspector);
|
||||||
}
|
}
|
||||||
NetworkInspector::~NetworkInspector() {
|
NetworkInspector::~NetworkInspector() {
|
||||||
network_agent_.reset();
|
network_agent_.reset();
|
||||||
|
@ -11,7 +11,8 @@ namespace inspector {
|
|||||||
|
|
||||||
class NetworkInspector {
|
class NetworkInspector {
|
||||||
public:
|
public:
|
||||||
explicit NetworkInspector(Environment* env);
|
explicit NetworkInspector(Environment* env,
|
||||||
|
v8_inspector::V8Inspector* v8_inspector);
|
||||||
~NetworkInspector();
|
~NetworkInspector();
|
||||||
|
|
||||||
void Wire(protocol::UberDispatcher* dispatcher);
|
void Wire(protocol::UberDispatcher* dispatcher);
|
||||||
|
@ -97,6 +97,7 @@
|
|||||||
'action_name': 'node_protocol_generated_sources',
|
'action_name': 'node_protocol_generated_sources',
|
||||||
'inputs': [
|
'inputs': [
|
||||||
'node_protocol_config.json',
|
'node_protocol_config.json',
|
||||||
|
'node_protocol.pdl',
|
||||||
'<(SHARED_INTERMEDIATE_DIR)/src/node_protocol.json',
|
'<(SHARED_INTERMEDIATE_DIR)/src/node_protocol.json',
|
||||||
'<@(node_protocol_files)',
|
'<@(node_protocol_files)',
|
||||||
'<(protocol_tool_path)/code_generator.py',
|
'<(protocol_tool_path)/code_generator.py',
|
||||||
|
@ -101,6 +101,8 @@ experimental domain NodeWorker
|
|||||||
# Partial support for Network domain of ChromeDevTools Protocol.
|
# Partial support for Network domain of ChromeDevTools Protocol.
|
||||||
# https://chromedevtools.github.io/devtools-protocol/tot/Network
|
# https://chromedevtools.github.io/devtools-protocol/tot/Network
|
||||||
experimental domain Network
|
experimental domain Network
|
||||||
|
depends on Runtime
|
||||||
|
|
||||||
# Resource type as it was perceived by the rendering engine.
|
# Resource type as it was perceived by the rendering engine.
|
||||||
type ResourceType extends string
|
type ResourceType extends string
|
||||||
enum
|
enum
|
||||||
@ -132,6 +134,31 @@ experimental domain Network
|
|||||||
# Monotonically increasing time in seconds since an arbitrary point in the past.
|
# Monotonically increasing time in seconds since an arbitrary point in the past.
|
||||||
type MonotonicTime extends number
|
type MonotonicTime extends number
|
||||||
|
|
||||||
|
# Information about the request initiator.
|
||||||
|
type Initiator extends object
|
||||||
|
properties
|
||||||
|
# Type of this initiator.
|
||||||
|
enum type
|
||||||
|
parser
|
||||||
|
script
|
||||||
|
preload
|
||||||
|
SignedExchange
|
||||||
|
preflight
|
||||||
|
other
|
||||||
|
# Initiator JavaScript stack trace, set for Script only.
|
||||||
|
# Requires the Debugger domain to be enabled.
|
||||||
|
optional Runtime.StackTrace stack
|
||||||
|
# Initiator URL, set for Parser type or for Script type (when script is importing module) or for SignedExchange type.
|
||||||
|
optional string url
|
||||||
|
# Initiator line number, set for Parser type or for Script type (when script is importing
|
||||||
|
# module) (0-based).
|
||||||
|
optional number lineNumber
|
||||||
|
# Initiator column number, set for Parser type or for Script type (when script is importing
|
||||||
|
# module) (0-based).
|
||||||
|
optional number columnNumber
|
||||||
|
# Set if another request triggered this request (e.g. preflight).
|
||||||
|
optional RequestId requestId
|
||||||
|
|
||||||
# HTTP request data.
|
# HTTP request data.
|
||||||
type Request extends object
|
type Request extends object
|
||||||
properties
|
properties
|
||||||
@ -163,6 +190,8 @@ experimental domain Network
|
|||||||
RequestId requestId
|
RequestId requestId
|
||||||
# Request data.
|
# Request data.
|
||||||
Request request
|
Request request
|
||||||
|
# Request initiator.
|
||||||
|
Initiator initiator
|
||||||
# Timestamp.
|
# Timestamp.
|
||||||
MonotonicTime timestamp
|
MonotonicTime timestamp
|
||||||
# Timestamp.
|
# Timestamp.
|
||||||
|
@ -5,6 +5,17 @@
|
|||||||
"output": "node/inspector/protocol",
|
"output": "node/inspector/protocol",
|
||||||
"namespace": ["node", "inspector", "protocol"]
|
"namespace": ["node", "inspector", "protocol"]
|
||||||
},
|
},
|
||||||
|
"imported": {
|
||||||
|
"path": "../../deps/v8/include/js_protocol.pdl",
|
||||||
|
"header": "<v8-inspector-protocol.h>",
|
||||||
|
"namespace": ["v8_inspector", "protocol"],
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"domain": "Runtime",
|
||||||
|
"imported": ["StackTrace"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"exported": {
|
"exported": {
|
||||||
"package": "include/inspector",
|
"package": "include/inspector",
|
||||||
"output": "../../include/inspector",
|
"output": "../../include/inspector",
|
||||||
|
@ -241,7 +241,8 @@ class ChannelImpl final : public v8_inspector::V8Inspector::Channel,
|
|||||||
}
|
}
|
||||||
runtime_agent_ = std::make_unique<protocol::RuntimeAgent>();
|
runtime_agent_ = std::make_unique<protocol::RuntimeAgent>();
|
||||||
runtime_agent_->Wire(node_dispatcher_.get());
|
runtime_agent_->Wire(node_dispatcher_.get());
|
||||||
network_inspector_ = std::make_unique<NetworkInspector>(env);
|
network_inspector_ =
|
||||||
|
std::make_unique<NetworkInspector>(env, inspector.get());
|
||||||
network_inspector_->Wire(node_dispatcher_.get());
|
network_inspector_->Wire(node_dispatcher_.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,6 +65,13 @@ const terminate = () => {
|
|||||||
inspector.close();
|
inspector.close();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function findFrameInInitiator(scriptName, initiator) {
|
||||||
|
const frame = initiator.stack.callFrames.find((it) => {
|
||||||
|
return it.url === scriptName;
|
||||||
|
});
|
||||||
|
return frame;
|
||||||
|
}
|
||||||
|
|
||||||
const testHttpGet = () => new Promise((resolve, reject) => {
|
const testHttpGet = () => new Promise((resolve, reject) => {
|
||||||
session.on('Network.requestWillBeSent', common.mustCall(({ params }) => {
|
session.on('Network.requestWillBeSent', common.mustCall(({ params }) => {
|
||||||
assert.ok(params.requestId.startsWith('node-network-event-'));
|
assert.ok(params.requestId.startsWith('node-network-event-'));
|
||||||
@ -77,6 +84,10 @@ const testHttpGet = () => new Promise((resolve, reject) => {
|
|||||||
assert.strictEqual(params.request.headers['x-header1'], 'value1, value2');
|
assert.strictEqual(params.request.headers['x-header1'], 'value1, value2');
|
||||||
assert.strictEqual(typeof params.timestamp, 'number');
|
assert.strictEqual(typeof params.timestamp, 'number');
|
||||||
assert.strictEqual(typeof params.wallTime, 'number');
|
assert.strictEqual(typeof params.wallTime, 'number');
|
||||||
|
|
||||||
|
assert.strictEqual(typeof params.initiator, 'object');
|
||||||
|
assert.strictEqual(params.initiator.type, 'script');
|
||||||
|
assert.ok(findFrameInInitiator(__filename, params.initiator));
|
||||||
}));
|
}));
|
||||||
session.on('Network.responseReceived', common.mustCall(({ params }) => {
|
session.on('Network.responseReceived', common.mustCall(({ params }) => {
|
||||||
assert.ok(params.requestId.startsWith('node-network-event-'));
|
assert.ok(params.requestId.startsWith('node-network-event-'));
|
||||||
|
@ -64,6 +64,13 @@ const terminate = () => {
|
|||||||
inspector.close();
|
inspector.close();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function findFrameInInitiator(scriptName, initiator) {
|
||||||
|
const frame = initiator.stack.callFrames.find((it) => {
|
||||||
|
return it.url === scriptName;
|
||||||
|
});
|
||||||
|
return frame;
|
||||||
|
}
|
||||||
|
|
||||||
function verifyRequestWillBeSent({ method, params }, expect) {
|
function verifyRequestWillBeSent({ method, params }, expect) {
|
||||||
assert.strictEqual(method, 'Network.requestWillBeSent');
|
assert.strictEqual(method, 'Network.requestWillBeSent');
|
||||||
|
|
||||||
@ -78,6 +85,10 @@ function verifyRequestWillBeSent({ method, params }, expect) {
|
|||||||
assert.strictEqual(typeof params.timestamp, 'number');
|
assert.strictEqual(typeof params.timestamp, 'number');
|
||||||
assert.strictEqual(typeof params.wallTime, 'number');
|
assert.strictEqual(typeof params.wallTime, 'number');
|
||||||
|
|
||||||
|
assert.strictEqual(typeof params.initiator, 'object');
|
||||||
|
assert.strictEqual(params.initiator.type, 'script');
|
||||||
|
assert.ok(findFrameInInitiator(__filename, params.initiator));
|
||||||
|
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user