Skip to content

Commit 197ba2c

Browse files
committed
http: optimize IncomingMessage._dump
1 parent 2ea31e5 commit 197ba2c

File tree

5 files changed

+42
-4
lines changed

5 files changed

+42
-4
lines changed

doc/api/http.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3537,6 +3537,9 @@ Found'`.
35373537
<!-- YAML
35383538
added: v0.1.13
35393539
changes:
3540+
- version: REPLACEME
3541+
pr-url: https://github.com/nodejs/node/pull/59747
3542+
description: Add dumpGET option.
35403543
- version:
35413544
- v20.1.0
35423545
- v18.17.0
@@ -3632,6 +3635,10 @@ changes:
36323635
* `rejectNonStandardBodyWrites` {boolean} If set to `true`, an error is thrown
36333636
when writing to an HTTP response which does not have a body.
36343637
**Default:** `false`.
3638+
* `dumpGETBody` {boolean} If set to `true`, request body for `HEAD` and `GET`
3639+
requests will be immediatly dumped and the `end` and `close` events for
3640+
`IncomingMessage` will be emitted before the server emits `'request'`. This
3641+
enables some optimizations that would otherwise not be possible.
36353642
36363643
* `requestListener` {Function}
36373644

lib/_http_server.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
const {
2525
ArrayIsArray,
26+
Boolean,
2627
Error,
2728
MathMin,
2829
NumberIsFinite,
@@ -32,6 +33,7 @@ const {
3233
Symbol,
3334
SymbolAsyncDispose,
3435
SymbolFor,
36+
globalThis,
3537
} = primordials;
3638

3739
const net = require('net');
@@ -105,6 +107,7 @@ const onResponseFinishChannel = dc.channel('http.server.response.finish');
105107

106108
const kServerResponse = Symbol('ServerResponse');
107109
const kServerResponseStatistics = Symbol('ServerResponseStatistics');
110+
const kDumpGet = Symbol('kDumpGet');
108111

109112
const {
110113
hasObserver,
@@ -447,6 +450,8 @@ function storeHTTPOptions(options) {
447450
this[kIncomingMessage] = options.IncomingMessage || IncomingMessage;
448451
this[kServerResponse] = options.ServerResponse || ServerResponse;
449452

453+
this[kDumpGet] = Boolean(options.dumpGETBody ?? globalThis.IS_NODE_TEST);
454+
450455
const maxHeaderSize = options.maxHeaderSize;
451456
if (maxHeaderSize !== undefined)
452457
validateInteger(maxHeaderSize, 'maxHeaderSize', 0);
@@ -1102,6 +1107,24 @@ function parserOnIncoming(server, socket, state, req, keepAlive) {
11021107
});
11031108
}
11041109

1110+
if (req[kDumpGet]) {
1111+
if (req.method === 'HEAD' || req.method === 'GET') {
1112+
// Fast dump where request "has" already emitted all lifecycle events.
1113+
// This avoid a lot of unnecessary overhead otherwise introduced by
1114+
// stream.Readable life cycle rules. The downside is that this will
1115+
// break some servers that read GET bodies.
1116+
1117+
req._dumped = true;
1118+
req._readableState.ended = true;
1119+
req._readableState.endEmitted = true;
1120+
req._readableState.destroyed = true;
1121+
req._readableState.closed = true;
1122+
req._readableState.closeEmitted = true;
1123+
1124+
req._read();
1125+
}
1126+
}
1127+
11051128
if (socket._httpMessage) {
11061129
// There are already pending outgoing res, append.
11071130
state.outgoing.push(res);

test/common/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ const bits = ['arm64', 'loong64', 'mips', 'mipsel', 'ppc64', 'riscv64', 's390x',
3636
.includes(process.arch) ? 64 : 32;
3737
const hasIntl = !!process.config.variables.v8_enable_i18n_support;
3838

39+
globalThis.IS_NODE_TEST = true;
40+
3941
const {
4042
atob,
4143
btoa,

test/parallel/test-http-chunk-extensions-limit.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ const assert = require('assert');
124124
}));
125125

126126
sock.end('' +
127-
'GET / HTTP/1.1\r\n' +
127+
'PUT / HTTP/1.1\r\n' +
128128
`Host: localhost:${port}\r\n` +
129129
'Transfer-Encoding: chunked\r\n\r\n' +
130130
'2;' + 'A'.repeat(10000) + '=bar\r\nAA\r\n' +

test/parallel/test-http.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,18 @@ const server = http.Server(common.mustCall((req, res) => {
5252
if (expectedRequests.length === 0)
5353
server.close();
5454

55-
req.on('end', () => {
55+
if (req.readableEnded) {
5656
res.writeHead(200, { 'Content-Type': 'text/plain' });
5757
res.write(`The path was ${url.parse(req.url).pathname}`);
5858
res.end();
59-
});
60-
req.resume();
59+
} else {
60+
req.on('end', () => {
61+
res.writeHead(200, { 'Content-Type': 'text/plain' });
62+
res.write(`The path was ${url.parse(req.url).pathname}`);
63+
res.end();
64+
});
65+
req.resume();
66+
}
6167
}, 3));
6268
server.listen(0);
6369

0 commit comments

Comments
 (0)