Skip to content

Commit b7db89f

Browse files
authored
inspector: initial support for Network.loadNetworkResource
Fixes: #57873 PR-URL: #58077 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Chengzhong Wu <[email protected]>
1 parent 09b4c57 commit b7db89f

25 files changed

+613
-26
lines changed

doc/api/cli.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1069,6 +1069,17 @@ passing a second `parentURL` argument for contextual resolution.
10691069

10701070
Previously gated the entire `import.meta.resolve` feature.
10711071

1072+
### `--experimental-inspector-network-resource`
1073+
1074+
<!-- YAML
1075+
added:
1076+
- REPLACEME
1077+
-->
1078+
1079+
> Stability: 1.1 - Active Development
1080+
1081+
Enable experimental support for inspector network resources.
1082+
10721083
### `--experimental-loader=module`
10731084

10741085
<!-- YAML

doc/api/inspector.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -600,6 +600,43 @@ This feature is only available with the `--experimental-network-inspection` flag
600600
Broadcasts the `Network.loadingFailed` event to connected frontends. This event indicates that
601601
HTTP request has failed to load.
602602

603+
### `inspector.NetworkResources.put`
604+
605+
<!-- YAML
606+
added:
607+
- REPLACEME
608+
-->
609+
610+
> Stability: 1.1 - Active Development
611+
612+
This feature is only available with the `--experimental-inspector-network-resource` flag enabled.
613+
614+
The inspector.NetworkResources.put method is used to provide a response for a loadNetworkResource
615+
request issued via the Chrome DevTools Protocol (CDP).
616+
This is typically triggered when a source map is specified by URL, and a DevTools frontend—such as
617+
Chrome—requests the resource to retrieve the source map.
618+
619+
This method allows developers to predefine the resource content to be served in response to such CDP requests.
620+
621+
```js
622+
const inspector = require('node:inspector');
623+
// By preemptively calling put to register the resource, a source map can be resolved when
624+
// a loadNetworkResource request is made from the frontend.
625+
async function setNetworkResources() {
626+
const mapUrl = 'http://localhost:3000/dist/app.js.map';
627+
const tsUrl = 'http://localhost:3000/src/app.ts';
628+
const distAppJsMap = await fetch(mapUrl).then((res) => res.text());
629+
const srcAppTs = await fetch(tsUrl).then((res) => res.text());
630+
inspector.NetworkResources.put(mapUrl, distAppJsMap);
631+
inspector.NetworkResources.put(tsUrl, srcAppTs);
632+
};
633+
setNetworkResources().then(() => {
634+
require('./dist/app');
635+
});
636+
```
637+
638+
For more details, see the official CDP documentation: [Network.loadNetworkResource](https://chromedevtools.github.io/devtools-protocol/tot/Network/#method-loadNetworkResource)
639+
603640
## Support of breakpoints
604641

605642
The Chrome DevTools Protocol [`Debugger` domain][] allows an

doc/node.1

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,9 @@ Enable experimental WebAssembly module support.
229229
.It Fl -experimental-quic
230230
Enable the experimental QUIC support.
231231
.
232+
.It Fl -experimental-inspector-network-resource
233+
Enable experimental support for inspector network resources.
234+
.
232235
.It Fl -force-context-aware
233236
Disable loading native addons that are not context-aware.
234237
.

lib/inspector.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ const {
3636
} = require('internal/validators');
3737
const { isMainThread } = require('worker_threads');
3838
const { _debugEnd } = internalBinding('process_methods');
39+
const {
40+
put,
41+
} = require('internal/inspector/network_resources');
3942

4043
const {
4144
Connection,
@@ -218,6 +221,10 @@ const Network = {
218221
dataReceived: (params) => broadcastToFrontend('Network.dataReceived', params),
219222
};
220223

224+
const NetworkResources = {
225+
put,
226+
};
227+
221228
module.exports = {
222229
open: inspectorOpen,
223230
close: _debugEnd,
@@ -226,4 +233,5 @@ module.exports = {
226233
console,
227234
Session,
228235
Network,
236+
NetworkResources,
229237
};
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
'use strict';
2+
3+
const { getOptionValue } = require('internal/options');
4+
const { validateString } = require('internal/validators');
5+
const { putNetworkResource } = internalBinding('inspector');
6+
7+
/**
8+
* Registers a resource for the inspector using the internal 'putNetworkResource' binding.
9+
* @param {string} url - The URL of the resource.
10+
* @param {string} data - The content of the resource to provide.
11+
*/
12+
function put(url, data) {
13+
if (!getOptionValue('--experimental-inspector-network-resource')) {
14+
process.emitWarning(
15+
'The --experimental-inspector-network-resource option is not enabled. ' +
16+
'Please enable it to use the putNetworkResource function');
17+
return;
18+
}
19+
validateString(url, 'url');
20+
validateString(data, 'data');
21+
22+
putNetworkResource(url, data);
23+
}
24+
25+
module.exports = {
26+
put,
27+
};

src/inspector/io_agent.cc

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#include "io_agent.h"
2+
#include <algorithm>
3+
#include <iostream>
4+
#include <string>
5+
#include <string_view>
6+
#include "crdtp/dispatch.h"
7+
#include "inspector/network_resource_manager.h"
8+
9+
namespace node::inspector::protocol {
10+
11+
void IoAgent::Wire(UberDispatcher* dispatcher) {
12+
frontend_ = std::make_shared<IO::Frontend>(dispatcher->channel());
13+
IO::Dispatcher::wire(dispatcher, this);
14+
}
15+
16+
DispatchResponse IoAgent::read(const String& in_handle,
17+
std::optional<int> in_offset,
18+
std::optional<int> in_size,
19+
String* out_data,
20+
bool* out_eof) {
21+
std::string url = in_handle;
22+
std::string txt = network_resource_manager_->Get(url);
23+
std::string_view txt_view(txt);
24+
25+
int offset = 0;
26+
bool offset_was_specified = false;
27+
if (in_offset.has_value()) {
28+
offset = *in_offset;
29+
offset_was_specified = true;
30+
} else if (offset_map_.find(url) != offset_map_.end()) {
31+
offset = offset_map_[url];
32+
}
33+
int size = 1 << 20;
34+
if (in_size.has_value()) {
35+
size = *in_size;
36+
}
37+
if (static_cast<std::size_t>(offset) < txt_view.length()) {
38+
std::string_view out_view = txt_view.substr(offset, size);
39+
out_data->assign(out_view.data(), out_view.size());
40+
*out_eof = false;
41+
if (!offset_was_specified) {
42+
offset_map_[url] = offset + size;
43+
}
44+
} else {
45+
*out_data = "";
46+
*out_eof = true;
47+
}
48+
49+
return DispatchResponse::Success();
50+
}
51+
52+
DispatchResponse IoAgent::close(const String& in_handle) {
53+
std::string url = in_handle;
54+
network_resource_manager_->Erase(url);
55+
return DispatchResponse::Success();
56+
}
57+
} // namespace node::inspector::protocol

src/inspector/io_agent.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#ifndef SRC_INSPECTOR_IO_AGENT_H_
2+
#define SRC_INSPECTOR_IO_AGENT_H_
3+
4+
#include <memory>
5+
#include "inspector/network_resource_manager.h"
6+
#include "node/inspector/protocol/IO.h"
7+
8+
namespace node::inspector::protocol {
9+
10+
class IoAgent : public IO::Backend {
11+
public:
12+
explicit IoAgent(
13+
std::shared_ptr<NetworkResourceManager> network_resource_manager)
14+
: network_resource_manager_(std::move(network_resource_manager)) {}
15+
void Wire(UberDispatcher* dispatcher);
16+
DispatchResponse read(const String& in_handle,
17+
std::optional<int> in_offset,
18+
std::optional<int> in_size,
19+
String* out_data,
20+
bool* out_eof) override;
21+
DispatchResponse close(const String& in_handle) override;
22+
23+
private:
24+
std::shared_ptr<IO::Frontend> frontend_;
25+
std::unordered_map<std::string, int> offset_map_ =
26+
{}; // Maps stream_id to offset
27+
std::shared_ptr<NetworkResourceManager> network_resource_manager_;
28+
};
29+
} // namespace node::inspector::protocol
30+
#endif // SRC_INSPECTOR_IO_AGENT_H_

src/inspector/network_agent.cc

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
#include "network_agent.h"
2+
#include <string>
23
#include "debug_utils-inl.h"
4+
#include "env-inl.h"
5+
#include "inspector/network_resource_manager.h"
36
#include "inspector/protocol_helper.h"
47
#include "network_inspector.h"
8+
#include "node_metadata.h"
59
#include "util-inl.h"
10+
#include "uv.h"
11+
#include "v8-context.h"
612
#include "v8.h"
713

814
namespace node {
@@ -202,9 +208,15 @@ std::unique_ptr<protocol::Network::Response> createResponseFromObject(
202208
.build();
203209
}
204210

205-
NetworkAgent::NetworkAgent(NetworkInspector* inspector,
206-
v8_inspector::V8Inspector* v8_inspector)
207-
: inspector_(inspector), v8_inspector_(v8_inspector) {
211+
NetworkAgent::NetworkAgent(
212+
NetworkInspector* inspector,
213+
v8_inspector::V8Inspector* v8_inspector,
214+
Environment* env,
215+
std::shared_ptr<NetworkResourceManager> network_resource_manager)
216+
: inspector_(inspector),
217+
v8_inspector_(v8_inspector),
218+
env_(env),
219+
network_resource_manager_(std::move(network_resource_manager)) {
208220
event_notifier_map_["requestWillBeSent"] = &NetworkAgent::requestWillBeSent;
209221
event_notifier_map_["responseReceived"] = &NetworkAgent::responseReceived;
210222
event_notifier_map_["loadingFailed"] = &NetworkAgent::loadingFailed;
@@ -329,10 +341,38 @@ protocol::DispatchResponse NetworkAgent::streamResourceContent(
329341
// If the request is finished, remove the entry.
330342
requests_.erase(in_requestId);
331343
}
332-
333344
return protocol::DispatchResponse::Success();
334345
}
335346

347+
protocol::DispatchResponse NetworkAgent::loadNetworkResource(
348+
const protocol::String& in_url,
349+
std::unique_ptr<protocol::Network::LoadNetworkResourcePageResult>*
350+
out_resource) {
351+
if (!env_->options()->experimental_inspector_network_resource) {
352+
return protocol::DispatchResponse::ServerError(
353+
"Network resource loading is not enabled. This feature is "
354+
"experimental and requires --experimental-inspector-network-resource "
355+
"flag to be set.");
356+
}
357+
CHECK_NOT_NULL(network_resource_manager_);
358+
std::string data = network_resource_manager_->Get(in_url);
359+
bool found = !data.empty();
360+
if (found) {
361+
auto result = protocol::Network::LoadNetworkResourcePageResult::create()
362+
.setSuccess(true)
363+
.setStream(in_url)
364+
.build();
365+
*out_resource = std::move(result);
366+
return protocol::DispatchResponse::Success();
367+
} else {
368+
auto result = protocol::Network::LoadNetworkResourcePageResult::create()
369+
.setSuccess(false)
370+
.build();
371+
*out_resource = std::move(result);
372+
return protocol::DispatchResponse::Success();
373+
}
374+
}
375+
336376
void NetworkAgent::requestWillBeSent(v8::Local<v8::Context> context,
337377
v8::Local<v8::Object> params) {
338378
protocol::String request_id;

src/inspector/network_agent.h

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
#ifndef SRC_INSPECTOR_NETWORK_AGENT_H_
22
#define SRC_INSPECTOR_NETWORK_AGENT_H_
33

4+
#include "env.h"
5+
#include "io_agent.h"
6+
#include "network_resource_manager.h"
47
#include "node/inspector/protocol/Network.h"
58

69
#include <map>
10+
#include <memory>
711
#include <unordered_map>
812

913
namespace node {
@@ -38,8 +42,11 @@ struct RequestEntry {
3842

3943
class NetworkAgent : public protocol::Network::Backend {
4044
public:
41-
explicit NetworkAgent(NetworkInspector* inspector,
42-
v8_inspector::V8Inspector* v8_inspector);
45+
explicit NetworkAgent(
46+
NetworkInspector* inspector,
47+
v8_inspector::V8Inspector* v8_inspector,
48+
Environment* env,
49+
std::shared_ptr<NetworkResourceManager> network_resource_manager);
4350

4451
void Wire(protocol::UberDispatcher* dispatcher);
4552

@@ -60,6 +67,11 @@ class NetworkAgent : public protocol::Network::Backend {
6067
const protocol::String& in_requestId,
6168
protocol::Binary* out_bufferedData) override;
6269

70+
protocol::DispatchResponse loadNetworkResource(
71+
const protocol::String& in_url,
72+
std::unique_ptr<protocol::Network::LoadNetworkResourcePageResult>*
73+
out_resource) override;
74+
6375
void emitNotification(v8::Local<v8::Context> context,
6476
const protocol::String& event,
6577
v8::Local<v8::Object> params);
@@ -89,6 +101,8 @@ class NetworkAgent : public protocol::Network::Backend {
89101
v8::Local<v8::Object>);
90102
std::unordered_map<protocol::String, EventNotifier> event_notifier_map_;
91103
std::map<protocol::String, RequestEntry> requests_;
104+
Environment* env_;
105+
std::shared_ptr<NetworkResourceManager> network_resource_manager_;
92106
};
93107

94108
} // namespace inspector

src/inspector/network_inspector.cc

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,15 @@
33
namespace node {
44
namespace inspector {
55

6-
NetworkInspector::NetworkInspector(Environment* env,
7-
v8_inspector::V8Inspector* v8_inspector)
8-
: enabled_(false), env_(env) {
9-
network_agent_ = std::make_unique<NetworkAgent>(this, v8_inspector);
6+
NetworkInspector::NetworkInspector(
7+
Environment* env,
8+
v8_inspector::V8Inspector* v8_inspector,
9+
std::shared_ptr<NetworkResourceManager> network_resource_manager)
10+
: enabled_(false),
11+
env_(env),
12+
network_resource_manager_(std::move(network_resource_manager)) {
13+
network_agent_ = std::make_unique<NetworkAgent>(
14+
this, v8_inspector, env, network_resource_manager_);
1015
}
1116
NetworkInspector::~NetworkInspector() {
1217
network_agent_.reset();

0 commit comments

Comments
 (0)