Skip to content

Commit bf72384

Browse files
authored
feat(ext/node): buffer Network.* bodies for inspector body-fetch commands (#34201)
The Node-style inspector Network domain shipped in #32707 plumbed event broadcast for Network.dataReceived / dataSent / requestWillBeSent / responseReceived, but the three CDP commands consumers actually use to pull bodies back out — Network.getResponseBody, Network.streamResourceContent, and Network.getRequestPostData — weren't implemented, so any tool that called them got a -32601 method-not-found error. This adds a bounded per-process buffer (32 requests max, 10 MB per request, FIFO eviction) on the inspector that captures bodies as the events flow through op_inspector_emit_protocol_event, plus dispatcher handlers for the three commands on both the local-session and WebSocket paths. Charset handling mirrors Node: utf-8 returns a decoded string, anything else returns base64. Raw Buffer/Uint8Array data passed to inspector.Network.dataReceived/.dataSent is base64-encoded at the polyfill layer to match the wire format consumers expect. Clears four node_compat tests: test-inspector-network-data-received.js, test-inspector-network-data-sent.js, test-inspector-network-arbitrary-data.js, and test-inspector-emit-protocol-event.js
1 parent 9847847 commit bf72384

6 files changed

Lines changed: 504 additions & 52 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ext/node/ops/inspector.rs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,17 @@ pub fn op_inspector_emit_protocol_event(
118118
let needs_initiator = event_name == "Network.requestWillBeSent"
119119
|| event_name == "Network.webSocketCreated";
120120
let needs_has_post_data = event_name == "Network.requestWillBeSent";
121+
let needs_capture = matches!(
122+
event_name.as_str(),
123+
"Network.requestWillBeSent"
124+
| "Network.responseReceived"
125+
| "Network.loadingFinished"
126+
| "Network.loadingFailed"
127+
| "Network.dataReceived"
128+
| "Network.dataSent"
129+
);
121130

122-
if !needs_initiator && !needs_has_post_data {
131+
if !needs_initiator && !needs_has_post_data && !needs_capture {
123132
inspector.broadcast_to_sessions(&event_name, &params);
124133
return;
125134
}
@@ -147,8 +156,16 @@ pub fn op_inspector_emit_protocol_event(
147156
.or_insert(serde_json::Value::Bool(false));
148157
}
149158

150-
let augmented = serde_json::to_string(&parsed).unwrap();
151-
inspector.broadcast_to_sessions(&event_name, &augmented);
159+
let should_broadcast = if needs_capture {
160+
inspector.capture_network_event(&event_name, &parsed)
161+
} else {
162+
true
163+
};
164+
165+
if should_broadcast {
166+
let augmented = serde_json::to_string(&parsed).unwrap();
167+
inspector.broadcast_to_sessions(&event_name, &augmented);
168+
}
152169
}
153170

154171
fn capture_initiator(scope: &mut v8::PinScope<'_, '_>) -> serde_json::Value {

ext/node/polyfills/inspector.js

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
(function () {
77
const { core, primordials } = globalThis.__bootstrap;
88
const {
9+
op_base64_encode_from_buffer,
910
op_get_extras_binding_object,
1011
op_inspector_close,
1112
op_inspector_connect,
@@ -53,12 +54,39 @@ function isLoopback(host) {
5354
const {
5455
ArrayPrototypePush,
5556
ArrayPrototypeShift,
57+
ObjectAssign,
5658
SymbolDispose,
5759
JSONParse,
5860
JSONStringify,
5961
SafeMap,
62+
TypedArrayPrototypeGetByteLength,
63+
TypedArrayPrototypeGetSymbolToStringTag,
64+
Uint8Array,
6065
} = primordials;
6166

67+
function encodeNetworkData(data) {
68+
if (data == null) return undefined;
69+
if (typeof data === "string") {
70+
// Encode UTF-8 string as base64.
71+
const buf = core.encode(data);
72+
return op_base64_encode_from_buffer(buf, 0, buf.byteLength);
73+
}
74+
if (TypedArrayPrototypeGetSymbolToStringTag(data) === "Uint8Array") {
75+
return op_base64_encode_from_buffer(
76+
data,
77+
0,
78+
TypedArrayPrototypeGetByteLength(data),
79+
);
80+
}
81+
if (data instanceof ArrayBuffer) {
82+
const view = new Uint8Array(data);
83+
return op_base64_encode_from_buffer(view, 0, view.byteLength);
84+
}
85+
throw new TypeError(
86+
"Expected data to be a string, Buffer, Uint8Array, or ArrayBuffer",
87+
);
88+
}
89+
6290
class Session extends EventEmitter {
6391
#connection = null;
6492
#nextId = 1;
@@ -247,6 +275,16 @@ function broadcastToFrontend(eventName, params) {
247275
op_inspector_emit_protocol_event(eventName, JSONStringify(params ?? {}));
248276
}
249277

278+
function broadcastNetworkData(eventName, params) {
279+
if (params && params.data !== undefined) {
280+
const encoded = encodeNetworkData(params.data);
281+
if (encoded !== params.data) {
282+
params = ObjectAssign({ __proto__: null }, params, { data: encoded });
283+
}
284+
}
285+
broadcastToFrontend(eventName, params);
286+
}
287+
250288
const Network = {
251289
requestWillBeSent: (params) =>
252290
broadcastToFrontend("Network.requestWillBeSent", params),
@@ -256,8 +294,9 @@ const Network = {
256294
broadcastToFrontend("Network.loadingFinished", params),
257295
loadingFailed: (params) =>
258296
broadcastToFrontend("Network.loadingFailed", params),
259-
dataReceived: (params) => broadcastToFrontend("Network.dataReceived", params),
260-
dataSent: (params) => broadcastToFrontend("Network.dataSent", params),
297+
dataReceived: (params) =>
298+
broadcastNetworkData("Network.dataReceived", params),
299+
dataSent: (params) => broadcastNetworkData("Network.dataSent", params),
261300
webSocketCreated: (params) =>
262301
broadcastToFrontend("Network.webSocketCreated", params),
263302
webSocketHandshakeResponseReceived: (params) =>

libs/core/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ uv_compat_export = []
2828
[dependencies]
2929
anyhow.workspace = true
3030
az = "1.2.1"
31+
base64 = { workspace = true }
3132
bincode.workspace = true
3233
bit-set.workspace = true
3334
bit-vec.workspace = true

0 commit comments

Comments
 (0)