diff --git a/lib/_tls_wrap.js b/lib/_tls_wrap.js index 929114d74ad6b6..85f071aaf9bd4e 100644 --- a/lib/_tls_wrap.js +++ b/lib/_tls_wrap.js @@ -46,6 +46,7 @@ const tls = require('tls'); const common = require('_tls_common'); const JSStreamSocket = require('internal/js_stream_socket'); const { Buffer } = require('buffer'); +const Writable = require('internal/streams/writable'); let debug = require('internal/util/debuglog').debuglog('tls', (fn) => { debug = fn; }); @@ -571,6 +572,14 @@ tls_wrap.TLSWrap.prototype.close = function close(cb) { return this._parent.close(done); }; +// TLS needs special handling when the server initiates the closing +// of the connection because a TLS-compliant client will send more data +// after receiving the server FIN +// If the server immediately destroys its socket, this data will trigger a +// RST packet in response +// https://github.com/nodejs/node/issues/36180 +TLSSocket.prototype.destroySoon = Writable.prototype.end; + TLSSocket.prototype.disableRenegotiation = function disableRenegotiation() { this[kDisableRenegotiation] = true; }; diff --git a/test/parallel/test-http-should-keep-alive.js b/test/parallel/test-http-should-keep-alive.js index 038af47ee4db97..33fa46187040d6 100644 --- a/test/parallel/test-http-should-keep-alive.js +++ b/test/parallel/test-http-should-keep-alive.js @@ -49,7 +49,7 @@ const countdown = new Countdown(SHOULD_KEEP_ALIVE.length, () => server.close()); const getCountdownIndex = () => SERVER_RESPONSES.length - countdown.remaining; const server = net.createServer(function(socket) { - socket.write(SERVER_RESPONSES[getCountdownIndex()]); + socket.end(SERVER_RESPONSES[getCountdownIndex()]); }).listen(0, function() { function makeRequest() { const req = http.get({ port: server.address().port }, function(res) { diff --git a/test/parallel/test-https-end-rst.js b/test/parallel/test-https-end-rst.js new file mode 100644 index 00000000000000..6249290b7b38e7 --- /dev/null +++ b/test/parallel/test-https-end-rst.js @@ -0,0 +1,40 @@ +'use strict'; +const common = require('../common'); + +if (!common.hasCrypto) + common.skip('missing crypto'); + +const https = require('https'); +const tls = require('tls'); +const fixtures = require('../common/fixtures'); + +const opt = { + key: fixtures.readKey('agent1-key.pem'), + cert: fixtures.readKey('agent1-cert.pem') +}; + +const data = 'hello'; +const server = https.createServer(opt, common.mustCallAtLeast((req, res) => { + res.setHeader('content-length', data.length); + res.end(data); +}, 2)); + +server.listen(0, function() { + const options = { + host: '127.0.0.1', + port: server.address().port, + rejectUnauthorized: false, + ALPNProtocols: ['http/1.1'], + allowHalfOpen: true + }; + const socket = tls.connect(options, common.mustCall(() => { + socket.write('GET /\n\n'); + socket.once('data', common.mustCall(() => { + socket.write('GET /\n\n'); + setTimeout(common.mustCall(() => { + socket.destroy(); + server.close(); + }), common.platformTimeout(10)); + })); + })); +});