Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 4395fe1

Browse filesBrowse files
RafaelGSSaduh95
authored andcommitted
http: add optimizeEmptyRequests server option
Signed-off-by: RafaelGSS <rafael.nunu@hotmail.com> Co-Authored-By: RafaelGSS <rafael.nunu@hotmail.com> PR-URL: #59778 Reviewed-By: Robert Nagy <ronagy@icloud.com> Reviewed-By: Tim Perry <pimterry@gmail.com> Reviewed-By: Gerhard Stöbich <deb2001-github@yahoo.de> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
1 parent 64fc625 commit 4395fe1
Copy full SHA for 4395fe1

File tree

Expand file treeCollapse file tree

4 files changed

+118
-0
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

4 files changed

+118
-0
lines changed
Open diff view settings
Collapse file

‎doc/api/http.md‎

Copy file name to clipboardExpand all lines: doc/api/http.md
+8Lines changed: 8 additions & 0 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -3555,6 +3555,9 @@ Found'`.
35553555
<!-- YAML
35563556
added: v0.1.13
35573557
changes:
3558+
- version: REPLACEME
3559+
pr-url: https://github.com/nodejs/node/pull/59778
3560+
description: Add optimizeEmptyRequests option.
35583561
- version: v24.9.0
35593562
pr-url: https://github.com/nodejs/node/pull/59824
35603563
description: The `shouldUpgradeCallback` option is now supported.
@@ -3660,6 +3663,11 @@ changes:
36603663
* `rejectNonStandardBodyWrites` {boolean} If set to `true`, an error is thrown
36613664
when writing to an HTTP response which does not have a body.
36623665
**Default:** `false`.
3666+
* `optimizeEmptyRequests` {boolean} If set to `true`, requests without `Content-Length`
3667+
or `Transfer-Encoding` headers (indicating no body) will be initialized with an
3668+
already-ended body stream, so they will never emit any stream events
3669+
(like `'data'` or `'end'`). You can use `req.readableEnded` to detect this case.
3670+
**Default:** `false`.
36633671
36643672
* `requestListener` {Function}
36653673
Collapse file

‎lib/_http_incoming.js‎

Copy file name to clipboardExpand all lines: lib/_http_incoming.js
+9Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,15 @@ function _addHeaderLineDistinct(field, value, dest) {
423423
}
424424
}
425425

426+
IncomingMessage.prototype._dumpAndCloseReadable = function _dumpAndCloseReadable() {
427+
this._dumped = true;
428+
this._readableState.ended = true;
429+
this._readableState.endEmitted = true;
430+
this._readableState.destroyed = true;
431+
this._readableState.closed = true;
432+
this._readableState.closeEmitted = true;
433+
};
434+
426435

427436
// Call this instead of resume() if we want to just
428437
// dump all the data to /dev/null
Collapse file

‎lib/_http_server.js‎

Copy file name to clipboardExpand all lines: lib/_http_server.js
+24Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,8 @@ const onResponseFinishChannel = dc.channel('http.server.response.finish');
107107
const kServerResponse = Symbol('ServerResponse');
108108
const kServerResponseStatistics = Symbol('ServerResponseStatistics');
109109

110+
const kOptimizeEmptyRequests = Symbol('OptimizeEmptyRequestsOption');
111+
110112
const {
111113
hasObserver,
112114
startPerf,
@@ -455,6 +457,11 @@ function storeHTTPOptions(options) {
455457
validateInteger(maxHeaderSize, 'maxHeaderSize', 0);
456458
this.maxHeaderSize = maxHeaderSize;
457459

460+
const optimizeEmptyRequests = options.optimizeEmptyRequests;
461+
if (optimizeEmptyRequests !== undefined)
462+
validateBoolean(optimizeEmptyRequests, 'options.optimizeEmptyRequests');
463+
this[kOptimizeEmptyRequests] = optimizeEmptyRequests || false;
464+
458465
const insecureHTTPParser = options.insecureHTTPParser;
459466
if (insecureHTTPParser !== undefined)
460467
validateBoolean(insecureHTTPParser, 'options.insecureHTTPParser');
@@ -1069,6 +1076,10 @@ function emitCloseNT(self) {
10691076
}
10701077
}
10711078

1079+
function hasBodyHeaders(headers) {
1080+
return ('content-length' in headers) || ('transfer-encoding' in headers);
1081+
}
1082+
10721083
// The following callback is issued after the headers have been read on a
10731084
// new message. In this callback we setup the response object and pass it
10741085
// to the user.
@@ -1120,6 +1131,19 @@ function parserOnIncoming(server, socket, state, req, keepAlive) {
11201131
});
11211132
}
11221133

1134+
// Check if we should optimize empty requests (those without Content-Length or Transfer-Encoding headers)
1135+
const shouldOptimize = server[kOptimizeEmptyRequests] === true && !hasBodyHeaders(req.headers);
1136+
1137+
if (shouldOptimize) {
1138+
// Fast processing where emitting 'data', 'end' and 'close' events is
1139+
// skipped and data is dumped.
1140+
// This avoids a lot of unnecessary overhead otherwise introduced by
1141+
// stream.Readable life cycle rules. The downside is that this will
1142+
// break some servers that read bodies for methods that don't have body headers.
1143+
req._dumpAndCloseReadable();
1144+
req._read();
1145+
}
1146+
11231147
if (socket._httpMessage) {
11241148
// There are already pending outgoing res, append.
11251149
state.outgoing.push(res);
Collapse file
+77Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const assert = require('assert');
5+
const http = require('http');
6+
const net = require('net');
7+
8+
let reqs = 0;
9+
let optimizedReqs = 0;
10+
const server = http.createServer({
11+
optimizeEmptyRequests: true
12+
}, (req, res) => {
13+
reqs++;
14+
if (req._dumped) {
15+
optimizedReqs++;
16+
req.on('data', common.mustNotCall());
17+
req.on('end', common.mustNotCall());
18+
19+
assert.strictEqual(req._dumped, true);
20+
assert.strictEqual(req.readableEnded, true);
21+
assert.strictEqual(req.destroyed, true);
22+
}
23+
res.writeHead(200);
24+
res.end('ok');
25+
});
26+
27+
server.listen(0, common.mustCall(async () => {
28+
// GET request without Content-Length (should be optimized)
29+
const getRequest = 'GET / HTTP/1.1\r\nHost: localhost\r\n\r\n';
30+
await makeRequest(getRequest);
31+
32+
// HEAD request (should always be optimized regardless of headers)
33+
const headRequest = 'HEAD / HTTP/1.1\r\nHost: localhost\r\n\r\n';
34+
await makeRequest(headRequest);
35+
36+
// POST request without body headers (should be optimized)
37+
const postWithoutBodyHeaders = 'POST / HTTP/1.1\r\nHost: localhost\r\n\r\n';
38+
await makeRequest(postWithoutBodyHeaders);
39+
40+
// DELETE request without body headers (should be optimized)
41+
const deleteWithoutBodyHeaders = 'DELETE / HTTP/1.1\r\nHost: localhost\r\n\r\n';
42+
await makeRequest(deleteWithoutBodyHeaders);
43+
44+
// POST request with Content-Length header (should not be optimized)
45+
const postWithContentLength = 'POST / HTTP/1.1\r\nHost: localhost\r\nContent-Length: 0\r\n\r\n';
46+
await makeRequest(postWithContentLength);
47+
48+
// GET request with Content-Length header (should not be optimized)
49+
const getWithContentLength = 'GET / HTTP/1.1\r\nHost: localhost\r\nContent-Length: 0\r\n\r\n';
50+
await makeRequest(getWithContentLength);
51+
52+
// POST request with Transfer-Encoding header (should not be optimized)
53+
const postWithTransferEncoding = 'POST / HTTP/1.1\r\nHost: localhost\r\nTransfer-Encoding: chunked\r\n\r\n';
54+
await makeRequest(postWithTransferEncoding);
55+
56+
// GET request with Transfer-Encoding header (should not be optimized)
57+
const getWithTransferEncoding = 'GET / HTTP/1.1\r\nHost: localhost\r\nTransfer-Encoding: chunked\r\n\r\n';
58+
await makeRequest(getWithTransferEncoding);
59+
60+
server.close();
61+
62+
assert.strictEqual(reqs, 8, `Expected 8 requests but got ${reqs}`);
63+
assert.strictEqual(optimizedReqs, 4, `Expected 4 optimized requests but got ${optimizedReqs}`);
64+
}));
65+
66+
function makeRequest(str) {
67+
return new Promise((resolve) => {
68+
const client = net.connect({ port: server.address().port }, common.mustCall(() => {
69+
client.on('data', () => {});
70+
client.on('end', common.mustCall(() => {
71+
resolve();
72+
}));
73+
client.write(str);
74+
client.end();
75+
}));
76+
});
77+
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.