diff --git a/lib/_http_common.js b/lib/_http_common.js index d5e7bdedee39fb..6731f155e617c9 100644 --- a/lib/_http_common.js +++ b/lib/_http_common.js @@ -42,6 +42,7 @@ const { const kIncomingMessage = Symbol('IncomingMessage'); const kSkipPendingData = Symbol('SkipPendingData'); +const kOnBodyChunk = Symbol('onBodyChunk'); const kOnMessageBegin = HTTPParser.kOnMessageBegin | 0; const kOnHeaders = HTTPParser.kOnHeaders | 0; const kOnHeadersComplete = HTTPParser.kOnHeadersComplete | 0; @@ -134,6 +135,7 @@ function parserOnBody(b) { // Pretend this was the result of a stream._read call. if (!stream._dumped) { + stream[kOnBodyChunk]?.(b); const ret = stream.push(b); if (!ret) readStop(this.socket); @@ -337,6 +339,7 @@ module.exports = { methods, parsers, kIncomingMessage, + kOnBodyChunk, HTTPParser, isLenient, calculateLenientFlags, diff --git a/lib/internal/inspector/network_http.js b/lib/internal/inspector/network_http.js index 1bb08762bdb418..b5305475b80895 100644 --- a/lib/internal/inspector/network_http.js +++ b/lib/internal/inspector/network_http.js @@ -18,7 +18,7 @@ const { sniffMimeType, } = require('internal/inspector/network'); const { Network } = require('inspector'); -const EventEmitter = require('events'); +const { kOnBodyChunk } = require('_http_common'); const { kEmptyObject } = require('internal/util'); const kRequestUrl = Symbol('kRequestUrl'); @@ -136,8 +136,7 @@ function onClientResponseFinish({ request, response }) { }, }); - // Unlike response.on('data', ...), this does not put the stream into flowing mode. - EventEmitter.prototype.on.call(response, 'data', (chunk) => { + response[kOnBodyChunk] = (chunk) => { Network.dataReceived({ requestId: request[kInspectorRequestId], timestamp: getMonotonicTime(), @@ -145,10 +144,11 @@ function onClientResponseFinish({ request, response }) { encodedDataLength: chunk.byteLength, data: chunk, }); - }); + }; // Wait until the response body is consumed by user code. response.once('end', () => { + response[kOnBodyChunk] = undefined; Network.loadingFinished({ requestId: request[kInspectorRequestId], timestamp: getMonotonicTime(), diff --git a/test/parallel/test-inspector-network-http.js b/test/parallel/test-inspector-network-http.js index 88d717d83c896a..c7c2cabe15f26b 100644 --- a/test/parallel/test-inspector-network-http.js +++ b/test/parallel/test-inspector-network-http.js @@ -194,6 +194,23 @@ function verifyHttpResponse(response) { })); } +function verifyHttpResponseWithEncoding(response) { + assert.strictEqual(response.statusCode, 200); + const chunks = []; + + response.setEncoding('hex'); + response.on('data', (chunk) => { + chunks.push(chunk); + }); + + response.on('end', common.mustCall(() => { + assert.strictEqual( + chunks.join(''), + Buffer.from('\nhello world\n').toString('hex'), + ); + })); +} + function drainHttpResponse(response) { response.resume(); } @@ -342,6 +359,27 @@ async function testHttpsGet() { await assertResponseBody(responseReceived, '\nhello world\n'); } +async function testHttpResponseBodyWithEncoding() { + const url = `http://127.0.0.1:${httpServer.address().port}/hello-world`; + const { + requestWillBeSentFuture, + responseReceivedFuture, + loadingFinishedFuture, + } = createRequestTracker(url, getDefaultResponseExpect(url)); + + http.get({ + host: '127.0.0.1', + port: httpServer.address().port, + path: '/hello-world', + headers: requestHeaders, + }, common.mustCall(verifyHttpResponseWithEncoding)); + + await requestWillBeSentFuture; + const responseReceived = await responseReceivedFuture; + await loadingFinishedFuture; + await assertResponseBody(responseReceived, '\nhello world\n'); +} + async function testHttpError() { const url = `http://${addresses.INVALID_HOST}/`; const requestWillBeSentFuture = once(session, 'Network.requestWillBeSent') @@ -389,6 +427,8 @@ const testNetworkInspection = async () => { session.removeAllListeners(); await testHttpsGet(); session.removeAllListeners(); + await testHttpResponseBodyWithEncoding(); + session.removeAllListeners(); await testHttpError(); session.removeAllListeners(); await testHttpsError();