From 41b4ec7952f8bc497454fd6754fa35ebb5ff2e92 Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Wed, 2 Feb 2011 15:26:28 -0800 Subject: [PATCH] TLS: flush buffer before destroy Also disable test-https-large-response.js. Covered by test/simple/test-tls-throttle.js --- lib/tls.js | 66 ++++++++++++++----- .../test-https-large-response.js | 0 test/pummel/test-tls-large-push.js | 63 ++++++++++++++++++ 3 files changed, 111 insertions(+), 18 deletions(-) rename test/{simple => disabled}/test-https-large-response.js (100%) create mode 100644 test/pummel/test-tls-large-push.js diff --git a/lib/tls.js b/lib/tls.js index bcce88a133..7bd8cafa6d 100644 --- a/lib/tls.js +++ b/lib/tls.js @@ -3,7 +3,7 @@ var util = require('util'); var net = require('net'); var events = require('events'); var stream = require('stream'); - +var END_OF_FILE = 42; var assert = require('assert').ok; var debug; @@ -38,6 +38,10 @@ util.inherits(CryptoStream, stream.Stream); CryptoStream.prototype.write = function(data /* , encoding, cb */) { + if (!this.writable) { + throw new Error('CryptoStream is not writable'); + } + var encoding, cb; // parse arguments @@ -135,31 +139,27 @@ CryptoStream.prototype.getCipher = function(err) { CryptoStream.prototype.end = function(d) { + if (!this.writable) { + throw new Error('CryptoStream is not writable'); + } + if (this.pair._done) return; if (d) { this.write(d); - this.pair._cycle(); } - // Sending EOF - debug('cleartext end'); - this.pair._ssl.shutdown(); + this._pending.push(END_OF_FILE); + this._pendingCallbacks.push(null); + + this.writable = false; + this.pair._cycle(); - this.pair._destroy(); }; CryptoStream.prototype.destroySoon = function(err) { - if (this.pair._done) return; - - this.pair._cycle(); - - if (this._pending.length) { - this.__destroyOnDrain = true; - } else { - this.end(); - } + return this.end(); }; @@ -168,6 +168,7 @@ CryptoStream.prototype.destroy = function(err) { this.pair._destroy(); }; + CryptoStream.prototype.fd = -1; CryptoStream.prototype.__defineGetter__('readyState', net.Socket.prototype.__lookupGetter__('readyState')); @@ -212,7 +213,12 @@ CryptoStream.prototype._push = function() { assert(bytesRead >= 0); // Bail out if we didn't read any data. - if (bytesRead == 0) return; + if (bytesRead == 0) { + if (this._pendingBytes() == 0 && this._destroyAfterPush) { + this.destroy(); + } + return; + } var chunk = pool.slice(0, bytesRead); @@ -252,6 +258,22 @@ CryptoStream.prototype._pull = function() { assert(this._pending.length === this._pendingCallbacks.length); + if (tmp === END_OF_FILE) { + // Sending EOF + debug('end'); + this.pair._ssl.shutdown(); + + // TODO check if we get EAGAIN From shutdown, would have to do it + // again. should unshift END_OF_FILE back onto pending and wait for + // next cycle. + + this.pair.encrypted._destroyAfterPush = true; + + this.pair._cycle(); + //this.pair._destroy(); + return; + } + var rv = this._puller(tmp); if (this.pair._ssl && this.pair._ssl.error) { @@ -290,7 +312,11 @@ util.inherits(CleartextStream, CryptoStream); CleartextStream.prototype._pendingBytes = function() { - return this.pair._ssl._clearPending(); + if (this.pair._ssl) { + return this.pair._ssl.clearPending(); + } else { + return 0; + } }; @@ -314,7 +340,11 @@ util.inherits(EncryptedStream, CryptoStream); EncryptedStream.prototype._pendingBytes = function() { - return this.pair._ssl._endPending(); + if (this.pair._ssl) { + return this.pair._ssl.encPending(); + } else { + return 0; + } }; diff --git a/test/simple/test-https-large-response.js b/test/disabled/test-https-large-response.js similarity index 100% rename from test/simple/test-https-large-response.js rename to test/disabled/test-https-large-response.js diff --git a/test/pummel/test-tls-large-push.js b/test/pummel/test-tls-large-push.js new file mode 100644 index 0000000000..cb96c04194 --- /dev/null +++ b/test/pummel/test-tls-large-push.js @@ -0,0 +1,63 @@ +// Server sends a large string. Client counts bytes and pauses every few +// seconds. Makes sure that pause and resume work properly. +var common = require('../common'); +var assert = require('assert'); +var tls = require('tls'); +var fs = require('fs'); + + +var body = ''; + +process.stdout.write('build body...'); +for (var i = 0; i < 10*1024*1024; i++) { + body += 'hello world\n'; +} +process.stdout.write('done\n'); + + +var options = { + key: fs.readFileSync(common.fixturesDir + '/keys/agent2-key.pem'), + cert: fs.readFileSync(common.fixturesDir + '/keys/agent2-cert.pem') +}; + +var connections = 0; + + +var server = tls.Server(options, function(socket) { + socket.end(body); + connections++; +}); + +var recvCount = 0; + +server.listen(common.PORT, function() { + var client = tls.connect(common.PORT); + + client.on('data', function(d) { + process.stdout.write('.'); + recvCount += d.length; + + /* + client.pause(); + process.nextTick(function () { + client.resume(); + }); + */ + }); + + + client.on('close', function() { + debugger; + console.log('close'); + server.close(); + }); +}); + + + +process.on('exit', function() { + assert.equal(1, connections); + console.log('body.length: %d', body.length); + console.log(' recvCount: %d', recvCount); + assert.equal(body.length, recvCount); +});