/
githubmirror
/
node
Обзор
Документация
Войти
/
githubmirror
/
node
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
lib/internal/inspector/network_http2.js
262 строки
8 KB
Antoine du Hamel
lib: add lint rule to enforce use of `kEmptyObject`
10 июн 2026, 13:56
Не верифицирован
10 июн 2026, 13:56
1778eeb
Код
Авторство
О чём код?
'use strict'; const { ArrayIsArray, DateNow, ObjectEntries, String, Symbol, } = primordials; const { kInspectorRequestId, kResourceType, getMonotonicTime, getNextRequestId, registerDiagnosticChannels, sniffMimeType, } = require('internal/inspector/network'); const { Network } = require('inspector'); const { HTTP2_HEADER_AUTHORITY, HTTP2_HEADER_CONTENT_TYPE, HTTP2_HEADER_COOKIE, HTTP2_HEADER_METHOD, HTTP2_HEADER_PATH, HTTP2_HEADER_SCHEME, HTTP2_HEADER_SET_COOKIE, HTTP2_HEADER_STATUS, NGHTTP2_NO_ERROR, } = internalBinding('http2').constants; const EventEmitter = require('events'); const { Buffer } = require('buffer'); const { kEmptyObject } = require('internal/util'); const kRequestUrl = Symbol('kRequestUrl'); // Convert a Headers object (Map<string, number | string | string[]>) to a plain object (Map<string, string>) function convertHeaderObject(headers = kEmptyObject) { let scheme; let authority; let path; let method; let statusCode; let charset; let mimeType; const dict = {}; for (const { 0: key, 1: value } of ObjectEntries(headers)) { const lowerCasedKey = key.toLowerCase(); if (lowerCasedKey === HTTP2_HEADER_SCHEME) { scheme = value; } else if (lowerCasedKey === HTTP2_HEADER_AUTHORITY) { authority = value; } else if (lowerCasedKey === HTTP2_HEADER_PATH) { path = value; } else if (lowerCasedKey === HTTP2_HEADER_METHOD) { method = value; } else if (lowerCasedKey === HTTP2_HEADER_STATUS) { statusCode = value; } else if (lowerCasedKey === HTTP2_HEADER_CONTENT_TYPE) { const result = sniffMimeType(value); charset = result.charset; mimeType = result.mimeType; } if (typeof value === 'string') { dict[key] = value; } else if (ArrayIsArray(value)) { if (lowerCasedKey === HTTP2_HEADER_COOKIE) dict[key] = value.join('; '); // ChromeDevTools frontend treats 'set-cookie' as a special case // https://github.com/ChromeDevTools/devtools-frontend/blob/4275917f84266ef40613db3c1784a25f902ea74e/front_end/core/sdk/NetworkRequest.ts#L1368 else if (lowerCasedKey === HTTP2_HEADER_SET_COOKIE) dict[key] = value.join('\n'); else dict[key] = value.join(', '); } else { dict[key] = String(value); } } const url = `${scheme}://${authority}${path}`; return [dict, url, method, statusCode, charset, mimeType]; } /** * When a client stream is created, emit Network.requestWillBeSent event. * https://chromedevtools.github.io/devtools-protocol/1-3/Network/#event-requestWillBeSent * @param {{ stream: import('http2').ClientHttp2Stream, headers: object }} event */ function onClientStreamCreated({ stream, headers }) { stream[kInspectorRequestId] = getNextRequestId(); const { 0: convertedHeaderObject, 1: url, 2: method, 4: charset } = convertHeaderObject(headers); stream[kRequestUrl] = url; Network.requestWillBeSent({ requestId: stream[kInspectorRequestId], timestamp: getMonotonicTime(), wallTime: DateNow(), charset, request: { url, method, headers: convertedHeaderObject, hasPostData: !stream.writableEnded, }, }); } /** * When a client stream errors, emit Network.loadingFailed event. * https://chromedevtools.github.io/devtools-protocol/1-3/Network/#event-loadingFailed * @param {{ stream: import('http2').ClientHttp2Stream, error: any }} event */ function onClientStreamError({ stream, error }) { if (typeof stream[kInspectorRequestId] !== 'string') { return; } Network.loadingFailed({ requestId: stream[kInspectorRequestId], timestamp: getMonotonicTime(), type: kResourceType.Other, errorText: error.message, }); } /** * When a chunk of the request body is being sent, cache it until `getRequestPostData` request. * https://chromedevtools.github.io/devtools-protocol/1-3/Network/#method-getRequestPostData * @param {{ * stream: import('http2').ClientHttp2Stream, * writev: boolean, * data: Buffer | string | Array<Buffer | {chunk: Buffer|string, encoding: string}>, * encoding: string, * }} event */ function onClientStreamBodyChunkSent({ stream, writev, data, encoding }) { if (typeof stream[kInspectorRequestId] !== 'string') { return; } let chunk; if (writev) { if (data.allBuffers) { chunk = Buffer.concat(data); } else { const buffers = []; for (let i = 0; i < data.length; ++i) { if (typeof data[i].chunk === 'string') { buffers.push(Buffer.from(data[i].chunk, data[i].encoding)); } else { buffers.push(data[i].chunk); } } chunk = Buffer.concat(buffers); } } else if (typeof data === 'string') { chunk = Buffer.from(data, encoding); } else { chunk = data; } Network.dataSent({ requestId: stream[kInspectorRequestId], timestamp: getMonotonicTime(), dataLength: chunk.byteLength, data: chunk, }); } /** * Mark a request body as fully sent. * @param {{ stream: import('http2').ClientHttp2Stream }} event */ function onClientStreamBodySent({ stream }) { if (typeof stream[kInspectorRequestId] !== 'string') { return; } Network.dataSent({ requestId: stream[kInspectorRequestId], finished: true, }); } /** * When response headers are received, emit Network.responseReceived event. * https://chromedevtools.github.io/devtools-protocol/1-3/Network/#event-responseReceived * @param {{ stream: import('http2').ClientHttp2Stream, headers: object }} event */ function onClientStreamFinish({ stream, headers }) { if (typeof stream[kInspectorRequestId] !== 'string') { return; } const { 0: convertedHeaderObject, 3: statusCode, 4: charset, 5: mimeType } = convertHeaderObject(headers); Network.responseReceived({ requestId: stream[kInspectorRequestId], timestamp: getMonotonicTime(), type: kResourceType.Other, response: { url: stream[kRequestUrl], status: statusCode, statusText: '', headers: convertedHeaderObject, mimeType, charset, }, }); // Unlike stream.on('data', ...), this does not put the stream into flowing mode. EventEmitter.prototype.on.call(stream, 'data', (chunk) => { /** * When a chunk of the response body has been received, cache it until `getResponseBody` request * https://chromedevtools.github.io/devtools-protocol/1-3/Network/#method-getResponseBody or * stream it with `streamResourceContent` request. * https://chromedevtools.github.io/devtools-protocol/tot/Network/#method-streamResourceContent */ Network.dataReceived({ requestId: stream[kInspectorRequestId], timestamp: getMonotonicTime(), dataLength: chunk.byteLength, encodedDataLength: chunk.byteLength, data: chunk, }); }); } /** * When user code completes consuming the response body, emit Network.loadingFinished event. * https://chromedevtools.github.io/devtools-protocol/1-3/Network/#event-loadingFinished * @param {{ stream: import('http2').ClientHttp2Stream }} event */ function onClientStreamClose({ stream }) { if (typeof stream[kInspectorRequestId] !== 'string') { return; } if (stream.rstCode !== NGHTTP2_NO_ERROR) { // This is an error case, so only Network.loadingFailed should be emitted // which is already done by onClientStreamError(). return; } Network.loadingFinished({ requestId: stream[kInspectorRequestId], timestamp: getMonotonicTime(), }); } module.exports = registerDiagnosticChannels([ ['http2.client.stream.created', onClientStreamCreated], ['http2.client.stream.error', onClientStreamError], ['http2.client.stream.finish', onClientStreamFinish], ['http2.client.stream.close', onClientStreamClose], ['http2.client.stream.bodyChunkSent', onClientStreamBodyChunkSent], ['http2.client.stream.bodySent', onClientStreamBodySent], ]);