| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,7 +1,10 @@ | |||
| 1 | 1 | 'use strict'; | |
| 2 | 2 | ||
| 3 | 3 | const { | |
| 4 | + ArrayIsArray, | ||
| 4 | 5 | DateNow, | |
| 6 | + ObjectEntries, | ||
| 7 | + String, | ||
| 5 | 8 | } = primordials; | |
| 6 | 9 | ||
| 7 | 10 | let dc; | |
@@ -10,6 +13,25 @@ let Network; | |||
| 10 | 13 | let requestId = 0; | |
| 11 | 14 | const getNextRequestId = () => `node-network-event-${++requestId}`; | |
| 12 | 15 | ||
| 16 | + // Convert a Headers object (Map<string, number | string | string[]>) to a plain object (Map<string, string>) | ||
| 17 | + const headerObjectToDictionary = (headers = {}) => { | ||
| 18 | + const dict = {}; | ||
| 19 | + for (const { 0: key, 1: value } of ObjectEntries(headers)) { | ||
| 20 | + if (typeof value === 'string') { | ||
| 21 | + dict[key] = value; | ||
| 22 | + } else if (ArrayIsArray(value)) { | ||
| 23 | + if (key.toLowerCase() === 'cookie') dict[key] = value.join('; '); | ||
| 24 | + // ChromeDevTools frontend treats 'set-cookie' as a special case | ||
| 25 | + // https://github.com/ChromeDevTools/devtools-frontend/blob/4275917f84266ef40613db3c1784a25f902ea74e/front_end/core/sdk/NetworkRequest.ts#L1368 | ||
| 26 | + else if (key.toLowerCase() === 'set-cookie') dict[key] = value.join('\n'); | ||
| 27 | + else dict[key] = value.join(', '); | ||
| 28 | + } else { | ||
| 29 | + dict[key] = String(value); | ||
| 30 | + } | ||
| 31 | + } | ||
| 32 | + return dict; | ||
| 33 | + }; | ||
| 34 | + | ||
| 13 | 35 | function onClientRequestStart({ request }) { | |
| 14 | 36 | const url = `${request.protocol}//${request.host}${request.path}`; | |
| 15 | 37 | const wallTime = DateNow(); | |
@@ -22,18 +44,27 @@ function onClientRequestStart({ request }) { | |||
| 22 | 44 | request: { | |
| 23 | 45 | url, | |
| 24 | 46 | method: request.method, | |
| 47 | + headers: headerObjectToDictionary(request.getHeaders()), | ||
| 25 | 48 | }, | |
| 26 | 49 | }); | |
| 27 | 50 | } | |
| 28 | 51 | ||
| 29 | - function onClientResponseFinish({ request }) { | ||
| 52 | + function onClientResponseFinish({ request, response }) { | ||
| 30 | 53 | if (typeof request._inspectorRequestId !== 'string') { | |
| 31 | 54 | return; | |
| 32 | 55 | } | |
| 56 | + const url = `${request.protocol}//${request.host}${request.path}`; | ||
| 33 | 57 | const timestamp = DateNow() / 1000; | |
| 34 | 58 | Network.responseReceived({ | |
| 35 | 59 | requestId: request._inspectorRequestId, | |
| 36 | 60 | timestamp, | |
| 61 | + type: 'Other', | ||
| 62 | + response: { | ||
| 63 | + url, | ||
| 64 | + status: response.statusCode, | ||
| 65 | + statusText: response.statusMessage ?? '', | ||
| 66 | + headers: headerObjectToDictionary(response.headers), | ||
| 67 | + }, | ||
| 37 | 68 | }); | |
| 38 | 69 | Network.loadingFinished({ | |
| 39 | 70 | requestId: request._inspectorRequestId, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -5,9 +5,28 @@ namespace node { | |||
| 5 | 5 | namespace inspector { | |
| 6 | 6 | namespace protocol { | |
| 7 | 7 | ||
| 8 | - std::unique_ptr<Network::Request> Request(const String& url, | ||
| 9 | - const String& method) { | ||
| 10 | - return Network::Request::create().setUrl(url).setMethod(method).build(); | ||
| 8 | + std::unique_ptr<Network::Request> createRequest( | ||
| 9 | + const String& url, | ||
| 10 | + const String& method, | ||
| 11 | + std::unique_ptr<Network::Headers> headers) { | ||
| 12 | + return Network::Request::create() | ||
| 13 | + .setUrl(url) | ||
| 14 | + .setMethod(method) | ||
| 15 | + .setHeaders(std::move(headers)) | ||
| 16 | + .build(); | ||
| 17 | + } | ||
| 18 | + | ||
| 19 | + std::unique_ptr<Network::Response> createResponse( | ||
| 20 | + const String& url, | ||
| 21 | + int status, | ||
| 22 | + const String& statusText, | ||
| 23 | + std::unique_ptr<Network::Headers> headers) { | ||
| 24 | + return Network::Response::create() | ||
| 25 | + .setUrl(url) | ||
| 26 | + .setStatus(status) | ||
| 27 | + .setStatusText(statusText) | ||
| 28 | + .setHeaders(std::move(headers)) | ||
| 29 | + .build(); | ||
| 11 | 30 | } | |
| 12 | 31 | ||
| 13 | 32 | NetworkAgent::NetworkAgent(NetworkInspector* inspector) | |
@@ -55,8 +74,17 @@ void NetworkAgent::requestWillBeSent( | |||
| 55 | 74 | String method; | |
| 56 | 75 | request->getString("method", &method); | |
| 57 | 76 | ||
| 58 | - frontend_->requestWillBeSent( | ||
| 59 | - request_id, Request(url, method), timestamp, wall_time); | ||
| 77 | + ErrorSupport errors; | ||
| 78 | + auto headers = | ||
| 79 | + Network::Headers::fromValue(request->getObject("headers"), &errors); | ||
| 80 | + if (errors.hasErrors()) { | ||
| 81 | + headers = std::make_unique<Network::Headers>(DictionaryValue::create()); | ||
| 82 | + } | ||
| 83 | + | ||
| 84 | + frontend_->requestWillBeSent(request_id, | ||
| 85 | + createRequest(url, method, std::move(headers)), | ||
| 86 | + timestamp, | ||
| 87 | + wall_time); | ||
| 60 | 88 | } | |
| 61 | 89 | ||
| 62 | 90 | void NetworkAgent::responseReceived( | |
@@ -65,8 +93,28 @@ void NetworkAgent::responseReceived( | |||
| 65 | 93 | params->getString("requestId", &request_id); | |
| 66 | 94 | double timestamp; | |
| 67 | 95 | params->getDouble("timestamp", ×tamp); | |
| 96 | + String type; | ||
| 97 | + params->getString("type", &type); | ||
| 98 | + auto response = params->getObject("response"); | ||
| 99 | + String url; | ||
| 100 | + response->getString("url", &url); | ||
| 101 | + int status; | ||
| 102 | + response->getInteger("status", &status); | ||
| 103 | + String statusText; | ||
| 104 | + response->getString("statusText", &statusText); | ||
| 105 | + | ||
| 106 | + ErrorSupport errors; | ||
| 107 | + auto headers = | ||
| 108 | + Network::Headers::fromValue(response->getObject("headers"), &errors); | ||
| 109 | + if (errors.hasErrors()) { | ||
| 110 | + headers = std::make_unique<Network::Headers>(DictionaryValue::create()); | ||
| 111 | + } | ||
| 68 | 112 | ||
| 69 | - frontend_->responseReceived(request_id, timestamp); | ||
| 113 | + frontend_->responseReceived( | ||
| 114 | + request_id, | ||
| 115 | + timestamp, | ||
| 116 | + type, | ||
| 117 | + createResponse(url, status, statusText, std::move(headers))); | ||
| 70 | 118 | } | |
| 71 | 119 | ||
| 72 | 120 | void NetworkAgent::loadingFinished( | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -12,9 +12,6 @@ class NetworkInspector; | |||
| 12 | 12 | ||
| 13 | 13 | namespace protocol { | |
| 14 | 14 | ||
| 15 | - std::unique_ptr<Network::Request> Request(const String& url, | ||
| 16 | - const String& method); | ||
| 17 | - | ||
| 18 | 15 | class NetworkAgent : public Network::Backend { | |
| 19 | 16 | public: | |
| 20 | 17 | explicit NetworkAgent(NetworkInspector* inspector); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -101,6 +101,28 @@ experimental domain NodeWorker | |||
| 101 | 101 | # Partial support for Network domain of ChromeDevTools Protocol. | |
| 102 | 102 | # https://chromedevtools.github.io/devtools-protocol/tot/Network | |
| 103 | 103 | experimental domain Network | |
| 104 | + # Resource type as it was perceived by the rendering engine. | ||
| 105 | + type ResourceType extends string | ||
| 106 | + enum | ||
| 107 | + Document | ||
| 108 | + Stylesheet | ||
| 109 | + Image | ||
| 110 | + Media | ||
| 111 | + Font | ||
| 112 | + Script | ||
| 113 | + TextTrack | ||
| 114 | + XHR | ||
| 115 | + Fetch | ||
| 116 | + Prefetch | ||
| 117 | + EventSource | ||
| 118 | + WebSocket | ||
| 119 | + Manifest | ||
| 120 | + SignedExchange | ||
| 121 | + Ping | ||
| 122 | + CSPViolationReport | ||
| 123 | + Preflight | ||
| 124 | + Other | ||
| 125 | + | ||
| 104 | 126 | # Unique request identifier. | |
| 105 | 127 | type RequestId extends string | |
| 106 | 128 | ||
@@ -115,6 +137,18 @@ experimental domain Network | |||
| 115 | 137 | properties | |
| 116 | 138 | string url | |
| 117 | 139 | string method | |
| 140 | + Headers headers | ||
| 141 | + | ||
| 142 | + # HTTP response data. | ||
| 143 | + type Response extends object | ||
| 144 | + properties | ||
| 145 | + string url | ||
| 146 | + integer status | ||
| 147 | + string statusText | ||
| 148 | + Headers headers | ||
| 149 | + | ||
| 150 | + # Request / response headers as keys / values of JSON object. | ||
| 151 | + type Headers extends object | ||
| 118 | 152 | ||
| 119 | 153 | # Disables network tracking, prevents network events from being sent to the client. | |
| 120 | 154 | command disable | |
@@ -141,6 +175,10 @@ experimental domain Network | |||
| 141 | 175 | RequestId requestId | |
| 142 | 176 | # Timestamp. | |
| 143 | 177 | MonotonicTime timestamp | |
| 178 | + # Resource type. | ||
| 179 | + ResourceType type | ||
| 180 | + # Response data. | ||
| 181 | + Response response | ||
| 144 | 182 | ||
| 145 | 183 | event loadingFinished | |
| 146 | 184 | parameters | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -15,7 +15,17 @@ const EXPECTED_EVENTS = { | |||
| 15 | 15 | requestId: 'request-id-1', | |
| 16 | 16 | request: { | |
| 17 | 17 | url: 'https://nodejs.org/en', | |
| 18 | - method: 'GET' | ||
| 18 | + method: 'GET', | ||
| 19 | + }, | ||
| 20 | + timestamp: 1000, | ||
| 21 | + wallTime: 1000, | ||
| 22 | + }, | ||
| 23 | + expected: { | ||
| 24 | + requestId: 'request-id-1', | ||
| 25 | + request: { | ||
| 26 | + url: 'https://nodejs.org/en', | ||
| 27 | + method: 'GET', | ||
| 28 | + headers: {} // Headers should be an empty object if not provided. | ||
| 19 | 29 | }, | |
| 20 | 30 | timestamp: 1000, | |
| 21 | 31 | wallTime: 1000, | |
@@ -26,6 +36,23 @@ const EXPECTED_EVENTS = { | |||
| 26 | 36 | params: { | |
| 27 | 37 | requestId: 'request-id-1', | |
| 28 | 38 | timestamp: 1000, | |
| 39 | + type: 'Other', | ||
| 40 | + response: { | ||
| 41 | + url: 'https://nodejs.org/en', | ||
| 42 | + status: 200, | ||
| 43 | + headers: { host: 'nodejs.org' } | ||
| 44 | + } | ||
| 45 | + }, | ||
| 46 | + expected: { | ||
| 47 | + requestId: 'request-id-1', | ||
| 48 | + timestamp: 1000, | ||
| 49 | + type: 'Other', | ||
| 50 | + response: { | ||
| 51 | + url: 'https://nodejs.org/en', | ||
| 52 | + status: 200, | ||
| 53 | + statusText: '', // Status text should be an empty string if not provided. | ||
| 54 | + headers: { host: 'nodejs.org' } | ||
| 55 | + } | ||
| 29 | 56 | } | |
| 30 | 57 | }, | |
| 31 | 58 | { | |
@@ -68,7 +95,7 @@ const runAsyncTest = async () => { | |||
| 68 | 95 | for (const [domain, events] of Object.entries(EXPECTED_EVENTS)) { | |
| 69 | 96 | for (const event of events) { | |
| 70 | 97 | session.on(`${domain}.${event.name}`, common.mustCall(({ params }) => { | |
| 71 | - assert.deepStrictEqual(params, event.params); | ||
| 98 | + assert.deepStrictEqual(params, event.expected ?? event.params); | ||
| 72 | 99 | })); | |
| 73 | 100 | inspector[domain][event.name](event.params); | |
| 74 | 101 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -13,10 +13,25 @@ const inspector = require('node:inspector/promises'); | |||
| 13 | 13 | const session = new inspector.Session(); | |
| 14 | 14 | session.connect(); | |
| 15 | 15 | ||
| 16 | + const requestHeaders = { | ||
| 17 | + 'accept-language': 'en-US', | ||
| 18 | + 'Cookie': ['k1=v1', 'k2=v2'], | ||
| 19 | + 'age': 1000, | ||
| 20 | + 'x-header1': ['value1', 'value2'] | ||
| 21 | + }; | ||
| 22 | + | ||
| 23 | + const setResponseHeaders = (res) => { | ||
| 24 | + res.setHeader('server', 'node'); | ||
| 25 | + res.setHeader('etag', 12345); | ||
| 26 | + res.setHeader('Set-Cookie', ['key1=value1', 'key2=value2']); | ||
| 27 | + res.setHeader('x-header2', ['value1', 'value2']); | ||
| 28 | + }; | ||
| 29 | + | ||
| 16 | 30 | const httpServer = http.createServer((req, res) => { | |
| 17 | 31 | const path = req.url; | |
| 18 | 32 | switch (path) { | |
| 19 | 33 | case '/hello-world': | |
| 34 | + setResponseHeaders(res); | ||
| 20 | 35 | res.writeHead(200); | |
| 21 | 36 | res.end('hello world\n'); | |
| 22 | 37 | break; | |
@@ -32,6 +47,7 @@ const httpsServer = https.createServer({ | |||
| 32 | 47 | const path = req.url; | |
| 33 | 48 | switch (path) { | |
| 34 | 49 | case '/hello-world': | |
| 50 | + setResponseHeaders(res); | ||
| 35 | 51 | res.writeHead(200); | |
| 36 | 52 | res.end('hello world\n'); | |
| 37 | 53 | break; | |
@@ -52,12 +68,26 @@ const testHttpGet = () => new Promise((resolve, reject) => { | |||
| 52 | 68 | assert.ok(params.requestId.startsWith('node-network-event-')); | |
| 53 | 69 | assert.strictEqual(params.request.url, 'http://127.0.0.1/hello-world'); | |
| 54 | 70 | assert.strictEqual(params.request.method, 'GET'); | |
| 71 | + assert.strictEqual(typeof params.request.headers, 'object'); | ||
| 72 | + assert.strictEqual(params.request.headers['accept-language'], 'en-US'); | ||
| 73 | + assert.strictEqual(params.request.headers.cookie, 'k1=v1; k2=v2'); | ||
| 74 | + assert.strictEqual(params.request.headers.age, '1000'); | ||
| 75 | + assert.strictEqual(params.request.headers['x-header1'], 'value1, value2'); | ||
| 55 | 76 | assert.strictEqual(typeof params.timestamp, 'number'); | |
| 56 | 77 | assert.strictEqual(typeof params.wallTime, 'number'); | |
| 57 | 78 | })); | |
| 58 | 79 | session.on('Network.responseReceived', common.mustCall(({ params }) => { | |
| 59 | 80 | assert.ok(params.requestId.startsWith('node-network-event-')); | |
| 60 | 81 | assert.strictEqual(typeof params.timestamp, 'number'); | |
| 82 | + assert.strictEqual(params.type, 'Other'); | ||
| 83 | + assert.strictEqual(params.response.status, 200); | ||
| 84 | + assert.strictEqual(params.response.statusText, 'OK'); | ||
| 85 | + assert.strictEqual(params.response.url, 'http://127.0.0.1/hello-world'); | ||
| 86 | + assert.strictEqual(typeof params.response.headers, 'object'); | ||
| 87 | + assert.strictEqual(params.response.headers.server, 'node'); | ||
| 88 | + assert.strictEqual(params.response.headers.etag, '12345'); | ||
| 89 | + assert.strictEqual(params.response.headers['set-cookie'], 'key1=value1\nkey2=value2'); | ||
| 90 | + assert.strictEqual(params.response.headers['x-header2'], 'value1, value2'); | ||
| 61 | 91 | })); | |
| 62 | 92 | session.on('Network.loadingFinished', common.mustCall(({ params }) => { | |
| 63 | 93 | assert.ok(params.requestId.startsWith('node-network-event-')); | |
@@ -69,6 +99,7 @@ const testHttpGet = () => new Promise((resolve, reject) => { | |||
| 69 | 99 | host: '127.0.0.1', | |
| 70 | 100 | port: httpServer.address().port, | |
| 71 | 101 | path: '/hello-world', | |
| 102 | + headers: requestHeaders | ||
| 72 | 103 | }, common.mustCall()); | |
| 73 | 104 | }); | |
| 74 | 105 | ||
@@ -77,12 +108,26 @@ const testHttpsGet = () => new Promise((resolve, reject) => { | |||
| 77 | 108 | assert.ok(params.requestId.startsWith('node-network-event-')); | |
| 78 | 109 | assert.strictEqual(params.request.url, 'https://127.0.0.1/hello-world'); | |
| 79 | 110 | assert.strictEqual(params.request.method, 'GET'); | |
| 111 | + assert.strictEqual(typeof params.request.headers, 'object'); | ||
| 112 | + assert.strictEqual(params.request.headers['accept-language'], 'en-US'); | ||
| 113 | + assert.strictEqual(params.request.headers.cookie, 'k1=v1; k2=v2'); | ||
| 114 | + assert.strictEqual(params.request.headers.age, '1000'); | ||
| 115 | + assert.strictEqual(params.request.headers['x-header1'], 'value1, value2'); | ||
| 80 | 116 | assert.strictEqual(typeof params.timestamp, 'number'); | |
| 81 | 117 | assert.strictEqual(typeof params.wallTime, 'number'); | |
| 82 | 118 | })); | |
| 83 | 119 | session.on('Network.responseReceived', common.mustCall(({ params }) => { | |
| 84 | 120 | assert.ok(params.requestId.startsWith('node-network-event-')); | |
| 85 | 121 | assert.strictEqual(typeof params.timestamp, 'number'); | |
| 122 | + assert.strictEqual(params.type, 'Other'); | ||
| 123 | + assert.strictEqual(params.response.status, 200); | ||
| 124 | + assert.strictEqual(params.response.statusText, 'OK'); | ||
| 125 | + assert.strictEqual(params.response.url, 'https://127.0.0.1/hello-world'); | ||
| 126 | + assert.strictEqual(typeof params.response.headers, 'object'); | ||
| 127 | + assert.strictEqual(params.response.headers.server, 'node'); | ||
| 128 | + assert.strictEqual(params.response.headers.etag, '12345'); | ||
| 129 | + assert.strictEqual(params.response.headers['set-cookie'], 'key1=value1\nkey2=value2'); | ||
| 130 | + assert.strictEqual(params.response.headers['x-header2'], 'value1, value2'); | ||
| 86 | 131 | })); | |
| 87 | 132 | session.on('Network.loadingFinished', common.mustCall(({ params }) => { | |
| 88 | 133 | assert.ok(params.requestId.startsWith('node-network-event-')); | |
@@ -95,6 +140,7 @@ const testHttpsGet = () => new Promise((resolve, reject) => { | |||
| 95 | 140 | port: httpsServer.address().port, | |
| 96 | 141 | path: '/hello-world', | |
| 97 | 142 | rejectUnauthorized: false, | |
| 143 | + headers: requestHeaders, | ||
| 98 | 144 | }, common.mustCall()); | |
| 99 | 145 | }); | |
| 100 | 146 | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments