From c365f5606121c4a1b8edbf260a0e32199e453fe7 Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Tue, 15 Feb 2011 16:45:54 -0800 Subject: [PATCH] https was missing 'end' event sometimes Closes GH-671. --- lib/http.js | 4 +- lib/tls.js | 15 ++++-- test/simple/test-https-eof-for-eom.js | 77 +++++++++++++++++++++++++++ 3 files changed, 90 insertions(+), 6 deletions(-) create mode 100644 test/simple/test-https-eof-for-eom.js diff --git a/lib/http.js b/lib/http.js index c806424d4e..c2f6e18741 100644 --- a/lib/http.js +++ b/lib/http.js @@ -1009,13 +1009,13 @@ function connectionListener(socket) { if (!self.httpAllowHalfOpen) { abortIncoming(); - socket.end(); + if (socket.writable) socket.end(); } else if (outgoing.length) { outgoing[outgoing.length - 1]._last = true; } else if (socket._httpMessage) { socket._httpMessage._last = true; } else { - socket.end(); + if (socket.writable) socket.end(); } }; diff --git a/lib/tls.js b/lib/tls.js index 3c39c0868b..cbe706b8af 100644 --- a/lib/tls.js +++ b/lib/tls.js @@ -531,18 +531,25 @@ SecurePair.prototype._maybeInitFinished = function() { SecurePair.prototype._destroy = function() { + var self = this; + if (!this._done) { this._done = true; this._ssl.error = null; this._ssl.close(); this._ssl = null; - this.encrypted.emit('end'); - this.encrypted.emit('close'); + process.nextTick(function() { + self.encrypted.emit('end'); + if (self.encrypted.onend) self.encrypted.onend(); + self.encrypted.emit('close'); - this.cleartext.emit('end'); - this.cleartext.emit('close'); + self.cleartext.emit('end'); + if (self.cleartext.onend) self.cleartext.onend(); + self.cleartext.emit('close'); + }); } + this._cycle(); }; diff --git a/test/simple/test-https-eof-for-eom.js b/test/simple/test-https-eof-for-eom.js new file mode 100644 index 0000000000..d562a21fdb --- /dev/null +++ b/test/simple/test-https-eof-for-eom.js @@ -0,0 +1,77 @@ +// I hate HTTP. One way of terminating an HTTP response is to not send +// a content-length header, not send a transfer-encoding: chunked header, +// and simply terminate the TCP connection. That is identity +// transfer-encoding. +// +// This test is to be sure that the https client is handling this case +// correctly. +if (!process.versions.openssl) { + console.error('Skipping because node compiled without OpenSSL.'); + process.exit(0); +} + +var common = require('../common'); +var assert = require('assert'); +var tls = require('tls'); +var https = require('https'); +var fs = require('fs'); + +var options = { + key: fs.readFileSync(common.fixturesDir + '/keys/agent1-key.pem'), + cert: fs.readFileSync(common.fixturesDir + '/keys/agent1-cert.pem') +}; + + +var server = tls.Server(options, function(socket) { + console.log('2) Server got request'); + socket.write('HTTP/1.1 200 OK\r\n' + + 'Date: Tue, 15 Feb 2011 22:14:54 GMT\r\n' + + 'Expires: -1\r\n' + + 'Cache-Control: private, max-age=0\r\n' + + 'Set-Cookie: xyz\r\n' + + 'Set-Cookie: abc\r\n' + + 'Server: gws\r\n' + + 'X-XSS-Protection: 1; mode=block\r\n' + + 'Connection: close\r\n' + + '\r\n'); + + socket.write('hello world\n'); + + setTimeout(function() { + socket.end('hello world\n'); + console.log('4) Server finished response'); + }, 100); +}); + + +var gotHeaders = false; +var gotEnd = false; +var bodyBuffer = ''; + +server.listen(common.PORT, function() { + console.log('1) Making Request'); + var req = https.get({ port: common.PORT }, function(res) { + server.close(); + console.log('3) Client got response headers.'); + + assert.equal('gws', res.headers.server); + gotHeaders = true; + + res.setEncoding('utf8'); + res.on('data', function(s) { + bodyBuffer += s; + }); + + res.on('end', function() { + console.log('5) Client got "end" event.'); + gotEnd = true; + }); + }); +}); + +process.on('exit', function() { + assert.ok(gotHeaders); + assert.ok(gotEnd); + assert.equal('hello world\nhello world\n', bodyBuffer); +}); +