mirror of
https://github.com/zebrajr/node.git
synced 2025-12-06 12:20:27 +01:00
inspector: add mimeType and charset support to Network.Response
Refs: https://github.com/nodejs/node/issues/53946 PR-URL: https://github.com/nodejs/node/pull/58192 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
This commit is contained in:
parent
3877800ffb
commit
a4c7c9f6d7
|
|
@ -16,6 +16,7 @@ const {
|
||||||
} = require('internal/inspector/network');
|
} = require('internal/inspector/network');
|
||||||
const dc = require('diagnostics_channel');
|
const dc = require('diagnostics_channel');
|
||||||
const { Network } = require('inspector');
|
const { Network } = require('inspector');
|
||||||
|
const { MIMEType } = require('internal/mime');
|
||||||
|
|
||||||
const kRequestUrl = Symbol('kRequestUrl');
|
const kRequestUrl = Symbol('kRequestUrl');
|
||||||
|
|
||||||
|
|
@ -93,6 +94,18 @@ function onClientResponseFinish({ request, response }) {
|
||||||
if (typeof request[kInspectorRequestId] !== 'string') {
|
if (typeof request[kInspectorRequestId] !== 'string') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mimeType;
|
||||||
|
let charset;
|
||||||
|
try {
|
||||||
|
const mimeTypeObj = new MIMEType(response.headers['content-type']);
|
||||||
|
mimeType = mimeTypeObj.essence || '';
|
||||||
|
charset = mimeTypeObj.params.get('charset') || '';
|
||||||
|
} catch {
|
||||||
|
mimeType = '';
|
||||||
|
charset = '';
|
||||||
|
}
|
||||||
|
|
||||||
Network.responseReceived({
|
Network.responseReceived({
|
||||||
requestId: request[kInspectorRequestId],
|
requestId: request[kInspectorRequestId],
|
||||||
timestamp: getMonotonicTime(),
|
timestamp: getMonotonicTime(),
|
||||||
|
|
@ -102,6 +115,8 @@ function onClientResponseFinish({ request, response }) {
|
||||||
status: response.statusCode,
|
status: response.statusCode,
|
||||||
statusText: response.statusMessage ?? '',
|
statusText: response.statusMessage ?? '',
|
||||||
headers: convertHeaderObject(response.headers)[1],
|
headers: convertHeaderObject(response.headers)[1],
|
||||||
|
mimeType,
|
||||||
|
charset,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
ArrayPrototypeFindIndex,
|
||||||
DateNow,
|
DateNow,
|
||||||
} = primordials;
|
} = primordials;
|
||||||
|
|
||||||
|
|
@ -12,6 +13,7 @@ const {
|
||||||
} = require('internal/inspector/network');
|
} = require('internal/inspector/network');
|
||||||
const dc = require('diagnostics_channel');
|
const dc = require('diagnostics_channel');
|
||||||
const { Network } = require('inspector');
|
const { Network } = require('inspector');
|
||||||
|
const { MIMEType } = require('internal/mime');
|
||||||
|
|
||||||
// Convert an undici request headers array to a plain object (Map<string, string>)
|
// Convert an undici request headers array to a plain object (Map<string, string>)
|
||||||
function requestHeadersArrayToDictionary(headers) {
|
function requestHeadersArrayToDictionary(headers) {
|
||||||
|
|
@ -91,6 +93,21 @@ function onClientResponseHeaders({ request, response }) {
|
||||||
if (typeof request[kInspectorRequestId] !== 'string') {
|
if (typeof request[kInspectorRequestId] !== 'string') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mimeType;
|
||||||
|
let charset;
|
||||||
|
try {
|
||||||
|
const contentTypeKeyIndex =
|
||||||
|
ArrayPrototypeFindIndex(response.headers, (header) => header.toString().toLowerCase() === 'content-type');
|
||||||
|
const contentType = contentTypeKeyIndex !== -1 ? response.headers[contentTypeKeyIndex + 1].toString() : '';
|
||||||
|
const mimeTypeObj = new MIMEType(contentType);
|
||||||
|
mimeType = mimeTypeObj.essence || '';
|
||||||
|
charset = mimeTypeObj.params.get('charset') || '';
|
||||||
|
} catch {
|
||||||
|
mimeType = '';
|
||||||
|
charset = '';
|
||||||
|
}
|
||||||
|
|
||||||
const url = `${request.origin}${request.path}`;
|
const url = `${request.origin}${request.path}`;
|
||||||
Network.responseReceived({
|
Network.responseReceived({
|
||||||
requestId: request[kInspectorRequestId],
|
requestId: request[kInspectorRequestId],
|
||||||
|
|
@ -102,6 +119,8 @@ function onClientResponseHeaders({ request, response }) {
|
||||||
status: response.statusCode,
|
status: response.statusCode,
|
||||||
statusText: response.statusText,
|
statusText: response.statusText,
|
||||||
headers: responseHeadersArrayToDictionary(response.headers),
|
headers: responseHeadersArrayToDictionary(response.headers),
|
||||||
|
mimeType,
|
||||||
|
charset,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -169,11 +169,23 @@ std::unique_ptr<protocol::Network::Response> createResponseFromObject(
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protocol::String mimeType;
|
||||||
|
if (!ObjectGetProtocolString(context, response, "mimeType").To(&mimeType)) {
|
||||||
|
mimeType = protocol::String("");
|
||||||
|
}
|
||||||
|
|
||||||
|
protocol::String charset = protocol::String();
|
||||||
|
if (!ObjectGetProtocolString(context, response, "charset").To(&charset)) {
|
||||||
|
charset = protocol::String("");
|
||||||
|
}
|
||||||
|
|
||||||
return protocol::Network::Response::create()
|
return protocol::Network::Response::create()
|
||||||
.setUrl(url)
|
.setUrl(url)
|
||||||
.setStatus(status)
|
.setStatus(status)
|
||||||
.setStatusText(statusText)
|
.setStatusText(statusText)
|
||||||
.setHeaders(std::move(headers))
|
.setHeaders(std::move(headers))
|
||||||
|
.setMimeType(mimeType)
|
||||||
|
.setCharset(charset)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -173,6 +173,8 @@ experimental domain Network
|
||||||
integer status
|
integer status
|
||||||
string statusText
|
string statusText
|
||||||
Headers headers
|
Headers headers
|
||||||
|
string mimeType
|
||||||
|
string charset
|
||||||
|
|
||||||
# Request / response headers as keys / values of JSON object.
|
# Request / response headers as keys / values of JSON object.
|
||||||
type Headers extends object
|
type Headers extends object
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,9 @@ const EXPECTED_EVENTS = {
|
||||||
url: 'https://nodejs.org/en',
|
url: 'https://nodejs.org/en',
|
||||||
status: 200,
|
status: 200,
|
||||||
statusText: '',
|
statusText: '',
|
||||||
headers: { host: 'nodejs.org' }
|
headers: { host: 'nodejs.org' },
|
||||||
|
mimeType: 'text/html',
|
||||||
|
charset: 'utf-8'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
expected: {
|
expected: {
|
||||||
|
|
@ -53,7 +55,9 @@ const EXPECTED_EVENTS = {
|
||||||
url: 'https://nodejs.org/en',
|
url: 'https://nodejs.org/en',
|
||||||
status: 200,
|
status: 200,
|
||||||
statusText: '',
|
statusText: '',
|
||||||
headers: { host: 'nodejs.org' }
|
headers: { host: 'nodejs.org' },
|
||||||
|
mimeType: 'text/html',
|
||||||
|
charset: 'utf-8'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
170
test/parallel/test-inspector-network-content-type.js
Normal file
170
test/parallel/test-inspector-network-content-type.js
Normal file
|
|
@ -0,0 +1,170 @@
|
||||||
|
// Flags: --inspect=0 --experimental-network-inspection
|
||||||
|
'use strict';
|
||||||
|
const common = require('../common');
|
||||||
|
|
||||||
|
common.skipIfInspectorDisabled();
|
||||||
|
|
||||||
|
const assert = require('node:assert');
|
||||||
|
const http = require('node:http');
|
||||||
|
const inspector = require('node:inspector/promises');
|
||||||
|
|
||||||
|
const testNetworkInspection = async (session, port, assert) => {
|
||||||
|
let assertPromise = assert(session);
|
||||||
|
fetch(`http://127.0.0.1:${port}/hello-world`).then(common.mustCall());
|
||||||
|
await assertPromise;
|
||||||
|
session.removeAllListeners();
|
||||||
|
assertPromise = assert(session);
|
||||||
|
new Promise((resolve, reject) => {
|
||||||
|
const req = http.get(
|
||||||
|
{
|
||||||
|
host: '127.0.0.1',
|
||||||
|
port,
|
||||||
|
path: '/hello-world',
|
||||||
|
},
|
||||||
|
common.mustCall((res) => {
|
||||||
|
res.on('data', () => {});
|
||||||
|
res.on('end', () => {});
|
||||||
|
resolve(res);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
req.on('error', reject);
|
||||||
|
});
|
||||||
|
await assertPromise;
|
||||||
|
session.removeAllListeners();
|
||||||
|
};
|
||||||
|
|
||||||
|
const test = (handleRequest, testSessionFunc) => new Promise((resolve) => {
|
||||||
|
const session = new inspector.Session();
|
||||||
|
session.connect();
|
||||||
|
const httpServer = http.createServer(handleRequest);
|
||||||
|
httpServer.listen(0, async () => {
|
||||||
|
try {
|
||||||
|
await session.post('Network.enable');
|
||||||
|
await testNetworkInspection(
|
||||||
|
session,
|
||||||
|
httpServer.address().port,
|
||||||
|
testSessionFunc
|
||||||
|
);
|
||||||
|
await session.post('Network.disable');
|
||||||
|
} catch (err) {
|
||||||
|
assert.fail(err);
|
||||||
|
} finally {
|
||||||
|
await session.disconnect();
|
||||||
|
await httpServer.close();
|
||||||
|
await inspector.close();
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
await test(
|
||||||
|
(req, res) => {
|
||||||
|
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
|
||||||
|
res.writeHead(200);
|
||||||
|
res.end('hello world\n');
|
||||||
|
},
|
||||||
|
common.mustCall(
|
||||||
|
(session) =>
|
||||||
|
new Promise((resolve) => {
|
||||||
|
session.on(
|
||||||
|
'Network.responseReceived',
|
||||||
|
common.mustCall(({ params }) => {
|
||||||
|
assert.strictEqual(params.response.mimeType, 'text/plain');
|
||||||
|
assert.strictEqual(params.response.charset, 'utf-8');
|
||||||
|
})
|
||||||
|
);
|
||||||
|
session.on(
|
||||||
|
'Network.loadingFinished',
|
||||||
|
common.mustCall(({ params }) => {
|
||||||
|
assert.ok(params.requestId.startsWith('node-network-event-'));
|
||||||
|
assert.strictEqual(typeof params.timestamp, 'number');
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
2
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
await test(
|
||||||
|
(req, res) => {
|
||||||
|
res.writeHead(200, {});
|
||||||
|
res.end('hello world\n');
|
||||||
|
},
|
||||||
|
common.mustCall((session) =>
|
||||||
|
new Promise((resolve) => {
|
||||||
|
session.on(
|
||||||
|
'Network.responseReceived',
|
||||||
|
common.mustCall(({ params }) => {
|
||||||
|
assert.strictEqual(params.response.mimeType, '');
|
||||||
|
assert.strictEqual(params.response.charset, '');
|
||||||
|
})
|
||||||
|
);
|
||||||
|
session.on(
|
||||||
|
'Network.loadingFinished',
|
||||||
|
common.mustCall(({ params }) => {
|
||||||
|
assert.ok(params.requestId.startsWith('node-network-event-'));
|
||||||
|
assert.strictEqual(typeof params.timestamp, 'number');
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}), 2
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
await test(
|
||||||
|
(req, res) => {
|
||||||
|
res.setHeader('Content-Type', 'invalid content-type');
|
||||||
|
res.writeHead(200);
|
||||||
|
res.end('hello world\n');
|
||||||
|
},
|
||||||
|
common.mustCall((session) =>
|
||||||
|
new Promise((resolve) => {
|
||||||
|
session.on(
|
||||||
|
'Network.responseReceived',
|
||||||
|
common.mustCall(({ params }) => {
|
||||||
|
assert.strictEqual(params.response.mimeType, '');
|
||||||
|
assert.strictEqual(params.response.charset, '');
|
||||||
|
})
|
||||||
|
);
|
||||||
|
session.on(
|
||||||
|
'Network.loadingFinished',
|
||||||
|
common.mustCall(({ params }) => {
|
||||||
|
assert.ok(params.requestId.startsWith('node-network-event-'));
|
||||||
|
assert.strictEqual(typeof params.timestamp, 'number');
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}), 2
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
await test(
|
||||||
|
(req, res) => {
|
||||||
|
res.setHeader('Content-Type', 'text/plain');
|
||||||
|
res.writeHead(200);
|
||||||
|
res.end('hello world\n');
|
||||||
|
},
|
||||||
|
common.mustCall((session) =>
|
||||||
|
new Promise((resolve) => {
|
||||||
|
session.on(
|
||||||
|
'Network.responseReceived',
|
||||||
|
common.mustCall(({ params }) => {
|
||||||
|
assert.strictEqual(params.response.mimeType, 'text/plain');
|
||||||
|
assert.strictEqual(params.response.charset, '');
|
||||||
|
})
|
||||||
|
);
|
||||||
|
session.on(
|
||||||
|
'Network.loadingFinished',
|
||||||
|
common.mustCall(({ params }) => {
|
||||||
|
assert.ok(params.requestId.startsWith('node-network-event-'));
|
||||||
|
assert.strictEqual(typeof params.timestamp, 'number');
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}), 2
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
})().then(common.mustCall());
|
||||||
|
|
@ -36,6 +36,7 @@ const setResponseHeaders = (res) => {
|
||||||
res.setHeader('etag', 12345);
|
res.setHeader('etag', 12345);
|
||||||
res.setHeader('Set-Cookie', ['key1=value1', 'key2=value2']);
|
res.setHeader('Set-Cookie', ['key1=value1', 'key2=value2']);
|
||||||
res.setHeader('x-header2', ['value1', 'value2']);
|
res.setHeader('x-header2', ['value1', 'value2']);
|
||||||
|
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleRequest = (req, res) => {
|
const handleRequest = (req, res) => {
|
||||||
|
|
@ -101,6 +102,8 @@ const testHttpGet = () => new Promise((resolve, reject) => {
|
||||||
assert.strictEqual(params.response.headers.etag, '12345');
|
assert.strictEqual(params.response.headers.etag, '12345');
|
||||||
assert.strictEqual(params.response.headers['Set-Cookie'], 'key1=value1\nkey2=value2');
|
assert.strictEqual(params.response.headers['Set-Cookie'], 'key1=value1\nkey2=value2');
|
||||||
assert.strictEqual(params.response.headers['x-header2'], 'value1, value2');
|
assert.strictEqual(params.response.headers['x-header2'], 'value1, value2');
|
||||||
|
assert.strictEqual(params.response.mimeType, 'text/plain');
|
||||||
|
assert.strictEqual(params.response.charset, 'utf-8');
|
||||||
}));
|
}));
|
||||||
session.on('Network.loadingFinished', common.mustCall(({ params }) => {
|
session.on('Network.loadingFinished', common.mustCall(({ params }) => {
|
||||||
assert.ok(params.requestId.startsWith('node-network-event-'));
|
assert.ok(params.requestId.startsWith('node-network-event-'));
|
||||||
|
|
@ -138,6 +141,8 @@ const testHttpsGet = () => new Promise((resolve, reject) => {
|
||||||
assert.strictEqual(params.response.headers.etag, '12345');
|
assert.strictEqual(params.response.headers.etag, '12345');
|
||||||
assert.strictEqual(params.response.headers['Set-Cookie'], 'key1=value1\nkey2=value2');
|
assert.strictEqual(params.response.headers['Set-Cookie'], 'key1=value1\nkey2=value2');
|
||||||
assert.strictEqual(params.response.headers['x-header2'], 'value1, value2');
|
assert.strictEqual(params.response.headers['x-header2'], 'value1, value2');
|
||||||
|
assert.strictEqual(params.response.mimeType, 'text/plain');
|
||||||
|
assert.strictEqual(params.response.charset, 'utf-8');
|
||||||
}));
|
}));
|
||||||
session.on('Network.loadingFinished', common.mustCall(({ params }) => {
|
session.on('Network.loadingFinished', common.mustCall(({ params }) => {
|
||||||
assert.ok(params.requestId.startsWith('node-network-event-'));
|
assert.ok(params.requestId.startsWith('node-network-event-'));
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ const setResponseHeaders = (res) => {
|
||||||
res.setHeader('etag', 12345);
|
res.setHeader('etag', 12345);
|
||||||
res.setHeader('Set-Cookie', ['key1=value1', 'key2=value2']);
|
res.setHeader('Set-Cookie', ['key1=value1', 'key2=value2']);
|
||||||
res.setHeader('x-header2', ['value1', 'value2']);
|
res.setHeader('x-header2', ['value1', 'value2']);
|
||||||
|
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
|
||||||
};
|
};
|
||||||
|
|
||||||
const kTimeout = 1000;
|
const kTimeout = 1000;
|
||||||
|
|
@ -106,6 +107,8 @@ function verifyResponseReceived({ method, params }, expect) {
|
||||||
assert.strictEqual(params.response.headers.etag, '12345');
|
assert.strictEqual(params.response.headers.etag, '12345');
|
||||||
assert.strictEqual(params.response.headers['set-cookie'], 'key1=value1\nkey2=value2');
|
assert.strictEqual(params.response.headers['set-cookie'], 'key1=value1\nkey2=value2');
|
||||||
assert.strictEqual(params.response.headers['x-header2'], 'value1, value2');
|
assert.strictEqual(params.response.headers['x-header2'], 'value1, value2');
|
||||||
|
assert.strictEqual(params.response.mimeType, 'text/plain');
|
||||||
|
assert.strictEqual(params.response.charset, 'utf-8');
|
||||||
|
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user