Skip to content

Commit 435f550

Browse files
committed
inspector: initial support websocket inspection
Refs: #53946
1 parent 134625d commit 435f550

File tree

9 files changed

+422
-1
lines changed

9 files changed

+422
-1
lines changed

doc/api/inspector.md

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

605+
### `inspector.Network.webSocketCreated([params])`
606+
607+
<!-- YAML
608+
added:
609+
- REPLACEME
610+
-->
611+
612+
* `params` {Object}
613+
614+
This feature is only available with the `--experimental-network-inspection` flag enabled.
615+
616+
Broadcasts the `Network.webSocketCreated` event to connected frontends. This event indicates that
617+
a WebSocket connection has been initiated.
618+
619+
### `inspector.Network.webSocketHandshakeResponseReceived([params])`
620+
621+
<!-- YAML
622+
added:
623+
- REPLACEME
624+
-->
625+
626+
* `params` {Object}
627+
628+
This feature is only available with the `--experimental-network-inspection` flag enabled.
629+
630+
Broadcasts the `Network.webSocketHandshakeResponseReceived` event to connected frontends.
631+
This event indicates that the WebSocket handshake response has been received.
632+
633+
### `inspector.Network.webSocketClosed([params])`
634+
635+
<!-- YAML
636+
added:
637+
- REPLACEME
638+
-->
639+
640+
* `params` {Object}
641+
642+
This feature is only available with the `--experimental-network-inspection` flag enabled.
643+
644+
Broadcasts the `Network.webSocketClosed` event to connected frontends.
645+
This event indicates that a WebSocket connection has been closed.
646+
605647
### `inspector.NetworkResources.put`
606648

607649
<!-- YAML

lib/inspector.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,10 @@ const Network = {
219219
loadingFailed: (params) => broadcastToFrontend('Network.loadingFailed', params),
220220
dataSent: (params) => broadcastToFrontend('Network.dataSent', params),
221221
dataReceived: (params) => broadcastToFrontend('Network.dataReceived', params),
222+
webSocketCreated: (params) => broadcastToFrontend('Network.webSocketCreated', params),
223+
webSocketClosed: (params) => broadcastToFrontend('Network.webSocketClosed', params),
224+
webSocketHandshakeResponseReceived:
225+
(params) => broadcastToFrontend('Network.webSocketHandshakeResponseReceived', params),
222226
};
223227

224228
const NetworkResources = {

lib/internal/inspector/network_undici.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
const {
44
DateNow,
5+
NumberMAX_SAFE_INTEGER,
56
StringPrototypeToLowerCase,
67
} = primordials;
78

@@ -206,6 +207,43 @@ function onClientResponseFinish({ request }) {
206207
});
207208
}
208209

210+
let networkRequestId = 0;
211+
function getNextNetworkRequestId() {
212+
if (networkRequestId === NumberMAX_SAFE_INTEGER) {
213+
networkRequestId = 0;
214+
}
215+
return `node-network-event-${++networkRequestId}`;
216+
}
217+
218+
function onWebSocketOpen({ websocket }) {
219+
websocket[kInspectorRequestId] = getNextNetworkRequestId();
220+
const url = websocket.url.toString();
221+
Network.webSocketCreated({
222+
requestId: websocket[kInspectorRequestId],
223+
url,
224+
});
225+
// TODO: Use handshake response data from undici diagnostics when available.
226+
Network.webSocketHandshakeResponseReceived({
227+
requestId: websocket[kInspectorRequestId],
228+
timestamp: getMonotonicTime(),
229+
response: {
230+
status: 101,
231+
statusText: 'Switching Protocols',
232+
headers: {},
233+
},
234+
});
235+
}
236+
237+
function onWebSocketClose({ websocket }) {
238+
if (typeof websocket[kInspectorRequestId] !== 'string') {
239+
return;
240+
}
241+
Network.webSocketClosed({
242+
requestId: websocket[kInspectorRequestId],
243+
timestamp: getMonotonicTime(),
244+
});
245+
}
246+
209247
function enable() {
210248
dc.subscribe('undici:request:create', onClientRequestStart);
211249
dc.subscribe('undici:request:error', onClientRequestError);
@@ -214,6 +252,8 @@ function enable() {
214252
dc.subscribe('undici:request:bodyChunkSent', onClientRequestBodyChunkSent);
215253
dc.subscribe('undici:request:bodySent', onClientRequestBodySent);
216254
dc.subscribe('undici:request:bodyChunkReceived', onClientRequestBodyChunkReceived);
255+
dc.subscribe('undici:websocket:open', onWebSocketOpen);
256+
dc.subscribe('undici:websocket:close', onWebSocketClose);
217257
}
218258

219259
function disable() {
@@ -224,6 +264,8 @@ function disable() {
224264
dc.unsubscribe('undici:request:bodyChunkSent', onClientRequestBodyChunkSent);
225265
dc.unsubscribe('undici:request:bodySent', onClientRequestBodySent);
226266
dc.unsubscribe('undici:request:bodyChunkReceived', onClientRequestBodyChunkReceived);
267+
dc.unsubscribe('undici:websocket:open', onWebSocketOpen);
268+
dc.unsubscribe('undici:websocket:close', onWebSocketClose);
227269
}
228270

229271
module.exports = {

src/inspector/network_agent.cc

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,35 @@ std::unique_ptr<protocol::Network::Response> createResponseFromObject(
208208
.build();
209209
}
210210

211+
std::unique_ptr<protocol::Network::WebSocketResponse> createWebSocketResponse(
212+
v8::Local<v8::Context> context, Local<Object> response) {
213+
HandleScope handle_scope(context->GetIsolate());
214+
int status;
215+
if (!ObjectGetInt(context, response, "status").To(&status)) {
216+
return {};
217+
}
218+
protocol::String statusText;
219+
if (!ObjectGetProtocolString(context, response, "statusText")
220+
.To(&statusText)) {
221+
return {};
222+
}
223+
Local<Object> headers_obj;
224+
if (!ObjectGetObject(context, response, "headers").ToLocal(&headers_obj)) {
225+
return {};
226+
}
227+
std::unique_ptr<protocol::Network::Headers> headers =
228+
createHeadersFromObject(context, headers_obj);
229+
if (!headers) {
230+
return {};
231+
}
232+
233+
return protocol::Network::WebSocketResponse::create()
234+
.setStatus(status)
235+
.setStatusText(statusText)
236+
.setHeaders(std::move(headers))
237+
.build();
238+
}
239+
211240
NetworkAgent::NetworkAgent(
212241
NetworkInspector* inspector,
213242
v8_inspector::V8Inspector* v8_inspector,
@@ -223,6 +252,64 @@ NetworkAgent::NetworkAgent(
223252
event_notifier_map_["loadingFinished"] = &NetworkAgent::loadingFinished;
224253
event_notifier_map_["dataSent"] = &NetworkAgent::dataSent;
225254
event_notifier_map_["dataReceived"] = &NetworkAgent::dataReceived;
255+
event_notifier_map_["webSocketCreated"] = &NetworkAgent::webSocketCreated;
256+
event_notifier_map_["webSocketClosed"] = &NetworkAgent::webSocketClosed;
257+
event_notifier_map_["webSocketHandshakeResponseReceived"] =
258+
&NetworkAgent::webSocketHandshakeResponseReceived;
259+
}
260+
261+
void NetworkAgent::webSocketCreated(v8::Local<v8::Context> context,
262+
v8::Local<v8::Object> params) {
263+
protocol::String request_id;
264+
if (!ObjectGetProtocolString(context, params, "requestId").To(&request_id)) {
265+
return;
266+
}
267+
protocol::String url;
268+
if (!ObjectGetProtocolString(context, params, "url").To(&url)) {
269+
return;
270+
}
271+
std::unique_ptr<protocol::Network::Initiator> initiator =
272+
protocol::Network::Initiator::create()
273+
.setType(protocol::Network::Initiator::TypeEnum::Script)
274+
.setStack(
275+
v8_inspector_->captureStackTrace(true)->buildInspectorObject(0))
276+
.build();
277+
frontend_->webSocketCreated(request_id, url, std::move(initiator));
278+
}
279+
280+
void NetworkAgent::webSocketClosed(v8::Local<v8::Context> context,
281+
v8::Local<v8::Object> params) {
282+
protocol::String request_id;
283+
if (!ObjectGetProtocolString(context, params, "requestId").To(&request_id)) {
284+
return;
285+
}
286+
double timestamp;
287+
if (!ObjectGetDouble(context, params, "timestamp").To(&timestamp)) {
288+
return;
289+
}
290+
frontend_->webSocketClosed(request_id, timestamp);
291+
}
292+
293+
void NetworkAgent::webSocketHandshakeResponseReceived(
294+
v8::Local<v8::Context> context, v8::Local<v8::Object> params) {
295+
protocol::String request_id;
296+
if (!ObjectGetProtocolString(context, params, "requestId").To(&request_id)) {
297+
return;
298+
}
299+
double timestamp;
300+
if (!ObjectGetDouble(context, params, "timestamp").To(&timestamp)) {
301+
return;
302+
}
303+
Local<Object> response_obj;
304+
if (!ObjectGetObject(context, params, "response").ToLocal(&response_obj)) {
305+
return;
306+
}
307+
auto response = createWebSocketResponse(context, response_obj);
308+
if (!response) {
309+
return;
310+
}
311+
frontend_->webSocketHandshakeResponseReceived(
312+
request_id, timestamp, std::move(response));
226313
}
227314

228315
void NetworkAgent::emitNotification(v8::Local<v8::Context> context,

src/inspector/network_agent.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,13 @@ class NetworkAgent : public protocol::Network::Backend {
9393
void dataReceived(v8::Local<v8::Context> context,
9494
v8::Local<v8::Object> params);
9595

96+
void webSocketCreated(v8::Local<v8::Context> context,
97+
v8::Local<v8::Object> params);
98+
void webSocketClosed(v8::Local<v8::Context> context,
99+
v8::Local<v8::Object> params);
100+
void webSocketHandshakeResponseReceived(v8::Local<v8::Context> context,
101+
v8::Local<v8::Object> params);
102+
96103
private:
97104
NetworkInspector* inspector_;
98105
v8_inspector::V8Inspector* v8_inspector_;

src/inspector/node_protocol.pdl

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,16 @@ experimental domain Network
185185
boolean success
186186
optional IO.StreamHandle stream
187187

188+
# WebSocket response data.
189+
type WebSocketResponse extends object
190+
properties
191+
# HTTP response status code.
192+
integer status
193+
# HTTP response status text.
194+
string statusText
195+
# HTTP response headers.
196+
Headers headers
197+
188198
# Disables network tracking, prevents network events from being sent to the client.
189199
command disable
190200

@@ -285,6 +295,31 @@ experimental domain Network
285295
integer encodedDataLength
286296
# Data that was received.
287297
experimental optional binary data
298+
# Fired upon WebSocket creation.
299+
event webSocketCreated
300+
parameters
301+
# Request identifier.
302+
RequestId requestId
303+
# WebSocket request URL.
304+
string url
305+
# Request initiator.
306+
Initiator initiator
307+
# Fired when WebSocket is closed.
308+
event webSocketClosed
309+
parameters
310+
# Request identifier.
311+
RequestId requestId
312+
# Timestamp.
313+
MonotonicTime timestamp
314+
# Fired when WebSocket handshake response becomes available.
315+
event webSocketHandshakeResponseReceived
316+
parameters
317+
# Request identifier.
318+
RequestId requestId
319+
# Timestamp.
320+
MonotonicTime timestamp
321+
# WebSocket response data.
322+
WebSocketResponse response
288323

289324
# Support for inspecting node process state.
290325
experimental domain NodeRuntime

0 commit comments

Comments
 (0)