Skip to content

Commit

Permalink
http: keep HTTP/1.1 conns alive even if the Connection header is removed
Browse files Browse the repository at this point in the history
Previously persistence was completed disabled if you removed this
header, which is not correct for modern HTTP, where the header is
optional and all connections should persist by default regardless.

PR-URL: #46331
Reviewed-By: Matteo Collina <[email protected]>
Reviewed-By: Minwoo Jung <[email protected]>
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Ricky Zhou <[email protected]>
  • Loading branch information
pimterry authored and aduh95 committed Jan 30, 2023
1 parent f35723b commit 7498b1c
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 4 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -346,8 +346,6 @@ For information about the governance of the Node.js project, see
**Zeyu "Alex" Yang** <<[email protected]>> (he/him)
* [iansu](https://github.com/iansu) -
**Ian Sutherland** <<[email protected]>>
* [indutny](https://github.com/indutny) -
**Fedor Indutny** <<[email protected]>>
* [JacksonTian](https://github.com/JacksonTian) -
**Jackson Tian** <<[email protected]>>
* [JakobJingleheimer](https://github.com/JakobJingleheimer) -
Expand Down Expand Up @@ -534,6 +532,8 @@ For information about the governance of the Node.js project, see
**Imran Iqbal** <<[email protected]>>
* [imyller](https://github.com/imyller) -
**Ilkka Myller** <<[email protected]>>
* [indutny](https://github.com/indutny) -
**Fedor Indutny** <<[email protected]>>
* [isaacs](https://github.com/isaacs) -
**Isaac Z. Schlueter** <<[email protected]>>
* [italoacasas](https://github.com/italoacasas) -
Expand Down
5 changes: 3 additions & 2 deletions lib/_http_outgoing.js
Original file line number Diff line number Diff line change
Expand Up @@ -506,8 +506,9 @@ function _storeHeader(firstLine, headers) {

// keep-alive logic
if (this._removedConnection) {
this._last = true;
this.shouldKeepAlive = false;
// shouldKeepAlive is generally true for HTTP/1.1. In that common case,
// even if the connection header isn't sent, we still persist by default.
this._last = !this.shouldKeepAlive;
} else if (!state.connection) {
const shouldSendKeepAlive = this.shouldKeepAlive &&
(state.contLen || this.useChunkedEncodingByDefault || this.agent);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
'use strict';
require('../common');
const assert = require('assert');

const net = require('net');
const http = require('http');

const server = http.createServer(function(request, response) {
// When the connection header is removed, for HTTP/1.1 the connection should still persist.
// For HTTP/1.0, the connection should be closed after the response automatically.
response.removeHeader('connection');

response.end('beep boop\n');
});

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

function makeHttp11Request(cb) {
http.get({
port: server.address().port,
agent
}, function(res) {
const socket = res.socket;

assert.strictEqual(res.statusCode, 200);
assert.strictEqual(res.headers.connection, undefined);

res.setEncoding('ascii');
let response = '';
res.on('data', function(chunk) {
response += chunk;
});
res.on('end', function() {
assert.strictEqual(response, 'beep boop\n');

// Wait till next tick to ensure that the socket is returned to the agent before
// we continue to the next request
process.nextTick(function() {
cb(socket);
});
});
});
}

function makeHttp10Request(cb) {
// We have to manually make HTTP/1.0 requests since Node does not allow sending them:
const socket = net.connect({ port: server.address().port }, function() {
socket.write('GET / HTTP/1.0\r\n' +
'Host: localhost:' + server.address().port + '\r\n' +
'\r\n');
socket.resume(); // Ignore the response itself

setTimeout(function() {
cb(socket);
}, 10);
});
}

server.listen(0, function() {
makeHttp11Request(function(firstSocket) {
makeHttp11Request(function(secondSocket) {
// Both HTTP/1.1 requests should have used the same socket:
assert.strictEqual(firstSocket, secondSocket);

makeHttp10Request(function(socket) {
// The server should have immediately closed the HTTP/1.0 socket:
assert.strictEqual(socket.closed, true);
server.close();
});
});
});
});

0 comments on commit 7498b1c

Please sign in to comment.