| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent ad376c3 commit d2087ba
2 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -28,6 +28,8 @@ const { | |||
| 28 | 28 | HTTP2_HEADER_STATUS, | |
| 29 | 29 | NGHTTP2_NO_ERROR, | |
| 30 | 30 | } = internalBinding('http2').constants; | |
| 31 | + const EventEmitter = require('events'); | ||
| 32 | + const { Buffer } = require('buffer'); | ||
| 31 | 33 | ||
| 32 | 34 | const kRequestUrl = Symbol('kRequestUrl'); | |
| 33 | 35 | ||
@@ -99,6 +101,7 @@ function onClientStreamCreated({ stream, headers }) { | |||
| 99 | 101 | url, | |
| 100 | 102 | method, | |
| 101 | 103 | headers: convertedHeaderObject, | |
| 104 | + hasPostData: !stream.writableEnded, | ||
| 102 | 105 | }, | |
| 103 | 106 | }); | |
| 104 | 107 | } | |
@@ -121,6 +124,66 @@ function onClientStreamError({ stream, error }) { | |||
| 121 | 124 | }); | |
| 122 | 125 | } | |
| 123 | 126 | ||
| 127 | + /** | ||
| 128 | + * When a chunk of the request body is being sent, cache it until `getRequestPostData` request. | ||
| 129 | + * https://chromedevtools.github.io/devtools-protocol/1-3/Network/#method-getRequestPostData | ||
| 130 | + * @param {{ | ||
| 131 | + * stream: import('http2').ClientHttp2Stream, | ||
| 132 | + * writev: boolean, | ||
| 133 | + * data: Buffer | string | Array<Buffer | {chunk: Buffer|string, encoding: string}>, | ||
| 134 | + * encoding: string, | ||
| 135 | + * }} event | ||
| 136 | + */ | ||
| 137 | + function onClientStreamBodyChunkSent({ stream, writev, data, encoding }) { | ||
| 138 | + if (typeof stream[kInspectorRequestId] !== 'string') { | ||
| 139 | + return; | ||
| 140 | + } | ||
| 141 | + | ||
| 142 | + let chunk; | ||
| 143 | + | ||
| 144 | + if (writev) { | ||
| 145 | + if (data.allBuffers) { | ||
| 146 | + chunk = Buffer.concat(data); | ||
| 147 | + } else { | ||
| 148 | + const buffers = []; | ||
| 149 | + for (let i = 0; i < data.length; ++i) { | ||
| 150 | + if (typeof data[i].chunk === 'string') { | ||
| 151 | + buffers.push(Buffer.from(data[i].chunk, data[i].encoding)); | ||
| 152 | + } else { | ||
| 153 | + buffers.push(data[i].chunk); | ||
| 154 | + } | ||
| 155 | + } | ||
| 156 | + chunk = Buffer.concat(buffers); | ||
| 157 | + } | ||
| 158 | + } else if (typeof data === 'string') { | ||
| 159 | + chunk = Buffer.from(data, encoding); | ||
| 160 | + } else { | ||
| 161 | + chunk = data; | ||
| 162 | + } | ||
| 163 | + | ||
| 164 | + Network.dataSent({ | ||
| 165 | + requestId: stream[kInspectorRequestId], | ||
| 166 | + timestamp: getMonotonicTime(), | ||
| 167 | + dataLength: chunk.byteLength, | ||
| 168 | + data: chunk, | ||
| 169 | + }); | ||
| 170 | + } | ||
| 171 | + | ||
| 172 | + /** | ||
| 173 | + * Mark a request body as fully sent. | ||
| 174 | + * @param {{ stream: import('http2').ClientHttp2Stream }} event | ||
| 175 | + */ | ||
| 176 | + function onClientStreamBodySent({ stream }) { | ||
| 177 | + if (typeof stream[kInspectorRequestId] !== 'string') { | ||
| 178 | + return; | ||
| 179 | + } | ||
| 180 | + | ||
| 181 | + Network.dataSent({ | ||
| 182 | + requestId: stream[kInspectorRequestId], | ||
| 183 | + finished: true, | ||
| 184 | + }); | ||
| 185 | + } | ||
| 186 | + | ||
| 124 | 187 | /** | |
| 125 | 188 | * When response headers are received, emit Network.responseReceived event. | |
| 126 | 189 | * https://chromedevtools.github.io/devtools-protocol/1-3/Network/#event-responseReceived | |
@@ -146,6 +209,24 @@ function onClientStreamFinish({ stream, headers }) { | |||
| 146 | 209 | charset, | |
| 147 | 210 | }, | |
| 148 | 211 | }); | |
| 212 | + | ||
| 213 | + // Unlike stream.on('data', ...), this does not put the stream into flowing mode. | ||
| 214 | + EventEmitter.prototype.on.call(stream, 'data', (chunk) => { | ||
| 215 | + /** | ||
| 216 | + * When a chunk of the response body has been received, cache it until `getResponseBody` request | ||
| 217 | + * https://chromedevtools.github.io/devtools-protocol/1-3/Network/#method-getResponseBody or | ||
| 218 | + * stream it with `streamResourceContent` request. | ||
| 219 | + * https://chromedevtools.github.io/devtools-protocol/tot/Network/#method-streamResourceContent | ||
| 220 | + */ | ||
| 221 | + | ||
| 222 | + Network.dataReceived({ | ||
| 223 | + requestId: stream[kInspectorRequestId], | ||
| 224 | + timestamp: getMonotonicTime(), | ||
| 225 | + dataLength: chunk.byteLength, | ||
| 226 | + encodedDataLength: chunk.byteLength, | ||
| 227 | + data: chunk, | ||
| 228 | + }); | ||
| 229 | + }); | ||
| 149 | 230 | } | |
| 150 | 231 | ||
| 151 | 232 | /** | |
@@ -175,4 +256,6 @@ module.exports = registerDiagnosticChannels([ | |||
| 175 | 256 | ['http2.client.stream.error', onClientStreamError], | |
| 176 | 257 | ['http2.client.stream.finish', onClientStreamFinish], | |
| 177 | 258 | ['http2.client.stream.close', onClientStreamClose], | |
| 259 | + ['http2.client.stream.bodyChunkSent', onClientStreamBodyChunkSent], | ||
| 260 | + ['http2.client.stream.bodySent', onClientStreamBodySent], | ||
| 178 | 261 | ]); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -14,12 +14,15 @@ const inspector = require('node:inspector/promises'); | |||
| 14 | 14 | const session = new inspector.Session(); | |
| 15 | 15 | session.connect(); | |
| 16 | 16 | ||
| 17 | + const requestBody = { 'hello': 'world' }; | ||
| 18 | + | ||
| 17 | 19 | const requestHeaders = { | |
| 18 | 20 | 'x-header1': ['value1', 'value2'], | |
| 19 | 21 | [http2.constants.HTTP2_HEADER_ACCEPT_LANGUAGE]: 'en-US', | |
| 20 | 22 | [http2.constants.HTTP2_HEADER_AGE]: 1000, | |
| 23 | + [http2.constants.HTTP2_HEADER_CONTENT_TYPE]: 'application/json; charset=utf-8', | ||
| 21 | 24 | [http2.constants.HTTP2_HEADER_COOKIE]: ['k1=v1', 'k2=v2'], | |
| 22 | - [http2.constants.HTTP2_HEADER_METHOD]: 'GET', | ||
| 25 | + [http2.constants.HTTP2_HEADER_METHOD]: 'POST', | ||
| 23 | 26 | [http2.constants.HTTP2_HEADER_PATH]: '/hello-world', | |
| 24 | 27 | }; | |
| 25 | 28 | ||
@@ -54,23 +57,35 @@ const pushResponseHeaders = { | |||
| 54 | 57 | [http2.constants.HTTP2_HEADER_STATUS]: 200, | |
| 55 | 58 | }; | |
| 56 | 59 | ||
| 60 | + const styleCss = 'body { color: red; }\n'; | ||
| 61 | + const serverResponse = 'hello world\n'; | ||
| 62 | + | ||
| 57 | 63 | const kTimeout = 1000; | |
| 58 | 64 | const kDelta = 200; | |
| 59 | 65 | ||
| 60 | 66 | const handleStream = (stream, headers) => { | |
| 61 | 67 | const path = headers[http2.constants.HTTP2_HEADER_PATH]; | |
| 68 | + let body = ''; | ||
| 62 | 69 | switch (path) { | |
| 63 | 70 | case '/hello-world': | |
| 64 | - stream.pushStream(pushRequestHeaders, common.mustSucceed((pushStream) => { | ||
| 65 | - pushStream.respond(pushResponseHeaders); | ||
| 66 | - pushStream.end('body { color: red; }\n'); | ||
| 67 | - })); | ||
| 71 | + stream.on('data', (chunk) => { | ||
| 72 | + body += chunk; | ||
| 73 | + }); | ||
| 68 | 74 | ||
| 69 | - stream.respond(responseHeaders); | ||
| 75 | + stream.on('end', () => { | ||
| 76 | + assert.strictEqual(body, JSON.stringify(requestBody)); | ||
| 70 | 77 | ||
| 71 | - setTimeout(() => { | ||
| 72 | - stream.end('hello world\n'); | ||
| 73 | - }, kTimeout); | ||
| 78 | + stream.pushStream(pushRequestHeaders, common.mustSucceed((pushStream) => { | ||
| 79 | + pushStream.respond(pushResponseHeaders); | ||
| 80 | + pushStream.end(styleCss); | ||
| 81 | + })); | ||
| 82 | + | ||
| 83 | + stream.respond(responseHeaders); | ||
| 84 | + | ||
| 85 | + setTimeout(() => { | ||
| 86 | + stream.end(serverResponse); | ||
| 87 | + }, kTimeout); | ||
| 88 | + }); | ||
| 74 | 89 | break; | |
| 75 | 90 | case '/trigger-error': | |
| 76 | 91 | stream.close(http2.constants.NGHTTP2_STREAM_CLOSED); | |
@@ -114,7 +129,6 @@ function verifyRequestWillBeSent({ method, params }, expectedUrl) { | |||
| 114 | 129 | ||
| 115 | 130 | assert.ok(params.requestId.startsWith('node-network-event-')); | |
| 116 | 131 | assert.strictEqual(params.request.url, expectedUrl); | |
| 117 | - assert.strictEqual(params.request.method, 'GET'); | ||
| 118 | 132 | assert.strictEqual(typeof params.request.headers, 'object'); | |
| 119 | 133 | ||
| 120 | 134 | if (expectedUrl.endsWith('/hello-world')) { | |
@@ -123,10 +137,17 @@ function verifyRequestWillBeSent({ method, params }, expectedUrl) { | |||
| 123 | 137 | assert.strictEqual(params.request.headers.age, '1000'); | |
| 124 | 138 | assert.strictEqual(params.request.headers['x-header1'], 'value1, value2'); | |
| 125 | 139 | assert.ok(findFrameInInitiator(__filename, params.initiator)); | |
| 140 | + assert.strictEqual(params.request.hasPostData, true); | ||
| 141 | + assert.strictEqual(params.request.method, 'POST'); | ||
| 126 | 142 | } else if (expectedUrl.endsWith('/style.css')) { | |
| 127 | 143 | assert.strictEqual(params.request.headers['x-header3'], 'value1, value2'); | |
| 128 | 144 | assert.strictEqual(params.request.headers['x-push'], 'true'); | |
| 129 | 145 | assert.ok(!findFrameInInitiator(__filename, params.initiator)); | |
| 146 | + assert.strictEqual(params.request.hasPostData, true); | ||
| 147 | + assert.strictEqual(params.request.method, 'GET'); | ||
| 148 | + } else { | ||
| 149 | + assert.strictEqual(params.request.hasPostData, false); | ||
| 150 | + assert.strictEqual(params.request.method, 'GET'); | ||
| 130 | 151 | } | |
| 131 | 152 | ||
| 132 | 153 | assert.strictEqual(typeof params.timestamp, 'number'); | |
@@ -198,6 +219,8 @@ async function testHttp2(secure = false) { | |||
| 198 | 219 | rejectUnauthorized: false, | |
| 199 | 220 | }); | |
| 200 | 221 | const request = client.request(requestHeaders); | |
| 222 | + request.write(JSON.stringify(requestBody)); | ||
| 223 | + request.end(); | ||
| 201 | 224 | ||
| 202 | 225 | // Dump the responses. | |
| 203 | 226 | request.on('data', () => {}); | |
@@ -216,6 +239,11 @@ async function testHttp2(secure = false) { | |||
| 216 | 239 | verifyRequestWillBeSent(mainRequest, url); | |
| 217 | 240 | verifyRequestWillBeSent(pushRequest, pushedUrl); | |
| 218 | 241 | ||
| 242 | + const { postData } = await session.post('Network.getRequestPostData', { | ||
| 243 | + requestId: mainRequest.params.requestId | ||
| 244 | + }); | ||
| 245 | + assert.strictEqual(postData, JSON.stringify(requestBody)); | ||
| 246 | + | ||
| 219 | 247 | const [ | |
| 220 | 248 | { value: [ mainResponse ] }, | |
| 221 | 249 | { value: [ pushResponse ] }, | |
@@ -230,6 +258,18 @@ async function testHttp2(secure = false) { | |||
| 230 | 258 | verifyLoadingFinished(event1); | |
| 231 | 259 | verifyLoadingFinished(event2); | |
| 232 | 260 | ||
| 261 | + const responseBody = await session.post('Network.getResponseBody', { | ||
| 262 | + requestId: mainRequest.params.requestId, | ||
| 263 | + }); | ||
| 264 | + assert.strictEqual(responseBody.base64Encoded, false); | ||
| 265 | + assert.strictEqual(responseBody.body, serverResponse); | ||
| 266 | + | ||
| 267 | + const pushResponseBody = await session.post('Network.getResponseBody', { | ||
| 268 | + requestId: pushRequest.params.requestId, | ||
| 269 | + }); | ||
| 270 | + assert.strictEqual(pushResponseBody.base64Encoded, true); | ||
| 271 | + assert.strictEqual(Buffer.from(pushResponseBody.body, 'base64').toString(), styleCss); | ||
| 272 | + | ||
| 233 | 273 | const mainFinished = [event1, event2] | |
| 234 | 274 | .find((event) => event.params.requestId === mainResponse.params.requestId); | |
| 235 | 275 | const pushFinished = [event1, event2] | |
| Back | FazBrowse Home | New Git URL |
0 commit comments