Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
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 <anna@addaleax.net>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Reviewed-By: Zeyu Yang <himself65@outlook.com>
Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
Reviewed-By: Pranshu Srivastava <rexagod@gmail.com>
  • Loading branch information
ronag committed Aug 1, 2020
commit 849d9e7b90ebaae930a28a7994b3a56ec4f280c6
7 changes: 7 additions & 0 deletions lib/_http_outgoing.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const {
ObjectKeys,
ObjectPrototypeHasOwnProperty,
ObjectSetPrototypeOf,
MathFloor,
Symbol,
} = primordials;

Expand Down Expand Up @@ -123,6 +124,8 @@ function OutgoingMessage() {
this._header = null;
this[kOutHeaders] = null;

this._keepAliveTimeout = 0;

this._onPendingData = noopPendingOutput;
}
ObjectSetPrototypeOf(OutgoingMessage.prototype, Stream.prototype);
Expand Down Expand Up @@ -424,6 +427,10 @@ function _storeHeader(firstLine, headers) {
(state.contLen || this.useChunkedEncodingByDefault || this.agent);
if (shouldSendKeepAlive) {
header += 'Connection: keep-alive\r\n';
if (this._keepAliveTimeout) {
const timeoutSeconds = MathFloor(this._keepAliveTimeout) / 1000;
header += `Keep-Alive: timeout=${timeoutSeconds}\r\n`;
}
} else {
this._last = true;
header += 'Connection: close\r\n';
Expand Down
1 change: 1 addition & 0 deletions lib/_http_server.js
Original file line number Diff line number Diff line change
Expand Up @@ -766,6 +766,7 @@ function parserOnIncoming(server, socket, state, req, keepAlive) {
}

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

res.shouldKeepAlive = keepAlive;
Expand Down
28 changes: 28 additions & 0 deletions test/parallel/test-http-keep-alive-timeout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
'use strict';

const common = require('../common');
const http = require('http');
const assert = require('assert');

const server = http.createServer(common.mustCall((req, res) => {
const body = 'hello world\n';

res.writeHead(200, { 'Content-Length': body.length });
res.write(body);
res.end();
}));
server.keepAliveTimeout = 12000;

const agent = new http.Agent({ maxSockets: 1, keepAlive: true });

server.listen(0, common.mustCall(function() {
http.get({
path: '/', port: this.address().port, agent: agent
}, common.mustCall((response) => {
response.resume();
assert.strictEqual(
response.headers['keep-alive'], 'timeout=12');
server.close();
agent.destroy();
}));
}));