Browse Source

Merge branch 'master' of git://github.com/ry/node

Conflicts:
	src/node.cc
	src/node.js
v0.7.4-release
Bert Belder 14 years ago
parent
commit
3c330b05b1
  1. 3
      AUTHORS
  2. 25
      ChangeLog
  3. 1
      benchmark/http_simple.js
  4. 1
      doc/api/_toc.markdown
  5. 1
      doc/api/all.markdown
  6. 73
      doc/api/debugger.markdown
  7. 5
      doc/api/streams.markdown
  8. 8
      doc/index.html
  9. 750
      lib/_debugger.js
  10. 3
      lib/fs.js
  11. 13
      lib/http.js
  12. 22
      lib/https.js
  13. 21
      lib/net.js
  14. 47
      lib/readline.js
  15. 7
      lib/repl.js
  16. 31
      lib/tls.js
  17. 86
      src/node.cc
  18. 30
      src/node.js
  19. 4
      src/node_stdio.cc
  20. 2
      src/node_version.h
  21. 166
      test/simple/test-debugger-client.js
  22. 44
      test/simple/test-https-simple.js
  23. 21
      test/simple/test-repl.js

3
AUTHORS

@ -148,3 +148,6 @@ Michael W <gcr@sneakygcr.net>
Sean Braithwaite <brapse@gmail.com>
Anders Conbere <aconbere@gmail.com>
Devin Torres <devin@devintorres.com>
Theo Schlossnagle <jesus@omniti.com>
Kai Chen <kaichenxyz@gmail.com>
Daniel C <333222@gmail.com>

25
ChangeLog

@ -1,4 +1,27 @@
2010.12.16, Version 0.3.2 (unstable)
2011.01.02, Version 0.3.3 (unstable)
* TLS improvements.
* url.parse(url, true) defaults query field to {} (Jeremy Martin)
* Upgrade V8 to 3.0.4
* Handle ECONNABORT properly (Theo Schlossnagle)
* Fix memory leaks (Tom Hughes)
* Add os.cpus(), os.freemem(), os.totalmem(), os.loadavg() and other
functions for OSX, Linux, and Cygwin. (Brian White)
* Fix REPL syntax error bug (GH-543), improve how REPL commands are
evaulated.
* Use process.stdin instead of process.openStdin().
* Disable TLS tests when node doesn't have OpenSSL.
2010.12.16, Version 0.3.2 (unstable), 4bb914bde9f3c2d6de00853353b6b8fc9c66143a
* Rip out the old (broken) TLS implementation introduce new tested
implementation and API. See docs. HTTPS not supported in this release.

1
benchmark/http_simple.js

@ -43,6 +43,7 @@ var server = http.createServer(function (req, res) {
if (command == "bytes") {
var n = parseInt(arg, 10)
debugger;
if (n <= 0)
throw "bytes called with n <= 0"
if (stored[n] === undefined) {

1
doc/api/_toc.markdown

@ -28,6 +28,7 @@
* [Assertion Testing](assert.html)
* [TTY](tty.html)
* [OS](os.html)
* [Debugger](debugger.html)
* Appendixes
* [Appendix 1: Recommended Third-party Modules](appendix_1.html)
* [Appendix 2: Deprecated API's](appendix_2.html)

1
doc/api/all.markdown

@ -29,6 +29,7 @@
@include assert
@include tty
@include os
@include debugger
# Appendixes
@include appendix_1

73
doc/api/debugger.markdown

@ -0,0 +1,73 @@
## Debugger
V8 comes with an extensive debugger which is accessable out-of-process via a
simple [TCP protocol](http://code.google.com/p/v8/wiki/DebuggerProtocol).
Node has a built-in client for this debugger. To use this, start Node with the
`debug` argument; a prompt will appear:
% node debug myscript.js
debug>
At this point `myscript.js` is not yet running. To start the script, enter
the command `run`. If everything works okay, the output should look like
this:
% node debug myscript.js
debug> run
debugger listening on port 5858
connecting...ok
Node's debugger client doesn't support the full range of commands, but
simple step and inspection is possible. By putting the statement `debugger;`
into the source code of your script, you will enable a breakpoint.
For example, suppose `myscript.js` looked like this:
// myscript.js
x = 5;
setTimeout(function () {
debugger;
console.log("world");
}, 1000);
console.log("hello");
Then once the debugger is run, it will break on line 4.
% ./node debug myscript.js
debug> run
debugger listening on port 5858
connecting...ok
hello
break in #<an Object>._onTimeout(), myscript.js:4
debugger;
^
debug> next
break in #<an Object>._onTimeout(), myscript.js:5
console.log("world");
^
debug> print x
5
debug> print 2+2
4
debug> next
world
break in #<an Object>._onTimeout() returning undefined, myscript.js:6
}, 1000);
^
debug> quit
A debugging session is active. Quit anyway? (y or n) y
%
The `print` command allows you to evaluate variables. The `next` command steps
over to the next line. There are a few other commands available and more to
come type `help` to see others.
### Advanced Usage
The V8 debugger can be enabled and accessed either by starting Node with
the `--debug` command-line flag or by signaling an existing Node process
with `SIGUSR1`.

5
doc/api/streams.markdown

@ -65,6 +65,11 @@ Resumes the incoming `'data'` events after a `pause()`.
Closes the underlying file descriptor. Stream will not emit any more events.
### stream.destroySoon()
After the write queue is drained, close the file descriptor.
### stream.pipe(destination, [options])
This is a `Stream.prototype` method available on all `Stream`s.

8
doc/index.html

@ -23,7 +23,7 @@
<li><a href="#about">About</a></li>
<li><a href="#links">Links</a></li>
<li><a href="#contributing">Contributing</a></li>
<li><a href="http://nodejs.org/docs/v0.3.2/api">v0.3.2 docs</a></li>
<li><a href="http://nodejs.org/docs/v0.3.3/api">v0.3.3 docs</a></li>
<li><a href="http://nodejs.org/docs/v0.2.6/api.html">v0.2.6 docs</a></li>
</ol>
</div>
@ -92,9 +92,9 @@ net.createServer(function (socket) {
</p>
<p>
Unstable: 2010.12.16
<a href="http://nodejs.org/dist/node-v0.3.2.tar.gz">node-v0.3.2.tar.gz</a>
(<a href="http://nodejs.org/docs/v0.3.2/api/index.html">Documentation</a>)
Unstable: 2011.01.02
<a href="http://nodejs.org/dist/node-v0.3.3.tar.gz">node-v0.3.3.tar.gz</a>
(<a href="http://nodejs.org/docs/v0.3.3/api/index.html">Documentation</a>)
</p>
<p>Historical: <a href="http://nodejs.org/dist">versions</a>, <a href="http://nodejs.org/docs">docs</a></p>

750
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);
};

3
lib/fs.js

@ -981,3 +981,6 @@ WriteStream.prototype.destroy = function(cb) {
}
};
// There is no shutdown() for files.
WriteStream.prototype.destroySoon = WriteStream.prototype.end;

13
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() {

22
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);
};

21
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.

47
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);

7
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');
}

31
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();
}
};

86
src/node.cc

@ -93,6 +93,7 @@ static ev_idle tick_spinner;
static bool need_tick_cb;
static Persistent<String> tick_callback_sym;
static ev_async enable_debug;
static ev_async eio_want_poll_notifier;
static ev_async eio_done_poll_notifier;
static ev_idle eio_poller;
@ -1909,7 +1910,42 @@ static void SignalExit(int signal) {
}
static void EnableDebugSignalHandler(int signal) {
// can't do much here, marshal this back into the main thread where we'll
// enable the debugger.
ev_async_send(EV_DEFAULT_UC_ &enable_debug);
}
static void EnableDebug(bool wait_connect) {
// Start the debug thread and it's associated TCP server on port 5858.
bool r = Debug::EnableAgent("node " NODE_VERSION, debug_port);
if (wait_connect) {
// Set up an empty handler so v8 will not continue until a debugger
// attaches. This is the same behavior as Debug::EnableAgent(_,_,true)
// except we don't break at the beginning of the script.
// see Debugger::StartAgent in debug.cc of v8/src
Debug::SetMessageHandler2(node::DebugBreakMessageHandler);
}
// Crappy check that everything went well. FIXME
assert(r);
// Print out some information.
fprintf(stderr, "debugger listening on port %d\r\n", debug_port);
}
static void EnableDebug2(EV_P_ ev_async *watcher, int revents) {
assert(watcher == &enable_debug);
assert(revents == EV_ASYNC);
EnableDebug(false);
}
#ifdef __POSIX__
static int RegisterSignalHandler(int signal, void (*handler)(int)) {
struct sigaction sa;
@ -2022,37 +2058,31 @@ int Start(int argc, char *argv[]) {
V8::SetFatalErrorHandler(node::OnFatalError);
// Initialize the async watcher for receiving messages from the debug
// thread and marshal it into the main thread. DebugMessageCallback()
// is called from the main thread to execute a random bit of javascript
// - which will give V8 control so it can handle whatever new message
// had been received on the debug thread.
ev_async_init(&node::debug_watcher, node::DebugMessageCallback);
ev_set_priority(&node::debug_watcher, EV_MAXPRI);
// Set the callback DebugMessageDispatch which is called from the debug
// thread.
Debug::SetDebugMessageDispatchHandler(node::DebugMessageDispatch);
// Start the async watcher.
ev_async_start(EV_DEFAULT_UC_ &node::debug_watcher);
// unref it so that we exit the event loop despite it being active.
ev_unref(EV_DEFAULT_UC);
// If the --debug flag was specified then initialize the debug thread.
if (node::use_debug_agent) {
// Initialize the async watcher for receiving messages from the debug
// thread and marshal it into the main thread. DebugMessageCallback()
// is called from the main thread to execute a random bit of javascript
// - which will give V8 control so it can handle whatever new message
// had been received on the debug thread.
ev_async_init(&node::debug_watcher, node::DebugMessageCallback);
ev_set_priority(&node::debug_watcher, EV_MAXPRI);
// Set the callback DebugMessageDispatch which is called from the debug
// thread.
Debug::SetDebugMessageDispatchHandler(node::DebugMessageDispatch);
// Start the async watcher.
ev_async_start(EV_DEFAULT_UC_ &node::debug_watcher);
// unref it so that we exit the event loop despite it being active.
EnableDebug(debug_wait_connect);
} else {
RegisterSignalHandler(SIGUSR1, EnableDebugSignalHandler);
ev_async_init(&enable_debug, EnableDebug2);
ev_async_start(EV_DEFAULT_UC_ &enable_debug);
ev_unref(EV_DEFAULT_UC);
// Start the debug thread and it's associated TCP server on port 5858.
bool r = Debug::EnableAgent("node " NODE_VERSION, node::debug_port);
if (node::debug_wait_connect) {
// Set up an empty handler so v8 will not continue until a debugger
// attaches. This is the same behavior as Debug::EnableAgent(_,_,true)
// except we don't break at the beginning of the script.
// see Debugger::StartAgent in debug.cc of v8/src
Debug::SetMessageHandler2(node::DebugBreakMessageHandler);
}
// Crappy check that everything went well. FIXME
assert(r);
// Print out some information.
printf("debugger listening on port %d\n", node::debug_port);
}
// Create the one and only Context.

30
src/node.js

@ -123,9 +123,8 @@
// Modules
var debugLevel = parseInt(process.env['NODE_DEBUG'], 16);
var debug;
if (debugLevel & 1) {
if (process.env.NODE_DEBUG && /module/.test(process.env.NODE_DEBUG)) {
debug = function(x) { console.error(x); };
} else {
debug = function() { };
@ -170,7 +169,7 @@
try {
var stats = fs.statSync(requestPath);
if (stats && !stats.isDirectory()) {
return requestPath;
return fs.realpathSync(requestPath);
}
} catch (e) {}
return false;
@ -278,6 +277,7 @@
if (!filename) {
throw new Error("Cannot find module '" + request + "'");
}
id = filename;
return [id, filename];
}
@ -545,15 +545,23 @@
}
if (process.argv[1]) {
// Load module
if ('/\\'.indexOf(process.argv[1].charAt(0)) < 0
&& process.argv[1].charAt(1) != ':'
&& !(/^http:\/\//).exec(process.argv[1])) {
process.argv[1] = path.join(cwd, process.argv[1]);
if (process.argv[1] == 'debug') {
// Start the debugger agent
var d = requireNative('_debugger');
d.start();
} else {
// Load module
if ('/\\'.indexOf(process.argv[1].charAt(0)) < 0
&& process.argv[1].charAt(1) != ':'
&& !(/^http:\/\//).exec(process.argv[1])) {
process.argv[1] = path.join(cwd, process.argv[1]);
}
// REMOVEME: nextTick should not be necessary. This hack to get
// test/simple/test-exception-handler2.js working.
process.nextTick(module.runMain);
}
// REMOVEME: nextTick should not be necessary. This hack to get
// test/simple/test-exception-handler2.js working.
process.nextTick(module.runMain);
} else if (process._eval) {
// -e, --eval

4
src/node_stdio.cc

@ -49,8 +49,8 @@ static int EnableRawMode(int fd) {
/* input modes: no break, no CR to NL, no parity check, no strip char,
* no start/stop output control. */
raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
/* output modes - disable post processing */
raw.c_oflag &= ~(OPOST);
/* output modes */
raw.c_oflag |= (ONLCR);
/* control modes - set 8 bit chars */
raw.c_cflag |= (CS8);
/* local modes - choing off, canonical off, no extended functions,

2
src/node_version.h

@ -6,7 +6,7 @@
#define NODE_MAJOR_VERSION 0
#define NODE_MINOR_VERSION 3
#define NODE_PATCH_VERSION 3
#define NODE_PATCH_VERSION 4
#define NODE_VERSION_IS_RELEASE 0
#ifndef NODE_STRINGIFY

166
test/simple/test-debugger-client.js

@ -0,0 +1,166 @@
var common = require('../common');
var assert = require('assert');
var debug = require('_debugger');
var spawn = require('child_process').spawn;
var resCount = 0;
var p = new debug.Protocol();
p.onResponse = function (res) {
resCount++;
};
p.execute("Type: connect\r\n" +
"V8-Version: 3.0.4.1\r\n" +
"Protocol-Version: 1\r\n" +
"Embedding-Host: node v0.3.3-pre\r\n" +
"Content-Length: 0\r\n\r\n");
assert.equal(1, resCount);
// Make sure split messages go in.
var parts = [];
parts.push('Content-Length: 336\r\n');
assert.equal(21, parts[0].length);
parts.push('\r\n');
assert.equal(2, parts[1].length);
var bodyLength = 0;
parts.push('{"seq":12,"type":"event","event":"break","body":' +
'{"invocationText":"#<a Server>');
assert.equal(78, parts[2].length);
bodyLength += parts[2].length;
parts.push('.[anonymous](req=#<an IncomingMessage>, res=#<a ServerResponse>)",' +
'"sourceLine"');
assert.equal(78, parts[3].length);
bodyLength += parts[3].length;
parts.push(':45,"sourceColumn":4,"sourceLineText":" debugger;","script":' +
'{"id":24,"name":"/home/ryan/projects/node/benchmark/http_simple.js",' +
'"lineOffset":0,"columnOffset":0,"lineCount":98}}}');
assert.equal(180, parts[4].length);
bodyLength += parts[4].length;
assert.equal(336, bodyLength);
for (var i = 0; i < parts.length; i++) {
p.execute(parts[i]);
}
assert.equal(2, resCount);
// Make sure that if we get backed up, we still manage to get all the
// messages
var d = 'Content-Length: 466\r\n\r\n' +
'{"seq":10,"type":"event","event":"afterCompile","success":true,' +
'"body":{"script":{"handle":1,"type":"script","name":"dns.js",' +
'"id":34,"lineOffset":0,"columnOffset":0,"lineCount":241,"sourceStart":' +
'"(function (module, exports, require) {var dns = process.binding(\'cares\')' +
';\\nvar ne","sourceLength":6137,"scriptType":2,"compilationType":0,' +
'"context":{"ref":0},"text":"dns.js (lines: 241)"}},"refs":[{"handle":0' +
',"type":"context","text":"#<a ContextMirror>"}],"running":true}' +
'Content-Length: 119\r\n\r\n' +
'{"seq":11,"type":"event","event":"scriptCollected","success":true,' +
'"body":{"script":{"id":26}},"refs":[],"running":true}';
p.execute(d);
assert.equal(4, resCount);
var expectedConnections = 0;
var tests = [];
function addTest (cb) {
expectedConnections++;
tests.push(cb);
}
addTest(function (client, done) {
console.error("requesting version");
client.reqVersion(function (v) {
console.log("version: %s", v);
assert.equal(process.versions.v8, v);
done();
});
});
addTest(function (client, done) {
console.error("requesting scripts");
client.reqScripts(function () {
console.error("got %d scripts", Object.keys(client.scripts).length);
var foundMainScript = false;
for (var k in client.scripts) {
var script = client.scripts[k];
if (script && script.name === 'node.js') {
foundMainScript = true;
break;
}
}
assert.ok(foundMainScript);
done();
});
});
addTest(function (client, done) {
console.error("eval 2+2");
client.reqEval("2+2", function (res) {
console.error(res);
assert.equal('4', res.text);
assert.equal(4, res.value);
done();
});
});
var connectCount = 0;
function doTest(cb, done) {
var nodeProcess = spawn(process.execPath,
['-e', 'setInterval(function () { console.log("blah"); }, 100);']);
nodeProcess.stdout.once('data', function () {
console.log(">>> new node process: %d", nodeProcess.pid);
process.kill(nodeProcess.pid, "SIGUSR1");
console.log(">>> signaling it with SIGUSR1");
});
var didTryConnect = false;
nodeProcess.stderr.setEncoding('utf8');
nodeProcess.stderr.on('data', function (data) {
if (didTryConnect == false && /debugger/.test(data)) {
didTryConnect = true;
// Wait for some data before trying to connect
var c = new debug.Client();
process.stdout.write(">>> connecting...");
c.connect(debug.port)
c.on('ready', function () {
connectCount++;
console.log("ready!");
cb(c, function () {
console.error(">>> killing node process %d\n\n", nodeProcess.pid);
nodeProcess.kill();
done();
});
});
}
});
}
function run () {
var t = tests[0];
if (!t) return;
doTest(t, function () {
tests.shift();
run();
});
}
run();
process.on('exit', function() {
assert.equal(expectedConnections, connectCount);
});

44
test/simple/test-https-simple.js

@ -0,0 +1,44 @@
if (!process.versions.openssl) {
console.error("Skipping because node compiled without OpenSSL.");
process.exit(0);
}
var common = require('../common');
var assert = require('assert');
var fs = require('fs');
var exec = require('child_process').exec;
var https = require('https');
var options = {
key: fs.readFileSync(common.fixturesDir + '/keys/agent1-key.pem'),
cert: fs.readFileSync(common.fixturesDir + '/keys/agent1-cert.pem')
};
var reqCount = 0;
var body = 'hello world\n';
var server = https.createServer(options, function (req, res) {
reqCount++;
console.log("got request");
res.writeHead(200, { 'content-type': 'text/plain' });
res.end(body);
})
function afterCurl (err, stdout, stderr) {
if (err) throw err;
server.close();
common.error(common.inspect(stdout));
assert.equal(body, stdout);
};
server.listen(common.PORT, function () {
var cmd = 'curl --insecure https://127.0.0.1:' + common.PORT + '/';
console.error("executing %j", cmd);
exec(cmd, afterCurl);
});
process.on('exit', function () {
assert.equal(1, reqCount);
});

21
test/simple/test-repl.js

@ -97,7 +97,26 @@ function error_test() {
// invalid input to JSON.parse error is special case of syntax error,
// should throw
{ client: client_unix, send: 'JSON.parse(\'{invalid: \\\'json\\\'}\');',
expect: /^SyntaxError: Unexpected token ILLEGAL/ }
expect: /^SyntaxError: Unexpected token ILLEGAL/ },
// Named functions can be used:
{ client: client_unix, send: 'function blah() { return 1; }',
expect: prompt_unix },
{ client: client_unix, send: 'blah()',
expect: "1\n" + prompt_unix },
// Multiline object
{ client: client_unix, send: '{ a: ',
expect: prompt_multiline },
{ client: client_unix, send: '1 }',
expect: "{ a: 1 }" },
// Multiline anonymous function with comment
{ client: client_unix, send: '(function () {',
expect: prompt_multiline },
{ client: client_unix, send: '// blah',
expect: prompt_multiline },
{ client: client_unix, send: 'return 1;',
expect: prompt_multiline },
{ client: client_unix, send: '})()',
expect: "1" },
]);
}

Loading…
Cancel
Save