diff --git a/AUTHORS b/AUTHORS
index 625580c405..c5a9261d21 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -148,3 +148,6 @@ Michael W
- Unstable: 2010.12.16 - node-v0.3.2.tar.gz - (Documentation) + Unstable: 2011.01.02 + node-v0.3.3.tar.gz + (Documentation)
diff --git a/lib/_debugger.js b/lib/_debugger.js new file mode 100644 index 0000000000..b0a539e7ff --- /dev/null +++ b/lib/_debugger.js @@ -0,0 +1,750 @@ +var net = require('net'); +var readline = require('readline'); +var inherits = require('util').inherits; +var spawn = require('child_process').spawn; + +exports.port = 5858; + +exports.start = function () { + var interface = new Interface(); +}; + + +var args = process.argv.slice(2); +args.unshift('--debug-brk'); + + + +// +// Parser/Serializer for V8 debugger protocol +// http://code.google.com/p/v8/wiki/DebuggerProtocol +// +// Usage: +// p = new Protocol(); +// +// p.onResponse = function (res) { +// // do stuff with response from V8 +// }; +// +// socket.setEncoding('utf8'); +// socket.on('data', function (s) { +// // Pass strings into the protocol +// p.execute(s); +// }); +// +// +function Protocol() { + this._newRes(); +} +exports.Protocol = Protocol; + + +Protocol.prototype._newRes = function(raw) { + this.res = { raw: raw || '', headers: {} }; + this.state = 'headers'; + this.reqSeq = 1; + this.execute(''); +}; + + +Protocol.prototype.execute = function(d) { + var res = this.res; + res.raw += d; + + switch (this.state) { + case 'headers': + var endHeaderIndex = res.raw.indexOf('\r\n\r\n'); + + if (endHeaderIndex < 0) break; + + var lines = res.raw.slice(0, endHeaderIndex).split('\r\n'); + for (var i = 0; i < lines.length; i++) { + var kv = lines[i].split(/: +/); + res.headers[kv[0]] = kv[1]; + } + + this.contentLength = +res.headers['Content-Length']; + this.bodyStartIndex = endHeaderIndex + 4; + + this.state = 'body'; + if (res.raw.length - this.bodyStartIndex < this.contentLength) break; + // pass thru + + case 'body': + if (res.raw.length - this.bodyStartIndex >= this.contentLength) { + res.body = + res.raw.slice(this.bodyStartIndex, + this.bodyStartIndex + this.contentLength); + // JSON parse body? + res.body = res.body.length ? JSON.parse(res.body) : {}; + + // Done! + this.onResponse(res); + + this._newRes(res.raw.slice(this.bodyStartIndex + this.contentLength)); + } + break; + + default: + throw new Error("Unknown state"); + break; + } +}; + + +Protocol.prototype.serialize = function(req) { + req.type = 'request'; + req.seq = this.reqSeq++; + var json = JSON.stringify(req); + return 'Content-Length: ' + json.length + '\r\n\r\n' + json; +}; + + +var NO_FRAME = -1; + +function Client() { + net.Stream.call(this); + var protocol = this.protocol = new Protocol(this); + this._reqCallbacks = []; + var socket = this; + + this.currentFrame = NO_FRAME; + this.currentSourceLine = -1; + this.currentSource = null; + this.handles = {}; + this.scripts = {}; + + // Note that 'Protocol' requires strings instead of Buffers. + socket.setEncoding('utf8'); + socket.on('data', function(d) { + protocol.execute(d); + }); + + protocol.onResponse = this._onResponse.bind(this); +}; +inherits(Client, net.Stream); +exports.Client = Client; + + +Client.prototype._addHandle = function(desc) { + if (typeof desc != 'object' || !desc.handle) throw new Error("bad type"); + this.handles[desc.handle] = desc; + + if (desc.type == 'script') { + this._addScript(desc); + } +}; + + +var natives = process.binding('natives'); + + +Client.prototype._addScript = function(desc) { + this.scripts[desc.id] = desc; + if (desc.name) { + desc.isNative = (desc.name.replace('.js', '') in natives) || + desc.name == 'node.js'; + } +}; + + +Client.prototype._removeScript = function(desc) { + this.scripts[desc.id] = undefined; +}; + + +Client.prototype._onResponse = function(res) { + for (var i = 0; i < this._reqCallbacks.length; i++) { + var cb = this._reqCallbacks[i]; + if (this._reqCallbacks[i].request_seq == res.body.request_seq) break; + } + + var self = this; + var handled = false; + + if (res.headers.Type == 'connect') { + // Request a list of scripts for our own storage. + self.reqScripts(); + self.emit('ready'); + handled = true; + + } else if (res.body && res.body.event == 'break') { + this.emit('break', res.body); + handled = true; + + } else if (res.body && res.body.event == 'afterCompile') { + this._addHandle(res.body.body.script); + handled = true; + + } else if (res.body && res.body.event == 'scriptCollected') { + // ??? + this._removeScript(res.body.body.script); + handled = true; + + } + + if (cb) { + this._reqCallbacks.splice(i, 1); + handled = true; + cb(res.body); + } + + if (!handled) this.emit('unhandledResponse', res.body); +}; + + +Client.prototype.req = function(req, cb) { + this.write(this.protocol.serialize(req)); + cb.request_seq = req.seq; + this._reqCallbacks.push(cb); +}; + + +Client.prototype.reqVersion = function(cb) { + this.req({ command: 'version' } , function (res) { + if (cb) cb(res.body.V8Version, res.running); + }); +}; + + +Client.prototype.reqEval = function(expression, cb) { + var self = this; + var req = { + command: 'evaluate', + arguments: { expression: expression } + }; + + + if (this.currentFrame == NO_FRAME) { + req.arguments.global = true; + } else { + req.arguments.frame = this.currentFrame; + } + + this.req(req, function (res) { + console.error('reqEval res ', res.body); + self._addHandle(res.body); + if (cb) cb(res.body); + }); +}; + + +// reqBacktrace(cb) +// TODO: from, to, bottom +Client.prototype.reqBacktrace = function(cb) { + this.req({ command: 'backtrace' } , function (res) { + if (cb) cb(res.body); + }); +}; + + +// Returns an array of objects like this: +// +// { handle: 11, +// type: 'script', +// name: 'node.js', +// id: 14, +// lineOffset: 0, +// columnOffset: 0, +// lineCount: 562, +// sourceStart: '(function(process) {\n\n ', +// sourceLength: 15939, +// scriptType: 2, +// compilationType: 0, +// context: { ref: 10 }, +// text: 'node.js (lines: 562)' } +// +Client.prototype.reqScripts = function(cb) { + var self = this; + this.req({ command: 'scripts' } , function (res) { + for (var i = 0; i < res.body.length; i++) { + self._addHandle(res.body[i]); + } + if (cb) cb(); + }); +}; + + +Client.prototype.reqContinue = function(cb) { + this.req({ command: 'continue' }, function (res) { + if (cb) cb(res); + }); +}; + +Client.prototype.listbreakpoints = function(cb) { + this.req({ command: 'listbreakpoints' }, function (res) { + if (cb) cb(res); + }); +}; + +// client.next(1, cb); +Client.prototype.step = function(action, count, cb) { + var req = { + command: 'continue', + arguments: { stepaction: action, stepcount: count } + }; + + this.req(req, function (res) { + if (cb) cb(res); + }); +}; + + + + +var helpMessage = "Commands: run, kill, print, step, next, " + + "continue, scripts, backtrace, version, quit"; + +function SourceUnderline(sourceText, position) { + if (!sourceText) return; + + // Create an underline with a caret pointing to the source position. If the + // source contains a tab character the underline will have a tab character in + // the same place otherwise the underline will have a space character. + var underline = ''; + for (var i = 0; i < position; i++) { + if (sourceText[i] == '\t') { + underline += '\t'; + } else { + underline += ' '; + } + } + underline += '^'; + + // Return the source line text with the underline beneath. + return sourceText + '\n' + underline; +} + + +function SourceInfo(body) { + var result = ''; + + if (body.script) { + if (body.script.name) { + result += body.script.name; + } else { + result += '[unnamed]'; + } + } + result += ':'; + result += body.sourceLine + 1; + + return result; +} + + +// This class is the readline-enabled debugger interface which is invoked on +// "node debug" +function Interface() { + var self = this; + var term = this.term = readline.createInterface(process.stdout); + var child; + var client; + var term; + + process.on('exit', function () { + self.killChild(); + }); + + this.stdin = process.openStdin(); + this.stdin.addListener('data', function(chunk) { + term.write(chunk); + }); + + term.setPrompt('debug> '); + term.prompt(); + + this.quitting = false; + + process.on('SIGINT', function () { + self.handleSIGINT(); + }); + + term.on('SIGINT', function () { + self.handleSIGINT(); + }); + + term.on('close', function () { + self.tryQuit(); + }); + + term.on('line', function(cmd) { + // trim whitespace + cmd = cmd.replace(/^\s*/, '').replace(/\s*$/, ''); + + if (cmd.length) { + self._lastCommand = cmd; + self.handleCommand(cmd); + } else { + self.handleCommand(self._lastCommand); + } + }); +} + + +Interface.prototype.handleSIGINT = function() { + if (this.paused) { + this.child.kill('SIGINT'); + } else { + this.tryQuit(); + } +}; + + +Interface.prototype.quit = function() { + if (this.quitting) return; + this.quitting = true; + this.killChild(); + this.term.close(); + process.exit(0); +}; + + +Interface.prototype.tryQuit = function() { + var self = this; + + if (self.child) { + self.quitQuestion(function (yes) { + if (yes) { + self.quit(); + } else { + self.term.prompt(); + } + }); + } else { + self.quit(); + } +}; + + +Interface.prototype.pause = function() { + this.paused = true; + this.stdin.pause(); + this.term.pause(); +}; + + +Interface.prototype.resume = function() { + if (!this.paused) return false + this.paused = false; + this.stdin.resume(); + this.term.resume(); + this.term.prompt(); + return true; +}; + + +Interface.prototype.handleBreak = function(r) { + var result = ''; + if (r.breakpoints) { + result += 'breakpoint'; + if (r.breakpoints.length > 1) { + result += 's'; + } + result += ' #'; + for (var i = 0; i < r.breakpoints.length; i++) { + if (i > 0) { + result += ', #'; + } + result += r.breakpoints[i]; + } + } else { + result += 'break'; + } + result += ' in '; + result += r.invocationText; + result += ', '; + result += SourceInfo(r); + result += '\n'; + result += SourceUnderline(r.sourceLineText, r.sourceColumn); + + this.client.currentSourceLine = r.sourceLine; + this.client.currentFrame = 0; + this.client.currentScript = r.script.name; + + console.log(result); + + if(!this.resume()) this.term.prompt(); +}; + + +Interface.prototype.handleCommand = function(cmd) { + var self = this; + + var client = this.client; + var term = this.term; + + if (cmd == 'quit' || cmd == 'q' || cmd == 'exit') { + self._lastCommand = null; + self.tryQuit(); + + } else if (/^r(un)?/.test(cmd)) { + self._lastCommand = null; + if (self.child) { + self.restartQuestion(function (yes) { + if (!yes) { + self._lastCommand = null; + term.prompt(); + } else { + console.log("restarting..."); + self.killChild(); + // XXX need to wait a little bit for the restart to work? + setTimeout(function () { + self.trySpawn(); + }, 1000); + } + }); + } else { + self.trySpawn(); + } + + } else if (/^help/.test(cmd)) { + console.log(helpMessage); + term.prompt(); + + } else if ('version' == cmd) { + if (!client) { + self.printNotConnected(); + return; + } + client.reqVersion(function (v) { + console.log(v); + term.prompt(); + }); + + } else if (/info +breakpoints/.test(cmd)) { + if (!client) { + self.printNotConnected(); + return; + } + client.listbreakpoints(function (res) { + console.log(res); + term.prompt(); + }); + + } else if (/^backtrace/.test(cmd) || /^bt/.test(cmd)) { + if (!client) { + self.printNotConnected(); + return; + } + client.reqBacktrace(function (bt) { + if (/full/.test(cmd)) { + console.log(bt); + } else if (bt.totalFrames == 0) { + console.log('(empty stack)'); + } else { + var result = ''; + for (j = 0; j < bt.frames.length; j++) { + if (j != 0) result += '\n'; + result += bt.frames[j].text; + } + console.log(result); + } + term.prompt(); + }); + + } else if (cmd == 'scripts' || cmd == 'scripts full') { + if (!client) { + self.printNotConnected(); + return; + } + self.printScripts(cmd.indexOf('full') > 0); + term.prompt(); + + } else if (/^c(ontinue)?/.test(cmd)) { + if (!client) { + self.printNotConnected(); + return; + } + + self.pause(); + client.reqContinue(function () { + self.resume(); + }); + + } else if (/^k(ill)?/.test(cmd)) { + if (!client) { + self.printNotConnected(); + return; + } + // kill + if (self.child) { + self.killQuestion(function (yes) { + if (yes) { + self.killChild(); + } else { + self._lastCommand = null; + } + }); + } else { + self.term.prompt(); + } + + } else if (/^next/.test(cmd) || /^n/.test(cmd)) { + if (!client) { + self.printNotConnected(); + return; + } + client.step('next', 1, function (res) { + // Wait for break point. (disable raw mode?) + }); + + } else if (/^step/.test(cmd) || /^s/.test(cmd)) { + if (!client) { + self.printNotConnected(); + return; + } + client.step('in', 1, function (res) { + // Wait for break point. (disable raw mode?) + }); + + } else if (/^print/.test(cmd) || /^p/.test(cmd)) { + if (!client) { + self.printNotConnected(); + return; + } + var i = cmd.indexOf(' '); + if (i < 0) { + console.log("print [expression]"); + term.prompt(); + } else { + cmd = cmd.slice(i); + client.reqEval(cmd, function (res) { + if (res) { + console.log(res.text); + } else { + console.log(res); + } + term.prompt(); + }); + } + + } else { + if (!/^\s*$/.test(cmd)) { + // If it's not all white-space print this error message. + console.log('Unknown command "%s". Try "help"', cmd); + } + term.prompt(); + } +}; + + + +Interface.prototype.yesNoQuestion = function(prompt, cb) { + var self = this; + self.resume(); + this.term.question(prompt, function (answer) { + if (/^y(es)?$/i.test(answer)) { + cb(true); + } else if (/^n(o)?$/i.test(answer)) { + cb(false); + } else { + console.log("Please answer y or n."); + self.restartQuestion(cb); + } + }); +}; + + +Interface.prototype.restartQuestion = function(cb) { + this.yesNoQuestion("The program being debugged has been started already.\n" + + "Start it from the beginning? (y or n) ", cb); +}; + + +Interface.prototype.killQuestion = function(cb) { + this.yesNoQuestion("Kill the program being debugged? (y or n) ", cb); +}; + + +Interface.prototype.quitQuestion = function(cb) { + this.yesNoQuestion("A debugging session is active. Quit anyway? (y or n) ", + cb); +}; + + +Interface.prototype.killChild = function() { + if (this.child) { + this.child.kill(); + this.child = null; + } + + if (this.client) { + this.client.destroy(); + this.client = null; + } + + this.resume(); +}; + + +Interface.prototype.trySpawn = function(cb) { + var self = this; + + this.killChild(); + + this.child = spawn(process.execPath, args, { customFds: [0, 1, 2] }); + + + this.pause(); + + setTimeout(function () { + process.stdout.write("connecting..."); + var client = self.client = new Client(); + client.connect(exports.port); + + client.once('ready', function () { + process.stdout.write("ok\r\n"); + + // since we did debug-brk, we're hitting a break point immediately + // continue before anything else. + client.reqContinue(function () { + if (cb) cb(); + }); + }); + + client.on('close', function () { + console.log("\nprogram terminated"); + self.client = null; + self.killChild(); + if (!self.quitting) self.term.prompt(); + }); + + client.on('unhandledResponse', function (res) { + console.log("\r\nunhandled res:"); + console.log(res); + self.term.prompt(); + }); + + client.on('break', function (res) { + self.handleBreak(res.body); + }); + }, 100); +}; + + +Interface.prototype.printNotConnected = function() { + console.log("Program not running. Try 'run'."); + this.term.prompt(); +}; + + +// argument full tells if it should display internal node scripts or not +Interface.prototype.printScripts = function(displayNatives) { + var client = this.client; + var text = ''; + for (var id in client.scripts) { + var script = client.scripts[id]; + if (typeof script == 'object' && script.name) { + if (displayNatives || script.name == client.currentScript || !script.isNative) { + text += script.name == client.currentScript ? '* ' : ' '; + var n = require('path').split(script.name); + text += n[n.length - 1] + '\n'; + } + } + } + process.stdout.write(text); +}; + + + diff --git a/lib/fs.js b/lib/fs.js index b9e6b2d716..af18877a8d 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -981,3 +981,6 @@ WriteStream.prototype.destroy = function(cb) { } }; +// There is no shutdown() for files. +WriteStream.prototype.destroySoon = WriteStream.prototype.end; + diff --git a/lib/http.js b/lib/http.js index 0b4a0e2212..960d28aaa3 100644 --- a/lib/http.js +++ b/lib/http.js @@ -6,8 +6,7 @@ var HTTPParser = process.binding('http_parser').HTTPParser; var debug; -var debugLevel = parseInt(process.env.NODE_DEBUG, 16); -if (debugLevel & 0x4) { +if (process.env.NODE_DEBUG && /http/.test(process.env.NODE_DEBUG)) { debug = function(x) { console.error('HTTP: %s', x); }; } else { debug = function() { }; @@ -734,13 +733,11 @@ function httpSocketSetup(socket) { // An array of outgoing messages for the socket. In pipelined connections // we need to keep track of the order they were sent. socket._outgoing = []; - socket.__destroyOnDrain = false; // NOTE: be sure not to use ondrain elsewhere in this file! socket.ondrain = function() { var message = socket._outgoing[0]; if (message) message.emit('drain'); - if (socket.__destroyOnDrain) socket.destroy(); }; } @@ -834,12 +831,7 @@ function connectionListener(socket) { if (message._last) { // No more messages to be pushed out. - // HACK: need way to do this with socket interface - if (socket._writeQueue.length) { - socket.__destroyOnDrain = true; //socket.end(); - } else { - socket.destroy(); - } + socket.destroySoon(); } else if (socket._outgoing.length) { // Push out the next message. @@ -872,6 +864,7 @@ function connectionListener(socket) { return false; // Not a HEAD response. (Not even a response!) }; } +exports._connectionListener = connectionListener; function Client() { diff --git a/lib/https.js b/lib/https.js new file mode 100644 index 0000000000..c3e17a3602 --- /dev/null +++ b/lib/https.js @@ -0,0 +1,22 @@ +var tls = require('tls'); +var http = require('http'); +var inherits = require('util').inherits; + + +function Server(opts, requestListener) { + if (!(this instanceof Server)) return new Server(opts, requestListener); + tls.Server.call(this, opts, http._connectionListener); + + if (requestListener) { + this.addListener('request', requestListener); + } +} +inherits(Server, tls.Server); + + +exports.Server = Server; + + +exports.createServer = function(opts, requestListener) { + return new Server(opts, requestListener); +}; diff --git a/lib/net.js b/lib/net.js index d29984668a..832954aa3e 100644 --- a/lib/net.js +++ b/lib/net.js @@ -5,9 +5,8 @@ var stream = require('stream'); var kMinPoolSpace = 128; var kPoolSize = 40 * 1024; -var debugLevel = parseInt(process.env.NODE_DEBUG, 16); var debug; -if (debugLevel & 0x2) { +if (process.env.NODE_DEBUG && /net/.test(process.env.NODE_DEBUG)) { debug = function(x) { util.error.apply(this, arguments); }; } else { debug = function() { }; @@ -555,6 +554,7 @@ Stream.prototype._onWritable = function() { if (this.flush()) { if (this._events && this._events['drain']) this.emit('drain'); if (this.ondrain) this.ondrain(); // Optimization + if (this.__destroyOnDrain) this.destroy(); } }; @@ -695,17 +695,26 @@ Stream.prototype.setTimeout = function(msecs) { Stream.prototype.pause = function() { - this._readWatcher.stop(); + if (this._readWatcher) this._readWatcher.stop(); }; Stream.prototype.resume = function() { if (this.fd === null) throw new Error('Cannot resume() closed Stream.'); - this._readWatcher.stop(); - this._readWatcher.set(this.fd, true, false); - this._readWatcher.start(); + if (this._readWatcher) { + this._readWatcher.stop(); + this._readWatcher.set(this.fd, true, false); + this._readWatcher.start(); + } }; +Stream.prototype.destroySoon = function() { + if (this.flush()) { + this.destroy(); + } else { + this.__destroyOnDrain = true; + } +}; Stream.prototype.destroy = function(exception) { // pool is shared between sockets, so don't need to free it here. diff --git a/lib/readline.js b/lib/readline.js index 341b20b479..6d588fd5b8 100644 --- a/lib/readline.js +++ b/lib/readline.js @@ -17,19 +17,6 @@ exports.createInterface = function(output, completer) { return new Interface(output, completer); }; -function writeFilter(stream) { - if (stream._writeFiltered) return; - stream._writeFiltered = true; - stream._normalWrite = stream.write; - stream.write = function(d) { - var args = Array.prototype.slice.call(arguments); - if (typeof d == 'string') { - args[0] = d.replace(/([^\r])\n|^\n/g, '$1\r\n'); - } - // TODO what about buffers? - return stream._normalWrite.apply(stream, args); - } -} function Interface(output, completer) { if (!(this instanceof Interface)) return new Interface(output, completer); @@ -49,9 +36,6 @@ function Interface(output, completer) { if (this.enabled) { // input refers to stdin - writeFilter(this.output); - writeFilter(process.stdout); - // Current line this.line = ''; @@ -105,10 +89,17 @@ Interface.prototype.prompt = function() { Interface.prototype.question = function(query, cb) { if (cb) { - this._oldPrompt = this._prompt; - this.setPrompt(query); - this._questionCallback = cb; - this.prompt(); + this.resume(); + if (this._questionCallback) { + this.output.write('\n'); + this.prompt(); + } else { + this._oldPrompt = this._prompt; + this.setPrompt(query); + this._questionCallback = cb; + this.output.write('\n'); + this.prompt(); + } } }; @@ -160,6 +151,8 @@ Interface.prototype._refreshLine = function() { Interface.prototype.close = function(d) { + if (this._closing) return; + this._closing = true; if (this.enabled) { tty.setRawMode(false); } @@ -168,6 +161,20 @@ Interface.prototype.close = function(d) { }; +Interface.prototype.pause = function() { + if (this.enabled) { + tty.setRawMode(false); + } +}; + + +Interface.prototype.resume = function() { + if (this.enabled) { + tty.setRawMode(true); + } +}; + + Interface.prototype.write = function(d) { if (this._closed) return; return this.enabled ? this._ttyWrite(d) : this._normalWrite(d); diff --git a/lib/repl.js b/lib/repl.js index aef0e9e2d3..c425603ef5 100644 --- a/lib/repl.js +++ b/lib/repl.js @@ -115,12 +115,17 @@ function REPLServer(prompt, stream) { // and statements e.g. // 'for (var i = 0; i < 10; i++) console.log(i);' - var ret; + var ret, success = false; try { // First we attempt to eval as expression with parens. // This catches '{a : 1}' properly. ret = vm.runInContext('(' + self.bufferedCommand + ')', context, 'repl'); + if (typeof ret !== 'function') success = true; } catch (e) { + success = false; + } + + if (!success) { // Now as statement without parens. ret = vm.runInContext(self.bufferedCommand, context, 'repl'); } diff --git a/lib/tls.js b/lib/tls.js index 433d33958c..41197f385c 100644 --- a/lib/tls.js +++ b/lib/tls.js @@ -6,9 +6,8 @@ var stream = require('stream'); var assert = process.assert; -var debugLevel = parseInt(process.env.NODE_DEBUG, 16); var debug; -if (debugLevel & 0x2) { +if (process.env.NODE_DEBUG && /tls/.test(process.env.NODE_DEBUG)) { debug = function() { util.error.apply(this, arguments); }; } else { debug = function() { }; @@ -78,6 +77,11 @@ CryptoStream.prototype.resume = function() { }; +CryptoStream.prototype.setTimeout = function(n) { + if (this.socket) this.socket.setTimeout(n); +}; + + function parseCertString (s) { // EG '/C=US/ST=CA/L=SF/O=Joyent/OU=Node.js/CN=ca1/emailAddress=ry@tinyclouds.org' var out = {}; @@ -135,6 +139,19 @@ CryptoStream.prototype.end = function(d) { }; +CryptoStream.prototype.destroySoon = function(err) { + if (this.pair._done) return; + + this.pair._cycle(); + + if (this._pending.length) { + this.__destroyOnDrain = true; + } else { + this.end(); + } +}; + + CryptoStream.prototype.destroy = function(err) { if (this.pair._done) return; this.pair._destroy(); @@ -184,8 +201,13 @@ CryptoStream.prototype._blow = function() { } while ((chunkBytes > 0) && (pool.used + bytesRead < pool.length)); if (bytesRead > 0) { - chunk = pool.slice(0, bytesRead); - this.emit('data', chunk); + if (this._events && this._events['data']) { + chunk = pool.slice(0, bytesRead); + this.emit('data', chunk); + } + + // Optimization: emit the original buffer with end points + if (this.ondata) this.ondata(pool, 0, bytesRead); } } while (bytesRead > 0 && this._writeState === true); }; @@ -240,6 +262,7 @@ CryptoStream.prototype._suck = function() { if (havePending && this._pending && this._pending.length === 0) { debug('drain'); this.emit('drain'); + if (this.__destroyOnDrain) this.end(); } }; diff --git a/src/node.cc b/src/node.cc index f42c82481e..1c0ad34fd4 100644 --- a/src/node.cc +++ b/src/node.cc @@ -93,6 +93,7 @@ static ev_idle tick_spinner; static bool need_tick_cb; static Persistent