Skip to content

Commit

Permalink
http: prevent slowloris with keepalive connections
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
mcollina authored and rvagg committed Feb 27, 2019
1 parent 7840f71 commit 1a7302b
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 0 deletions.
15 changes: 15 additions & 0 deletions lib/_http_server.js
Expand Up @@ -606,6 +606,10 @@ function resOnFinish(req, res, socket, state, server) {
function parserOnIncoming(server, socket, state, req, keepAlive) {
resetSocketTimeout(server, socket, state);

if (server.keepAliveTimeout > 0) {
req.on('end', resetHeadersTimeoutOnReqEnd);
}

// Set to zero to communicate that we have finished parsing.
socket.parser.parsingHeadersStart = 0;

Expand Down Expand Up @@ -730,6 +734,17 @@ function socketOnWrap(ev, fn) {
return res;
}

function resetHeadersTimeoutOnReqEnd() {
debug('resetHeadersTimeoutOnReqEnd');

const parser = this.socket.parser;
// Parser can be null if the socket was destroyed
// in that case, there is nothing to do.
if (parser !== null) {
parser.parsingHeadersStart = nowDate();
}
}

module.exports = {
STATUS_CODES,
Server,
Expand Down
51 changes: 51 additions & 0 deletions test/parallel/test-http-slow-headers-keepalive.js
@@ -0,0 +1,51 @@
'use strict';

const common = require('../common');
const http = require('http');
const net = require('net');
const { finished } = require('stream');

const headers =
'GET / HTTP/1.1\r\n' +
'Host: localhost\r\n' +
'Connection: keep-alive' +
'Agent: node\r\n';

let sendCharEvery = 1000;

const server = http.createServer(common.mustCall((req, res) => {
res.writeHead(200);
res.end();
}));

// Pass a REAL env variable to shortening up the default
// value which is 40s otherwise this is useful for manual
// testing
if (!process.env.REAL) {
sendCharEvery = common.platformTimeout(10);
server.headersTimeout = 2 * sendCharEvery;
}

server.once('timeout', common.mustCall((socket) => {
socket.destroy();
}));

server.listen(0, () => {
const client = net.connect(server.address().port);
client.write(headers);
// finish the first request
client.write('\r\n');
// second request
client.write(headers);
client.write('X-CRASH: ');

const interval = setInterval(() => {
client.write('a');
}, sendCharEvery);

client.resume();
finished(client, common.mustCall((err) => {
clearInterval(interval);
server.close();
}));
});

0 comments on commit 1a7302b

Please sign in to comment.