Skip to content

Commit

Permalink
inspector: add Network.Initiator in inspector protocol
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
legendecas committed Feb 5, 2025
1 parent d0bf9dd commit 314077c
Show file tree
Hide file tree
Showing 12 changed files with 92 additions and 15 deletions.
8 changes: 4 additions & 4 deletions lib/internal/inspector/network_http.js
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down Expand Up @@ -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);
}
Expand Down
8 changes: 4 additions & 4 deletions lib/internal/inspector/network_undici.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down
13 changes: 11 additions & 2 deletions src/inspector/network_agent.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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");
Expand All @@ -86,6 +94,7 @@ void NetworkAgent::requestWillBeSent(

frontend_->requestWillBeSent(request_id,
createRequest(url, method, std::move(headers)),
std::move(initiator),
timestamp,
wall_time);
}
Expand Down
4 changes: 3 additions & 1 deletion src/inspector/network_agent.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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>);
Expand Down
5 changes: 3 additions & 2 deletions src/inspector/network_inspector.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
3 changes: 2 additions & 1 deletion src/inspector/network_inspector.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions src/inspector/node_inspector.gypi
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
29 changes: 29 additions & 0 deletions src/inspector/node_protocol.pdl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -163,6 +190,8 @@ experimental domain Network
RequestId requestId
# Request data.
Request request
# Request initiator.
Initiator initiator
# Timestamp.
MonotonicTime timestamp
# Timestamp.
Expand Down
11 changes: 11 additions & 0 deletions src/inspector/node_protocol_config.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
3 changes: 2 additions & 1 deletion src/inspector_agent.cc
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,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());
}

Expand Down
11 changes: 11 additions & 0 deletions test/parallel/test-inspector-network-fetch.js
Original file line number Diff line number Diff line change
Expand Up @@ -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-'));
Expand All @@ -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-'));
Expand Down
11 changes: 11 additions & 0 deletions test/parallel/test-inspector-network-http.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');

Expand All @@ -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;
}

Expand Down

0 comments on commit 314077c

Please sign in to comment.