Skip to content

Commit 76d52c5

Browse files
mcollinarvagg
authored andcommitted
http: prevent slowloris with keepalive connections
Fixes: nodejs-private/security#214 PR-URL: nodejs-private/node-private#162 Reviewed-By: Rod Vagg <rod@vagg.org> Reviewed-By: Sam Roberts <vieuxtech@gmail.com> Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
1 parent 852d25b commit 76d52c5

File tree

2 files changed

+73
-0
lines changed

2 files changed

+73
-0
lines changed

lib/_http_server.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -596,6 +596,10 @@ function resOnFinish(req, res, socket, state, server) {
596596
function parserOnIncoming(server, socket, state, req, keepAlive) {
597597
resetSocketTimeout(server, socket, state);
598598

599+
if (server.keepAliveTimeout > 0) {
600+
req.on('end', resetHeadersTimeoutOnReqEnd);
601+
}
602+
599603
// Set to zero to communicate that we have finished parsing.
600604
socket.parser.parsingHeadersStart = 0;
601605

@@ -714,6 +718,17 @@ function socketOnWrap(ev, fn) {
714718
return res;
715719
}
716720

721+
function resetHeadersTimeoutOnReqEnd() {
722+
debug('resetHeadersTimeoutOnReqEnd');
723+
724+
const parser = this.socket.parser;
725+
// Parser can be null if the socket was destroyed
726+
// in that case, there is nothing to do.
727+
if (parser !== null) {
728+
parser.parsingHeadersStart = nowDate();
729+
}
730+
}
731+
717732
module.exports = {
718733
STATUS_CODES,
719734
Server,
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const http = require('http');
5+
const net = require('net');
6+
7+
const headers =
8+
'GET / HTTP/1.1\r\n' +
9+
'Host: localhost\r\n' +
10+
'Connection: keep-alive' +
11+
'Agent: node\r\n';
12+
13+
let sendCharEvery = 1000;
14+
15+
const server = http.createServer(common.mustCall((req, res) => {
16+
res.writeHead(200);
17+
res.end();
18+
}));
19+
20+
// Pass a REAL env variable to shortening up the default
21+
// value which is 40s otherwise this is useful for manual
22+
// testing
23+
if (!process.env.REAL) {
24+
sendCharEvery = common.platformTimeout(10);
25+
server.headersTimeout = 2 * sendCharEvery;
26+
}
27+
28+
server.once('timeout', common.mustCall((socket) => {
29+
socket.destroy();
30+
}));
31+
32+
server.listen(0, () => {
33+
const client = net.connect(server.address().port);
34+
client.write(headers);
35+
// finish the first request
36+
client.write('\r\n');
37+
// second request
38+
client.write(headers);
39+
client.write('X-CRASH: ');
40+
41+
const interval = setInterval(() => {
42+
client.write('a');
43+
}, sendCharEvery);
44+
45+
client.resume();
46+
47+
const onClose = common.mustCall(() => {
48+
client.removeListener('close', onClose);
49+
client.removeListener('error', onClose);
50+
client.removeListener('end', onClose);
51+
clearInterval(interval);
52+
server.close();
53+
});
54+
55+
client.on('error', onClose);
56+
client.on('close', onClose);
57+
client.on('end', onClose);
58+
});

0 commit comments

Comments
 (0)