Skip to content

Commit 36fd3da

Browse files
ronagcodebytere
authored andcommitted
http: provide keep-alive timeout response header
In http 1.1 persistent connection protocol there is a timing race where the client sends the request and then the server kills the connection (due to inactivity) before receiving the client's request. By providing a keep-alive header it is possible to provide the client a hint of when idle timeout would occur and avoid the race. Fixes: #34560 PR-URL: #34561 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Luigi Pinca <[email protected]> Reviewed-By: Zeyu Yang <[email protected]> Reviewed-By: Trivikram Kamat <[email protected]> Reviewed-By: Pranshu Srivastava <[email protected]>
1 parent c1abc8d commit 36fd3da

File tree

3 files changed

+36
-0
lines changed

3 files changed

+36
-0
lines changed

lib/_http_outgoing.js

+7
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ const {
2828
ObjectKeys,
2929
ObjectPrototypeHasOwnProperty,
3030
ObjectSetPrototypeOf,
31+
MathFloor,
3132
Symbol,
3233
} = primordials;
3334

@@ -118,6 +119,8 @@ function OutgoingMessage() {
118119
this._header = null;
119120
this[kOutHeaders] = null;
120121

122+
this._keepAliveTimeout = 0;
123+
121124
this._onPendingData = noopPendingOutput;
122125
}
123126
ObjectSetPrototypeOf(OutgoingMessage.prototype, Stream.prototype);
@@ -419,6 +422,10 @@ function _storeHeader(firstLine, headers) {
419422
(state.contLen || this.useChunkedEncodingByDefault || this.agent);
420423
if (shouldSendKeepAlive) {
421424
header += 'Connection: keep-alive\r\n';
425+
if (this._keepAliveTimeout) {
426+
const timeoutSeconds = MathFloor(this._keepAliveTimeout) / 1000;
427+
header += `Keep-Alive: timeout=${timeoutSeconds}\r\n`;
428+
}
422429
} else {
423430
this._last = true;
424431
header += 'Connection: close\r\n';

lib/_http_server.js

+1
Original file line numberDiff line numberDiff line change
@@ -743,6 +743,7 @@ function parserOnIncoming(server, socket, state, req, keepAlive) {
743743
}
744744

745745
const res = new server[kServerResponse](req);
746+
res._keepAliveTimeout = server.keepAliveTimeout;
746747
res._onPendingData = updateOutgoingData.bind(undefined, socket, state);
747748

748749
res.shouldKeepAlive = keepAlive;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const http = require('http');
5+
const assert = require('assert');
6+
7+
const server = http.createServer(common.mustCall((req, res) => {
8+
const body = 'hello world\n';
9+
10+
res.writeHead(200, { 'Content-Length': body.length });
11+
res.write(body);
12+
res.end();
13+
}));
14+
server.keepAliveTimeout = 12000;
15+
16+
const agent = new http.Agent({ maxSockets: 1, keepAlive: true });
17+
18+
server.listen(0, common.mustCall(function() {
19+
http.get({
20+
path: '/', port: this.address().port, agent: agent
21+
}, common.mustCall((response) => {
22+
response.resume();
23+
assert.strictEqual(
24+
response.headers['keep-alive'], 'timeout=12');
25+
server.close();
26+
agent.destroy();
27+
}));
28+
}));

0 commit comments

Comments
 (0)