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
|
||||
* @param {{ request: import('http').ClientRequest }} event
|
||||
*/
|
||||
function onClientRequestStart({ request }) {
|
||||
function onClientRequestCreated({ request }) {
|
||||
request[kInspectorRequestId] = getNextRequestId();
|
||||
|
||||
const { 0: host, 1: headers } = convertHeaderObject(request.getHeaders());
|
||||
@ -115,13 +115,13 @@ function onClientResponseFinish({ request, response }) {
|
||||
}
|
||||
|
||||
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.response.finish', onClientResponseFinish);
|
||||
}
|
||||
|
||||
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.response.finish', onClientResponseFinish);
|
||||
}
|
||||
|
@ -129,10 +129,10 @@ function enable() {
|
||||
}
|
||||
|
||||
function disable() {
|
||||
dc.subscribe('undici:request:create', onClientRequestStart);
|
||||
dc.subscribe('undici:request:error', onClientRequestError);
|
||||
dc.subscribe('undici:request:headers', onClientResponseHeaders);
|
||||
dc.subscribe('undici:request:trailers', onClientResponseFinish);
|
||||
dc.unsubscribe('undici:request:create', onClientRequestStart);
|
||||
dc.unsubscribe('undici:request:error', onClientRequestError);
|
||||
dc.unsubscribe('undici:request:headers', onClientResponseHeaders);
|
||||
dc.unsubscribe('undici:request:trailers', onClientResponseFinish);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
@ -29,8 +29,9 @@ std::unique_ptr<Network::Response> createResponse(
|
||||
.build();
|
||||
}
|
||||
|
||||
NetworkAgent::NetworkAgent(NetworkInspector* inspector)
|
||||
: inspector_(inspector) {
|
||||
NetworkAgent::NetworkAgent(NetworkInspector* inspector,
|
||||
v8_inspector::V8Inspector* v8_inspector)
|
||||
: inspector_(inspector), v8_inspector_(v8_inspector) {
|
||||
event_notifier_map_["requestWillBeSent"] = &NetworkAgent::requestWillBeSent;
|
||||
event_notifier_map_["responseReceived"] = &NetworkAgent::responseReceived;
|
||||
event_notifier_map_["loadingFailed"] = &NetworkAgent::loadingFailed;
|
||||
@ -75,6 +76,13 @@ void NetworkAgent::requestWillBeSent(
|
||||
String 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;
|
||||
errors.Push();
|
||||
errors.SetName("headers");
|
||||
@ -86,6 +94,7 @@ void NetworkAgent::requestWillBeSent(
|
||||
|
||||
frontend_->requestWillBeSent(request_id,
|
||||
createRequest(url, method, std::move(headers)),
|
||||
std::move(initiator),
|
||||
timestamp,
|
||||
wall_time);
|
||||
}
|
||||
|
@ -14,7 +14,8 @@ namespace protocol {
|
||||
|
||||
class NetworkAgent : public Network::Backend {
|
||||
public:
|
||||
explicit NetworkAgent(NetworkInspector* inspector);
|
||||
explicit NetworkAgent(NetworkInspector* inspector,
|
||||
v8_inspector::V8Inspector* v8_inspector);
|
||||
|
||||
void Wire(UberDispatcher* dispatcher);
|
||||
|
||||
@ -35,6 +36,7 @@ class NetworkAgent : public Network::Backend {
|
||||
|
||||
private:
|
||||
NetworkInspector* inspector_;
|
||||
v8_inspector::V8Inspector* v8_inspector_;
|
||||
std::shared_ptr<Network::Frontend> frontend_;
|
||||
using EventNotifier =
|
||||
void (NetworkAgent::*)(std::unique_ptr<protocol::DictionaryValue>);
|
||||
|
@ -3,9 +3,10 @@
|
||||
namespace node {
|
||||
namespace inspector {
|
||||
|
||||
NetworkInspector::NetworkInspector(Environment* env)
|
||||
NetworkInspector::NetworkInspector(Environment* env,
|
||||
v8_inspector::V8Inspector* v8_inspector)
|
||||
: enabled_(false), env_(env) {
|
||||
network_agent_ = std::make_unique<protocol::NetworkAgent>(this);
|
||||
network_agent_ = std::make_unique<protocol::NetworkAgent>(this, v8_inspector);
|
||||
}
|
||||
NetworkInspector::~NetworkInspector() {
|
||||
network_agent_.reset();
|
||||
|
@ -11,7 +11,8 @@ namespace inspector {
|
||||
|
||||
class NetworkInspector {
|
||||
public:
|
||||
explicit NetworkInspector(Environment* env);
|
||||
explicit NetworkInspector(Environment* env,
|
||||
v8_inspector::V8Inspector* v8_inspector);
|
||||
~NetworkInspector();
|
||||
|
||||
void Wire(protocol::UberDispatcher* dispatcher);
|
||||
|
@ -97,6 +97,7 @@
|
||||
'action_name': 'node_protocol_generated_sources',
|
||||
'inputs': [
|
||||
'node_protocol_config.json',
|
||||
'node_protocol.pdl',
|
||||
'<(SHARED_INTERMEDIATE_DIR)/src/node_protocol.json',
|
||||
'<@(node_protocol_files)',
|
||||
'<(protocol_tool_path)/code_generator.py',
|
||||
|
@ -101,6 +101,8 @@ experimental domain NodeWorker
|
||||
# Partial support for Network domain of ChromeDevTools Protocol.
|
||||
# https://chromedevtools.github.io/devtools-protocol/tot/Network
|
||||
experimental domain Network
|
||||
depends on Runtime
|
||||
|
||||
# Resource type as it was perceived by the rendering engine.
|
||||
type ResourceType extends string
|
||||
enum
|
||||
@ -132,6 +134,31 @@ experimental domain Network
|
||||
# Monotonically increasing time in seconds since an arbitrary point in the past.
|
||||
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.
|
||||
type Request extends object
|
||||
properties
|
||||
@ -163,6 +190,8 @@ experimental domain Network
|
||||
RequestId requestId
|
||||
# Request data.
|
||||
Request request
|
||||
# Request initiator.
|
||||
Initiator initiator
|
||||
# Timestamp.
|
||||
MonotonicTime timestamp
|
||||
# Timestamp.
|
||||
|
@ -5,6 +5,17 @@
|
||||
"output": "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": {
|
||||
"package": "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_->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());
|
||||
}
|
||||
|
||||
|
@ -65,6 +65,13 @@ const terminate = () => {
|
||||
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) => {
|
||||
session.on('Network.requestWillBeSent', common.mustCall(({ params }) => {
|
||||
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(typeof params.timestamp, '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 }) => {
|
||||
assert.ok(params.requestId.startsWith('node-network-event-'));
|
||||
|
@ -64,6 +64,13 @@ const terminate = () => {
|
||||
inspector.close();
|
||||
};
|
||||
|
||||
function findFrameInInitiator(scriptName, initiator) {
|
||||
const frame = initiator.stack.callFrames.find((it) => {
|
||||
return it.url === scriptName;
|
||||
});
|
||||
return frame;
|
||||
}
|
||||
|
||||
function verifyRequestWillBeSent({ method, params }, expect) {
|
||||
assert.strictEqual(method, 'Network.requestWillBeSent');
|
||||
|
||||
@ -78,6 +85,10 @@ function verifyRequestWillBeSent({ method, params }, expect) {
|
||||
assert.strictEqual(typeof params.timestamp, '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;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user