Skip to content

Commit 3caa2c1

Browse files
authored
http: refactor headersTimeout and requestTimeout logic
PR-URL: #41263 Fixes: #33440 Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Robert Nagy <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Darshan Sen <[email protected]>
1 parent 9d6af7d commit 3caa2c1

33 files changed

+858
-413
lines changed

benchmark/http/chunked.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,11 @@ function main({ len, n, c, duration }) {
3232
send(n);
3333
});
3434

35-
server.listen(common.PORT, () => {
35+
server.listen(0, () => {
3636
bench.http({
3737
connections: c,
38-
duration
38+
duration,
39+
port: server.address().port,
3940
}, () => {
4041
server.close();
4142
});

benchmark/http/client-request-body.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -32,24 +32,24 @@ function main({ dur, len, type, method }) {
3232
headers: { 'Connection': 'keep-alive', 'Transfer-Encoding': 'chunked' },
3333
agent: new http.Agent({ maxSockets: 1 }),
3434
host: '127.0.0.1',
35-
port: common.PORT,
3635
path: '/',
3736
method: 'POST'
3837
};
3938

4039
const server = http.createServer((req, res) => {
4140
res.end();
4241
});
43-
server.listen(options.port, options.host, () => {
42+
server.listen(0, options.host, () => {
4443
setTimeout(done, dur * 1000);
4544
bench.start();
46-
pummel();
45+
pummel(server.address().port);
4746
});
4847

49-
function pummel() {
48+
function pummel(port) {
49+
options.port = port;
5050
const req = http.request(options, (res) => {
5151
nreqs++;
52-
pummel(); // Line up next request.
52+
pummel(port); // Line up next request.
5353
res.resume();
5454
});
5555
if (method === 'write') {

benchmark/http/end-vs-write-end.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,11 @@ function main({ len, type, method, c, duration }) {
4848
fn(res);
4949
});
5050

51-
server.listen(common.PORT, () => {
51+
server.listen(0, () => {
5252
bench.http({
5353
connections: c,
54-
duration
54+
duration,
55+
port: server.address().port,
5556
}, () => {
5657
server.close();
5758
});

benchmark/http/headers.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,12 @@ function main({ len, n, duration }) {
2727
res.writeHead(200, headers);
2828
res.end();
2929
});
30-
server.listen(common.PORT, () => {
30+
server.listen(0, () => {
3131
bench.http({
3232
path: '/',
3333
connections: 10,
34-
duration
34+
duration,
35+
port: server.address().port,
3536
}, () => {
3637
server.close();
3738
});

benchmark/http/incoming_headers.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ function main({ connections, headers, w, duration }) {
1414
res.end();
1515
});
1616

17-
server.listen(common.PORT, () => {
17+
server.listen(0, () => {
1818
const headers = {
1919
'Content-Type': 'text/plain',
2020
'Accept': 'text/plain',
@@ -34,7 +34,8 @@ function main({ connections, headers, w, duration }) {
3434
path: '/',
3535
connections,
3636
headers,
37-
duration
37+
duration,
38+
port: server.address().port,
3839
}, () => {
3940
server.close();
4041
});

benchmark/http/set-header.js

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
'use strict';
22
const common = require('../common.js');
3-
const PORT = common.PORT;
43

54
const bench = common.createBenchmark(main, {
65
res: ['normal', 'setHeader', 'setHeaderWH'],
@@ -17,16 +16,16 @@ const c = 50;
1716
// setHeader: statusCode = status, setHeader(...) x2
1817
// setHeaderWH: setHeader(...), writeHead(status, ...)
1918
function main({ res, duration }) {
20-
process.env.PORT = PORT;
2119
const server = require('../fixtures/simple-http-server.js')
22-
.listen(PORT)
20+
.listen(0)
2321
.on('listening', () => {
2422
const path = `/${type}/${len}/${chunks}/${res}/${chunkedEnc}`;
2523

2624
bench.http({
2725
path: path,
2826
connections: c,
29-
duration
27+
duration,
28+
port: server.address().port,
3029
}, () => {
3130
server.close();
3231
});

benchmark/http/simple.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,15 @@ const bench = common.createBenchmark(main, {
1313

1414
function main({ type, len, chunks, c, chunkedEnc, duration }) {
1515
const server = require('../fixtures/simple-http-server.js')
16-
.listen(common.PORT)
16+
.listen(0)
1717
.on('listening', () => {
1818
const path = `/${type}/${len}/${chunks}/normal/${chunkedEnc}`;
1919

2020
bench.http({
2121
path,
2222
connections: c,
23-
duration
23+
duration,
24+
port: server.address().port,
2425
}, () => {
2526
server.close();
2627
});

benchmark/http/upgrade.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ const resData = 'HTTP/1.1 101 Web Socket Protocol Handshake\r\n' +
2020

2121
function main({ n }) {
2222
const server = require('../fixtures/simple-http-server.js')
23-
.listen(common.PORT)
23+
.listen(0)
2424
.on('listening', () => {
2525
bench.start();
2626
doBench(server.address(), n, () => {

doc/api/http.md

+35-10
Original file line numberDiff line numberDiff line change
@@ -1364,15 +1364,12 @@ added:
13641364
Limit the amount of time the parser will wait to receive the complete HTTP
13651365
headers.
13661366

1367-
In case of inactivity, the rules defined in [`server.timeout`][] apply. However,
1368-
that inactivity based timeout would still allow the connection to be kept open
1369-
if the headers are being sent very slowly (by default, up to a byte per 2
1370-
minutes). In order to prevent this, whenever header data arrives an additional
1371-
check is made that more than `server.headersTimeout` milliseconds has not
1372-
passed since the connection was established. If the check fails, a `'timeout'`
1373-
event is emitted on the server object, and (by default) the socket is destroyed.
1374-
See [`server.timeout`][] for more information on how timeout behavior can be
1375-
customized.
1367+
If the timeout expires, the server responds with status 408 without
1368+
forwarding the request to the request listener and then closes the connection.
1369+
1370+
It must be set to a non-zero value (e.g. 120 seconds) to protect against
1371+
potential Denial-of-Service attacks in case the server is deployed without a
1372+
reverse proxy in front.
13761373

13771374
### `server.listen()`
13781375

@@ -1401,9 +1398,14 @@ Limits maximum incoming headers count. If set to 0, no limit will be applied.
14011398

14021399
<!-- YAML
14031400
added: v14.11.0
1401+
changes:
1402+
- version: REPLACEME
1403+
pr-url: https://github.com/nodejs/node/pull/41263
1404+
description: The default request timeout changed
1405+
from no timeout to 300s (5 minutes).
14041406
-->
14051407

1406-
* {number} **Default:** `0`
1408+
* {number} **Default:** `300000`
14071409

14081410
Sets the timeout value in milliseconds for receiving the entire request from
14091411
the client.
@@ -2856,6 +2858,10 @@ Found'`.
28562858
<!-- YAML
28572859
added: v0.1.13
28582860
changes:
2861+
- version: REPLACEME
2862+
pr-url: https://github.com/nodejs/node/pull/41263
2863+
description: The `requestTimeout`, `headersTimeout`, `keepAliveTimeout` and
2864+
`connectionsCheckingInterval` are supported now.
28592865
- version: REPLACEME
28602866
pr-url: https://github.com/nodejs/node/pull/42163
28612867
description: The `noDelay` option now defaults to `true`.
@@ -2886,6 +2892,22 @@ changes:
28862892
* `ServerResponse` {http.ServerResponse} Specifies the `ServerResponse` class
28872893
to be used. Useful for extending the original `ServerResponse`. **Default:**
28882894
`ServerResponse`.
2895+
* `requestTimeout`: Sets the timeout value in milliseconds for receiving
2896+
the entire request from the client.
2897+
See [`server.requestTimeout`][] for more information.
2898+
**Default:** `300000`.
2899+
* `headersTimeout`: Sets the timeout value in milliseconds for receiving
2900+
the complete HTTP headers from the client.
2901+
See [`server.headersTimeout`][] for more information.
2902+
**Default:** `60000`.
2903+
* `keepAliveTimeout`: The number of milliseconds of inactivity a server
2904+
needs to wait for additional incoming data, after it has finished writing
2905+
the last response, before a socket will be destroyed.
2906+
See [`server.keepAliveTimeout`][] for more information.
2907+
**Default:** `5000`.
2908+
* `connectionsCheckingInterval`: Sets the interval value in milliseconds to
2909+
check for request and headers timeout in incomplete requests.
2910+
**Default:** `30000`.
28892911
* `insecureHTTPParser` {boolean} Use an insecure HTTP parser that accepts
28902912
invalid HTTP headers when `true`. Using the insecure parser should be
28912913
avoided. See [`--insecure-http-parser`][] for more information.
@@ -3478,7 +3500,10 @@ try {
34783500
[`response.write(data, encoding)`]: #responsewritechunk-encoding-callback
34793501
[`response.writeContinue()`]: #responsewritecontinue
34803502
[`response.writeHead()`]: #responsewriteheadstatuscode-statusmessage-headers
3503+
[`server.headersTimeout`]: #serverheaderstimeout
3504+
[`server.keepAliveTimeout`]: #serverkeepalivetimeout
34813505
[`server.listen()`]: net.md#serverlisten
3506+
[`server.requestTimeout`]: #serverrequesttimeout
34823507
[`server.timeout`]: #servertimeout
34833508
[`setHeader(name, value)`]: #requestsetheadername-value
34843509
[`socket.connect()`]: net.md#socketconnectoptions-connectlistener

lib/_http_client.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -735,8 +735,7 @@ function tickOnSocket(req, socket) {
735735
parser.initialize(HTTPParser.RESPONSE,
736736
new HTTPClientAsyncResource('HTTPINCOMINGMESSAGE', req),
737737
req.maxHeaderSize || 0,
738-
lenient ? kLenientAll : kLenientNone,
739-
0);
738+
lenient ? kLenientAll : kLenientNone);
740739
parser.socket = socket;
741740
parser.outgoing = req;
742741
req.parser = parser;

lib/_http_common.js

-12
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,7 @@ const {
4040
readStop
4141
} = incoming;
4242

43-
let debug = require('internal/util/debuglog').debuglog('http', (fn) => {
44-
debug = fn;
45-
});
46-
4743
const kIncomingMessage = Symbol('IncomingMessage');
48-
const kRequestTimeout = Symbol('RequestTimeout');
4944
const kOnMessageBegin = HTTPParser.kOnMessageBegin | 0;
5045
const kOnHeaders = HTTPParser.kOnHeaders | 0;
5146
const kOnHeadersComplete = HTTPParser.kOnHeadersComplete | 0;
@@ -102,12 +97,6 @@ function parserOnHeadersComplete(versionMajor, versionMinor, headers, method,
10297
incoming.url = url;
10398
incoming.upgrade = upgrade;
10499

105-
if (socket) {
106-
debug('requestTimeout timer moved to req');
107-
incoming[kRequestTimeout] = incoming.socket[kRequestTimeout];
108-
incoming.socket[kRequestTimeout] = undefined;
109-
}
110-
111100
let n = headers.length;
112101

113102
// If parser.maxHeaderPairs <= 0 assume that there's no limit.
@@ -273,7 +262,6 @@ module.exports = {
273262
methods,
274263
parsers,
275264
kIncomingMessage,
276-
kRequestTimeout,
277265
HTTPParser,
278266
isLenient,
279267
prepareError,

0 commit comments

Comments
 (0)