Skip to content

Commit 05534a2

Browse files
mcollinarvagg
authored andcommitted
http: prevent slowloris with keepalive connections
Fixes: nodejs-private/security#214 PR-URL: nodejs-private/node-private#158 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 ddba47a commit 05534a2

File tree

2 files changed

+66
-0
lines changed

2 files changed

+66
-0
lines changed

lib/_http_server.js

+15
Original file line numberDiff line numberDiff line change
@@ -613,6 +613,10 @@ function emitCloseNT(self) {
613613
function parserOnIncoming(server, socket, state, req, keepAlive) {
614614
resetSocketTimeout(server, socket, state);
615615

616+
if (server.keepAliveTimeout > 0) {
617+
req.on('end', resetHeadersTimeoutOnReqEnd);
618+
}
619+
616620
// Set to zero to communicate that we have finished parsing.
617621
socket.parser.parsingHeadersStart = 0;
618622

@@ -736,6 +740,17 @@ function socketOnWrap(ev, fn) {
736740
return res;
737741
}
738742

743+
function resetHeadersTimeoutOnReqEnd() {
744+
debug('resetHeadersTimeoutOnReqEnd');
745+
746+
const parser = this.socket.parser;
747+
// Parser can be null if the socket was destroyed
748+
// in that case, there is nothing to do.
749+
if (parser !== null) {
750+
parser.parsingHeadersStart = nowDate();
751+
}
752+
}
753+
739754
module.exports = {
740755
STATUS_CODES,
741756
Server,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const http = require('http');
5+
const net = require('net');
6+
const { finished } = require('stream');
7+
8+
const headers =
9+
'GET / HTTP/1.1\r\n' +
10+
'Host: localhost\r\n' +
11+
'Connection: keep-alive' +
12+
'Agent: node\r\n';
13+
14+
let sendCharEvery = 1000;
15+
16+
const server = http.createServer(common.mustCall((req, res) => {
17+
res.writeHead(200);
18+
res.end();
19+
}));
20+
21+
// Pass a REAL env variable to shortening up the default
22+
// value which is 40s otherwise this is useful for manual
23+
// testing
24+
if (!process.env.REAL) {
25+
sendCharEvery = common.platformTimeout(10);
26+
server.headersTimeout = 2 * sendCharEvery;
27+
}
28+
29+
server.once('timeout', common.mustCall((socket) => {
30+
socket.destroy();
31+
}));
32+
33+
server.listen(0, () => {
34+
const client = net.connect(server.address().port);
35+
client.write(headers);
36+
// finish the first request
37+
client.write('\r\n');
38+
// second request
39+
client.write(headers);
40+
client.write('X-CRASH: ');
41+
42+
const interval = setInterval(() => {
43+
client.write('a');
44+
}, sendCharEvery);
45+
46+
client.resume();
47+
finished(client, common.mustCall((err) => {
48+
clearInterval(interval);
49+
server.close();
50+
}));
51+
});

0 commit comments

Comments
 (0)