Skip to content

Commit 0d969a6

Browse files
legendecasRafaelGSS
authored andcommitted
inspector: support undici traffic data inspection
Support undici sent and received data inspection in Chrome DevTools. PR-URL: #58953 Reviewed-By: Ryuhei Shima <[email protected]> Reviewed-By: Moshe Atlow <[email protected]>
1 parent 78628d6 commit 0d969a6

File tree

3 files changed

+197
-117
lines changed

3 files changed

+197
-117
lines changed

lib/internal/inspector/network.js

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

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

@@ -52,8 +53,8 @@ function sniffMimeType(contentType) {
5253
let charset;
5354
try {
5455
const mimeTypeObj = new MIMEType(contentType);
55-
mimeType = mimeTypeObj.essence || '';
56-
charset = mimeTypeObj.params.get('charset') || '';
56+
mimeType = StringPrototypeToLowerCase(mimeTypeObj.essence || '');
57+
charset = StringPrototypeToLowerCase(mimeTypeObj.params.get('charset') || '');
5758
} catch {
5859
mimeType = '';
5960
charset = '';

lib/internal/inspector/network_undici.js

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

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

78
const {
@@ -13,16 +14,25 @@ const {
1314
} = require('internal/inspector/network');
1415
const dc = require('diagnostics_channel');
1516
const { Network } = require('inspector');
17+
const { Buffer } = require('buffer');
1618

1719
// Convert an undici request headers array to a plain object (Map<string, string>)
1820
function requestHeadersArrayToDictionary(headers) {
1921
const dict = {};
22+
let charset;
23+
let mimeType;
2024
for (let idx = 0; idx < headers.length; idx += 2) {
2125
const key = `${headers[idx]}`;
2226
const value = `${headers[idx + 1]}`;
2327
dict[key] = value;
28+
29+
if (StringPrototypeToLowerCase(key) === 'content-type') {
30+
const result = sniffMimeType(value);
31+
charset = result.charset;
32+
mimeType = result.mimeType;
33+
}
2434
}
25-
return dict;
35+
return [dict, charset, mimeType];
2636
};
2737

2838
// Convert an undici response headers array to a plain object (Map<string, string>)
@@ -32,7 +42,7 @@ function responseHeadersArrayToDictionary(headers) {
3242
let mimeType;
3343
for (let idx = 0; idx < headers.length; idx += 2) {
3444
const key = `${headers[idx]}`;
35-
const lowerCasedKey = key.toLowerCase();
45+
const lowerCasedKey = StringPrototypeToLowerCase(key);
3646
const value = `${headers[idx + 1]}`;
3747
const prevValue = dict[key];
3848

@@ -63,8 +73,7 @@ function onClientRequestStart({ request }) {
6373
const url = `${request.origin}${request.path}`;
6474
request[kInspectorRequestId] = getNextRequestId();
6575

66-
const headers = requestHeadersArrayToDictionary(request.headers);
67-
const { charset } = sniffMimeType(headers);
76+
const { 0: headers, 1: charset } = requestHeadersArrayToDictionary(request.headers);
6877

6978
Network.requestWillBeSent({
7079
requestId: request[kInspectorRequestId],
@@ -74,7 +83,8 @@ function onClientRequestStart({ request }) {
7483
request: {
7584
url,
7685
method: request.method,
77-
headers: requestHeadersArrayToDictionary(request.headers),
86+
headers: headers,
87+
hasPostData: request.body != null,
7888
},
7989
});
8090
}
@@ -97,6 +107,40 @@ function onClientRequestError({ request, error }) {
97107
});
98108
}
99109

110+
/**
111+
* When a chunk of the request body is being sent, cache it until `getRequestPostData` request.
112+
* https://chromedevtools.github.io/devtools-protocol/1-3/Network/#method-getRequestPostData
113+
* @param {{ request: undici.Request, chunk: Uint8Array | string }} event
114+
*/
115+
function onClientRequestBodyChunkSent({ request, chunk }) {
116+
if (typeof request[kInspectorRequestId] !== 'string') {
117+
return;
118+
}
119+
120+
const buffer = Buffer.from(chunk);
121+
Network.dataSent({
122+
requestId: request[kInspectorRequestId],
123+
timestamp: getMonotonicTime(),
124+
dataLength: buffer.byteLength,
125+
data: buffer,
126+
});
127+
}
128+
129+
/**
130+
* Mark a request body as fully sent.
131+
* @param {{request: undici.Request}} event
132+
*/
133+
function onClientRequestBodySent({ request }) {
134+
if (typeof request[kInspectorRequestId] !== 'string') {
135+
return;
136+
}
137+
138+
Network.dataSent({
139+
requestId: request[kInspectorRequestId],
140+
finished: true,
141+
});
142+
}
143+
100144
/**
101145
* When response headers are received, emit Network.responseReceived event.
102146
* https://chromedevtools.github.io/devtools-protocol/1-3/Network/#event-responseReceived
@@ -126,6 +170,27 @@ function onClientResponseHeaders({ request, response }) {
126170
});
127171
}
128172

173+
/**
174+
* When a chunk of the response body has been received, cache it until `getResponseBody` request
175+
* https://chromedevtools.github.io/devtools-protocol/1-3/Network/#method-getResponseBody or
176+
* stream it with `streamResourceContent` request.
177+
* https://chromedevtools.github.io/devtools-protocol/tot/Network/#method-streamResourceContent
178+
* @param {{ request: undici.Request, chunk: Uint8Array | string }} event
179+
*/
180+
function onClientRequestBodyChunkReceived({ request, chunk }) {
181+
if (typeof request[kInspectorRequestId] !== 'string') {
182+
return;
183+
}
184+
185+
Network.dataReceived({
186+
requestId: request[kInspectorRequestId],
187+
timestamp: getMonotonicTime(),
188+
dataLength: chunk.byteLength,
189+
encodedDataLength: chunk.byteLength,
190+
data: chunk,
191+
});
192+
}
193+
129194
/**
130195
* When a response is completed, emit Network.loadingFinished event.
131196
* https://chromedevtools.github.io/devtools-protocol/1-3/Network/#event-loadingFinished
@@ -146,13 +211,19 @@ function enable() {
146211
dc.subscribe('undici:request:error', onClientRequestError);
147212
dc.subscribe('undici:request:headers', onClientResponseHeaders);
148213
dc.subscribe('undici:request:trailers', onClientResponseFinish);
214+
dc.subscribe('undici:request:bodyChunkSent', onClientRequestBodyChunkSent);
215+
dc.subscribe('undici:request:bodySent', onClientRequestBodySent);
216+
dc.subscribe('undici:request:bodyChunkReceived', onClientRequestBodyChunkReceived);
149217
}
150218

151219
function disable() {
152220
dc.unsubscribe('undici:request:create', onClientRequestStart);
153221
dc.unsubscribe('undici:request:error', onClientRequestError);
154222
dc.unsubscribe('undici:request:headers', onClientResponseHeaders);
155223
dc.unsubscribe('undici:request:trailers', onClientResponseFinish);
224+
dc.unsubscribe('undici:request:bodyChunkSent', onClientRequestBodyChunkSent);
225+
dc.unsubscribe('undici:request:bodySent', onClientRequestBodySent);
226+
dc.unsubscribe('undici:request:bodyChunkReceived', onClientRequestBodyChunkReceived);
156227
}
157228

158229
module.exports = {

0 commit comments

Comments
 (0)