Skip to content

Commit 661251d

Browse files
committed
http: limit requests per connection
1 parent 3e8eda2 commit 661251d

File tree

2 files changed

+31
-3
lines changed

2 files changed

+31
-3
lines changed

lib/_http_outgoing.js

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ function OutgoingMessage() {
134134
this._header = null;
135135
this[kOutHeaders] = null;
136136

137+
this._maxRequestsPerSocket = null;
137138
this._keepAliveTimeout = 0;
138139

139140
this._onPendingData = nop;
@@ -448,9 +449,23 @@ function _storeHeader(firstLine, headers) {
448449
(state.contLen || this.useChunkedEncodingByDefault || this.agent);
449450
if (shouldSendKeepAlive) {
450451
header += 'Connection: keep-alive' + CRLF;
451-
if (this._keepAliveTimeout && this._defaultKeepAlive) {
452-
const timeoutSeconds = MathFloor(this._keepAliveTimeout / 1000);
453-
header += `Keep-Alive: timeout=${timeoutSeconds}${CRLF}`;
452+
453+
if (this._defaultKeepAlive) {
454+
let keepAliveParameters = '';
455+
456+
if (this._keepAliveTimeout) {
457+
const timeoutSeconds = MathFloor(this._keepAliveTimeout / 1000);
458+
keepAliveParameters += `timeout=${timeoutSeconds}`;
459+
}
460+
461+
if (this._maxRequestsPerSocket) {
462+
if (keepAliveParameters.length > 0) keepAliveParameters += ', ';
463+
keepAliveParameters += `max=${this._maxRequestsPerSocket}`;
464+
}
465+
466+
if (keepAliveParameters.length > 0) {
467+
header += `Keep-Alive: ${keepAliveParameters}${CRLF}`;
468+
}
454469
}
455470
} else {
456471
this._last = true;

lib/_http_server.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,7 @@ function Server(options, requestListener) {
395395
this.timeout = 0;
396396
this.keepAliveTimeout = 5000;
397397
this.maxHeadersCount = null;
398+
this.maxRequestsPerSocket = null;
398399
this.headersTimeout = 60 * 1000; // 60 seconds
399400
this.requestTimeout = 0;
400401
}
@@ -486,6 +487,7 @@ function connectionListenerInternal(server, socket) {
486487
// need to pause TCP socket/HTTP parser, and wait until the data will be
487488
// sent to the client.
488489
outgoingData: 0,
490+
requestsCount: 0,
489491
keepAliveTimeoutSet: false
490492
};
491493
state.onData = socketOnData.bind(undefined,
@@ -876,6 +878,7 @@ function parserOnIncoming(server, socket, state, req, keepAlive) {
876878

877879
const res = new server[kServerResponse](req);
878880
res._keepAliveTimeout = server.keepAliveTimeout;
881+
res._maxRequestsPerSocket = server.maxRequestsPerSocket;
879882
res._onPendingData = updateOutgoingData.bind(undefined,
880883
socket, state);
881884

@@ -904,6 +907,16 @@ function parserOnIncoming(server, socket, state, req, keepAlive) {
904907
resOnFinish.bind(undefined,
905908
req, res, socket, state, server));
906909

910+
if (req.httpVersionMajor === 1 && req.httpVersionMinor === 1
911+
&& typeof server.maxRequestsPerSocket === 'number'
912+
&& server.maxRequestsPerSocket > ++state.requestsCount) {
913+
res.shouldKeepAlive = false;
914+
res.writeHead(503, {
915+
'Connection': 'close'
916+
});
917+
res.end();
918+
}
919+
907920
if (req.headers.expect !== undefined &&
908921
(req.httpVersionMajor === 1 && req.httpVersionMinor === 1)) {
909922
if (RegExpPrototypeTest(continueExpression, req.headers.expect)) {

0 commit comments

Comments
 (0)