From 55bff5bab90903783751677ff17b0fab1cd8157f Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Fri, 6 May 2011 16:48:44 -0700 Subject: [PATCH] TLS: simplify logic --- lib/tls.js | 183 +++++++++++++++++++++++++---------------------------- 1 file changed, 86 insertions(+), 97 deletions(-) diff --git a/lib/tls.js b/lib/tls.js index a2496b23837..49ca0c4898a 100644 --- a/lib/tls.js +++ b/lib/tls.js @@ -51,7 +51,7 @@ function CryptoStream(pair) { this.readable = this.writable = true; - this._writeState = true; + this._paused = false; this._pending = []; this._pendingCallbacks = []; this._pendingBytes = 0; @@ -90,11 +90,10 @@ CryptoStream.prototype.write = function(data /* , encoding, cb */) { this._pending.push(data); this._pendingCallbacks.push(cb); - this._pendingBytes += data.length; this.pair._writeCalled = true; - this.pair._cycle(); + this.pair.cycle(); return this._pendingBytes < 128 * 1024; }; @@ -102,14 +101,14 @@ CryptoStream.prototype.write = function(data /* , encoding, cb */) { CryptoStream.prototype.pause = function() { debug('paused ' + (this == this.pair.cleartext ? 'cleartext' : 'encrypted')); - this._writeState = false; + this._paused = true; }; CryptoStream.prototype.resume = function() { debug('resume ' + (this == this.pair.cleartext ? 'cleartext' : 'encrypted')); - this._writeState = true; - this.pair._cycle(); + this._paused = false; + this.pair.cycle(); }; @@ -147,8 +146,8 @@ function parseCertString(s) { CryptoStream.prototype.getPeerCertificate = function() { - if (this.pair._ssl) { - var c = this.pair._ssl.getPeerCertificate(); + if (this.pair.ssl) { + var c = this.pair.ssl.getPeerCertificate(); if (c) { if (c.issuer) c.issuer = parseCertString(c.issuer); @@ -162,8 +161,8 @@ CryptoStream.prototype.getPeerCertificate = function() { CryptoStream.prototype.getCipher = function(err) { - if (this.pair._ssl) { - return this.pair._ssl.getCurrentCipher(); + if (this.pair.ssl) { + return this.pair.ssl.getCurrentCipher(); } else { return null; } @@ -171,23 +170,22 @@ CryptoStream.prototype.getCipher = function(err) { CryptoStream.prototype.end = function(d) { - if (this.writable) { - if (this.pair._done) return; + if (this.pair._doneFlag) return; + if (!this.writable) return; - if (d) { - this.write(d); - } - - this._pending.push(END_OF_FILE); - this._pendingCallbacks.push(null); - - // If this is an encrypted stream then we need to disable further 'data' - // events. - - this.writable = false; - - this.pair._cycle(); + if (d) { + this.write(d); } + + this._pending.push(END_OF_FILE); + this._pendingCallbacks.push(null); + + // If this is an encrypted stream then we need to disable further 'data' + // events. + + this.writable = false; + + this.pair.cycle(); }; @@ -201,8 +199,8 @@ CryptoStream.prototype.destroySoon = function(err) { CryptoStream.prototype.destroy = function(err) { - if (this.pair._done) return; - this.pair._destroy(); + if (this.pair._doneFlag) return; + this.pair.destroy(); }; @@ -211,9 +209,9 @@ CryptoStream.prototype._done = function() { if (this.pair.cleartext._doneFlag && this.pair.encrypted._doneFlag && - !this.pair._done) { + !this.pair._doneFlag) { // If both streams are done: - this.pair._destroy(); + this.pair.destroy(); } }; @@ -241,7 +239,7 @@ CryptoStream.prototype._push = function() { return; } - while (this._writeState == true) { + while (!this._paused) { var bytesRead = 0; var chunkBytes = 0; var pool = new Buffer(16 * 4096); // alloc every time? @@ -249,18 +247,18 @@ CryptoStream.prototype._push = function() { do { chunkBytes = this._pusher(pool, bytesRead, pool.length - bytesRead); - if (this.pair._ssl && this.pair._ssl.error) { - this.pair._error(); + if (this.pair.ssl && this.pair.ssl.error) { + this.pair.error(); return; } - this.pair._maybeInitFinished(); + this.pair.maybeInitFinished(); if (chunkBytes >= 0) { bytesRead += chunkBytes; } - } while ((chunkBytes > 0) && (bytesRead < pool.length)); + } while (chunkBytes > 0 && bytesRead < pool.length); assert(bytesRead >= 0); @@ -313,7 +311,7 @@ CryptoStream.prototype._pull = function() { assert(havePending || this._pendingBytes == 0); while (this._pending.length > 0) { - if (!this.pair._ssl) break; + if (!this.pair.ssl) break; var tmp = this._pending.shift(); var cb = this._pendingCallbacks.shift(); @@ -330,7 +328,7 @@ CryptoStream.prototype._pull = function() { assert(this === this.pair.cleartext); debug('end cleartext'); - this.pair._ssl.shutdown(); + 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 @@ -338,7 +336,7 @@ CryptoStream.prototype._pull = function() { this.pair.encrypted._destroyAfterPush = true; } - this.pair._cycle(); + this.pair.cycle(); this._done() return; } @@ -347,12 +345,12 @@ CryptoStream.prototype._pull = function() { var rv = this._puller(tmp); - if (this.pair._ssl && this.pair._ssl.error) { - this.pair._error(); + if (this.pair.ssl && this.pair.ssl.error) { + this.pair.error(); return; } - this.pair._maybeInitFinished(); + this.pair.maybeInitFinished(); if (rv === 0 || rv < 0) { this._pending.unshift(tmp); @@ -384,8 +382,8 @@ util.inherits(CleartextStream, CryptoStream); CleartextStream.prototype._internallyPendingBytes = function() { - if (this.pair._ssl) { - return this.pair._ssl.clearPending(); + if (this.pair.ssl) { + return this.pair.ssl.clearPending(); } else { return 0; } @@ -394,14 +392,14 @@ CleartextStream.prototype._internallyPendingBytes = function() { CleartextStream.prototype._puller = function(b) { debug('clearIn ' + b.length + ' bytes'); - return this.pair._ssl.clearIn(b, 0, b.length); + return this.pair.ssl.clearIn(b, 0, b.length); }; CleartextStream.prototype._pusher = function(pool, offset, length) { debug('reading from clearOut'); - if (!this.pair._ssl) return -1; - return this.pair._ssl.clearOut(pool, offset, length); + if (!this.pair.ssl) return -1; + return this.pair.ssl.clearOut(pool, offset, length); }; @@ -412,8 +410,8 @@ util.inherits(EncryptedStream, CryptoStream); EncryptedStream.prototype._internallyPendingBytes = function() { - if (this.pair._ssl) { - return this.pair._ssl.encPending(); + if (this.pair.ssl) { + return this.pair.ssl.encPending(); } else { return 0; } @@ -422,14 +420,14 @@ EncryptedStream.prototype._internallyPendingBytes = function() { EncryptedStream.prototype._puller = function(b) { debug('writing from encIn'); - return this.pair._ssl.encIn(b, 0, b.length); + return this.pair.ssl.encIn(b, 0, b.length); }; EncryptedStream.prototype._pusher = function(pool, offset, length) { debug('reading from encOut'); - if (!this.pair._ssl) return -1; - return this.pair._ssl.encOut(pool, offset, length); + if (!this.pair.ssl) return -1; + return this.pair.ssl.encOut(pool, offset, length); }; @@ -453,9 +451,7 @@ function SecurePair(credentials, isServer, requestCert, rejectUnauthorized) { this._isServer = isServer ? true : false; this._encWriteState = true; this._clearWriteState = true; - this._done = false; - - var crypto = require('crypto'); + this._doneFlag = false; if (!credentials) { this.credentials = crypto.createCredentials(); @@ -473,7 +469,7 @@ function SecurePair(credentials, isServer, requestCert, rejectUnauthorized) { this._rejectUnauthorized = rejectUnauthorized ? true : false; this._requestCert = requestCert ? true : false; - this._ssl = new Connection(this.credentials.context, + this.ssl = new Connection(this.credentials.context, this._isServer ? true : false, this._requestCert, this._rejectUnauthorized); @@ -486,8 +482,8 @@ function SecurePair(credentials, isServer, requestCert, rejectUnauthorized) { this.encrypted = new EncryptedStream(this); process.nextTick(function() { - self._ssl.start(); - self._cycle(); + self.ssl.start(); + self.cycle(); }); } @@ -535,59 +531,54 @@ exports.createSecurePair = function(credentials, * Because it is also called everywhere, we also check if the connection has * completed negotiation and emit 'secure' from here if it has. */ -SecurePair.prototype._cycle = function(depth) { - depth = depth ? depth : 0; - if (this._done) { - return; - } +SecurePair.prototype.cycle = function(depth) { + if (this._doneFlag) return; - if(depth == 0) this._writeCalled = false; + depth = depth ? depth : 0; + + if (depth == 0) this._writeCalled = false; var established = this._secureEstablished; - if (!this._cycleEncryptedPullLock) { - this._cycleEncryptedPullLock = true; + if (!this.cycleEncryptedPullLock) { + this.cycleEncryptedPullLock = true; debug("encrypted._pull"); this.encrypted._pull(); - this._cycleEncryptedPullLock = false; + this.cycleEncryptedPullLock = false; } - if (!this._cycleCleartextPullLock) { - this._cycleCleartextPullLock = true; + if (!this.cycleCleartextPullLock) { + this.cycleCleartextPullLock = true; debug("cleartext._pull"); this.cleartext._pull(); - this._cycleCleartextPullLock = false; + this.cycleCleartextPullLock = false; } - if (!this._cycleCleartextPushLock) { - this._cycleCleartextPushLock = true; + if (!this.cycleCleartextPushLock) { + this.cycleCleartextPushLock = true; debug("cleartext._push"); this.cleartext._push(); - this._cycleCleartextPushLock = false; + this.cycleCleartextPushLock = false; } - if (!this._cycleEncryptedPushLock) { - this._cycleEncryptedPushLock = true; + if (!this.cycleEncryptedPushLock) { + this.cycleEncryptedPushLock = true; debug("encrypted._push"); this.encrypted._push(); - this._cycleEncryptedPushLock = false; - } - - if (this._done) { - return; + this.cycleEncryptedPushLock = false; } if ((!established && this._secureEstablished) || (depth == 0 && this._writeCalled)) { // If we were not established but now we are, let's cycle again. // Or if there is some data to write... - this._cycle(depth + 1); + this.cycle(depth + 1); } }; -SecurePair.prototype._maybeInitFinished = function() { - if (this._ssl && !this._secureEstablished && this._ssl.isInitFinished()) { +SecurePair.prototype.maybeInitFinished = function() { + if (this.ssl && !this._secureEstablished && this.ssl.isInitFinished()) { this._secureEstablished = true; debug('secure established'); this.emit('secure'); @@ -595,14 +586,14 @@ SecurePair.prototype._maybeInitFinished = function() { }; -SecurePair.prototype._destroy = function() { +SecurePair.prototype.destroy = function() { var self = this; - if (!this._done) { - this._done = true; - this._ssl.error = null; - this._ssl.close(); - this._ssl = null; + if (!this._doneFlag) { + this._doneFlag = true; + this.ssl.error = null; + this.ssl.close(); + this.ssl = null; self.encrypted.writable = self.encrypted.readable = false; self.cleartext.writable = self.cleartext.readable = false; @@ -612,23 +603,21 @@ SecurePair.prototype._destroy = function() { self.cleartext.emit('close'); }); } - - this._cycle(); }; -SecurePair.prototype._error = function() { +SecurePair.prototype.error = function() { if (!this._secureEstablished) { - this._destroy(); + this.destroy(); } else { - var err = this._ssl.error; - this._ssl.error = null; + var err = this.ssl.error; + this.ssl.error = null; if (this._isServer && this._rejectUnauthorized && /peer did not return a certificate/.test(err.message)) { // Not really an error. - this._destroy(); + this.destroy(); } else { this.cleartext.emit('error', err); } @@ -749,13 +738,13 @@ function Server(/* [options], listener */) { cleartext._controlReleased = true; self.emit('secureConnection', pair.cleartext, pair.encrypted); } else { - var verifyError = pair._ssl.verifyError(); + var verifyError = pair.ssl.verifyError(); if (verifyError) { pair.cleartext.authorizationError = verifyError; if (self.rejectUnauthorized) { socket.destroy(); - pair._destroy(); + pair.destroy(); } else { cleartext._controlReleased = true; self.emit('secureConnection', pair.cleartext, pair.encrypted); @@ -851,7 +840,7 @@ exports.connect = function(port /* host, options, cb */) { socket.connect(port, host); pair.on('secure', function() { - var verifyError = pair._ssl.verifyError(); + var verifyError = pair.ssl.verifyError(); if (verifyError) { cleartext.authorized = false;