Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nodejs/node.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorBert Belder <bertbelder@gmail.com>2011-01-05 02:41:59 +0300
committerBert Belder <bertbelder@gmail.com>2011-01-05 02:41:59 +0300
commit3c330b05b10ffabe1b45d06efadc1f4e49830dc7 (patch)
treefd000a35785793332a3b9490ae55c50b4d91ba3a /lib
parentc3ffbf219ca8049c901ec58d4d4b3df5b96c5097 (diff)
parent131546e7339d6960ea91629915468c6f04a33cf7 (diff)
Merge branch 'master' of git://github.com/ry/node
Conflicts: src/node.cc src/node.js
Diffstat (limited to 'lib')
-rw-r--r--lib/_debugger.js750
-rw-r--r--lib/fs.js3
-rw-r--r--lib/http.js13
-rw-r--r--lib/https.js22
-rw-r--r--lib/net.js21
-rw-r--r--lib/readline.js47
-rw-r--r--lib/repl.js7
-rw-r--r--lib/tls.js31
8 files changed, 853 insertions, 41 deletions
diff --git a/lib/_debugger.js b/lib/_debugger.js
new file mode 100644
index 00000000000..b0a539e7ffc
--- /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 b9e6b2d7166..af18877a8d2 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 0b4a0e2212f..960d28aaa36 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 00000000000..c3e17a36027
--- /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 d29984668a3..832954aa3e8 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 341b20b4795..6d588fd5b89 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 aef0e9e2d3b..c425603ef51 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 433d33958c4..41197f385c3 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();
}
};