From 61785afb3d22133fb1968ac7c0274999d4e432eb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Felix=20Geisend=C3=B6rfer?=
Date: Tue, 2 Mar 2010 23:12:52 +0100
Subject: [PATCH 01/31] Initial write stream implementation
---
lib/fs.js | 87 +++++++++++++++++++++++++++
test/simple/test-file-write-stream.js | 47 +++++++++++++++
2 files changed, 134 insertions(+)
create mode 100644 test/simple/test-file-write-stream.js
diff --git a/lib/fs.js b/lib/fs.js
index 5687424dc2..02701f687f 100644
--- a/lib/fs.js
+++ b/lib/fs.js
@@ -375,3 +375,90 @@ exports.realpath = function(path, callback) {
callback(null, normalize(path));
});
}
+
+exports.fileWriteStream = function(path, options) {
+ return new FileWriteStream(path, options);
+};
+
+var FileWriteStream = exports.FileWriteStream = function(path, options) {
+ this.path = path;
+ this.fd = null;
+ this.closed = false;
+
+ this.flags = 'w';
+ this.encoding = 'binary';
+ this.mode = 0666;
+
+ process.mixin(this, options || {});
+
+ var
+ self = this,
+ queue = [],
+ busy = false;
+
+ queue.push([fs.open, this.path, this.flags, this.mode]);
+
+ function pump() {
+ if (busy) {
+ return;
+ }
+
+ var args = queue.shift();
+ if (!args) {
+ return self.emit('drain');
+ }
+
+ busy = true;
+
+ var method = args.shift();
+
+ args.push(function(err) {
+ busy = false;
+
+ if (err) {
+ self.emit('error', err);
+ return;
+ }
+
+ // save reference for file pointer
+ if (method === fs.open) {
+ self.fd = arguments[1];
+ self.emit('open', self.fd);
+ }
+
+ // stop pumping after close
+ if (method === fs.close) {
+ self.emit('close');
+ return;
+ }
+
+ pump();
+ });
+
+ // Inject the file pointer
+ if (method !== fs.open) {
+ args.unshift(self.fd);
+ }
+
+ method.apply(null, args);
+ };
+
+ this.write = function(data) {
+ if (this.closed) {
+ throw new Error('stream already closed');
+ }
+
+ queue.push([fs.write, data, undefined, this.encoding]);
+ pump();
+ return false;
+ };
+
+ this.close = function() {
+ this.closed = true;
+ queue.push([fs.close,]);
+ pump();
+ };
+
+ pump();
+};
+FileWriteStream.prototype.__proto__ = process.EventEmitter.prototype;
diff --git a/test/simple/test-file-write-stream.js b/test/simple/test-file-write-stream.js
new file mode 100644
index 0000000000..669985e178
--- /dev/null
+++ b/test/simple/test-file-write-stream.js
@@ -0,0 +1,47 @@
+process.mixin(require('../common'));
+
+var
+ fn = path.join(fixturesDir, "write.txt"),
+ file = fs.fileWriteStream(fn),
+
+ EXPECTED = '0123456789',
+
+ callbacks = {
+ open: -1,
+ drain: -2,
+ close: -1
+ };
+
+file
+ .addListener('open', function(fd) {
+ callbacks.open++;
+ assert.equal('number', typeof fd);
+ })
+ .addListener('drain', function() {
+ callbacks.drain++;
+ if (callbacks.drain == -1) {
+ assert.equal(EXPECTED, fs.readFileSync(fn));
+ file.write(EXPECTED);
+ } else if (callbacks.drain == 0) {
+ assert.equal(EXPECTED+EXPECTED, fs.readFileSync(fn));
+ file.close();
+ }
+ })
+ .addListener('close', function() {
+ callbacks.close++;
+ assert.throws(function() {
+ file.write('should not work anymore');
+ });
+
+ fs.unlinkSync(fn);
+ });
+
+for (var i = 0; i < 10; i++) {
+ assert.strictEqual(false, file.write(i));
+}
+
+process.addListener('exit', function() {
+ for (var k in callbacks) {
+ assert.equal(0, callbacks[k], k+' count off by '+callbacks[k]);
+ }
+});
\ No newline at end of file
From 18a70ffda13d3692aee34c097ad9330756d6479b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Felix=20Geisend=C3=B6rfer?=
Date: Tue, 2 Mar 2010 23:28:00 +0100
Subject: [PATCH 02/31] Tweaks
- Add 'writeable' property
- Renamed pump->flush
- Use sys.mixin instead of process.mixin
---
lib/fs.js | 27 +++++++++++++++------------
1 file changed, 15 insertions(+), 12 deletions(-)
diff --git a/lib/fs.js b/lib/fs.js
index 02701f687f..00f6168c15 100644
--- a/lib/fs.js
+++ b/lib/fs.js
@@ -1,3 +1,5 @@
+var sys = require('sys');
+
exports.Stats = process.Stats;
process.Stats.prototype._checkModeProperty = function (property) {
@@ -383,13 +385,13 @@ exports.fileWriteStream = function(path, options) {
var FileWriteStream = exports.FileWriteStream = function(path, options) {
this.path = path;
this.fd = null;
- this.closed = false;
+ this.writeable = true;
this.flags = 'w';
this.encoding = 'binary';
this.mode = 0666;
- process.mixin(this, options || {});
+ sys.mixin(this, options || {});
var
self = this,
@@ -398,7 +400,7 @@ var FileWriteStream = exports.FileWriteStream = function(path, options) {
queue.push([fs.open, this.path, this.flags, this.mode]);
- function pump() {
+ function flush() {
if (busy) {
return;
}
@@ -416,6 +418,7 @@ var FileWriteStream = exports.FileWriteStream = function(path, options) {
busy = false;
if (err) {
+ self.writeable = false;
self.emit('error', err);
return;
}
@@ -426,13 +429,13 @@ var FileWriteStream = exports.FileWriteStream = function(path, options) {
self.emit('open', self.fd);
}
- // stop pumping after close
+ // stop flushing after close
if (method === fs.close) {
self.emit('close');
return;
}
- pump();
+ flush();
});
// Inject the file pointer
@@ -444,21 +447,21 @@ var FileWriteStream = exports.FileWriteStream = function(path, options) {
};
this.write = function(data) {
- if (this.closed) {
- throw new Error('stream already closed');
+ if (!this.writeable) {
+ throw new Error('stream not writeable');
}
queue.push([fs.write, data, undefined, this.encoding]);
- pump();
+ flush();
return false;
};
this.close = function() {
- this.closed = true;
+ this.writeable = false;
queue.push([fs.close,]);
- pump();
+ flush();
};
- pump();
+ flush();
};
-FileWriteStream.prototype.__proto__ = process.EventEmitter.prototype;
+FileWriteStream.prototype.__proto__ = process.EventEmitter.prototype;
\ No newline at end of file
From 9be3df08283af081668c4cdfdd4297c5d15cbd04 Mon Sep 17 00:00:00 2001
From: Ryan Dahl
Date: Tue, 2 Mar 2010 17:35:01 -0800
Subject: [PATCH 03/31] Add sys.log()
---
doc/api.txt | 2 ++
lib/sys.js | 19 +++++++++++++++++++
2 files changed, 21 insertions(+)
diff --git a/doc/api.txt b/doc/api.txt
index 73f4f54c2e..9466fc6e45 100644
--- a/doc/api.txt
+++ b/doc/api.txt
@@ -187,6 +187,8 @@ Like +puts()+ but without the trailing new-line.
A synchronous output function. Will block the process and
output +string+ immediately to +stdout+.
++log(string)+::
+Output with timestamp.
+inspect(object, showHidden, depth)+ ::
diff --git a/lib/sys.js b/lib/sys.js
index 5ad68e4926..a828e5cef5 100644
--- a/lib/sys.js
+++ b/lib/sys.js
@@ -188,6 +188,25 @@ exports.p = function () {
}
};
+function pad (n) {
+ return n < 10 ? '0' + n.toString(10) : n.toString(10);
+}
+
+var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
+
+// 26 Feb 16:19:34
+function timestamp () {
+ var d = new Date();
+ return [ d.getDate()
+ , months[d.getMonth()]
+ , [pad(d.getHours()), pad(d.getMinutes()), pad(d.getSeconds())].join(':')
+ ].join(' ');
+}
+
+exports.log = function (msg) {
+ exports.puts(timestamp() + ' - ' + msg.toString());
+}
+
exports.exec = function (command, callback) {
var child = process.createChildProcess("/bin/sh", ["-c", command]);
var stdout = "";
From 5c602b750a64136cff86c6ffe8cf06a568c12717 Mon Sep 17 00:00:00 2001
From: Rasmus Andersson
Date: Wed, 3 Mar 2010 02:08:53 +0100
Subject: [PATCH 04/31] Rewrote realpath implementation solving all known
failing tests (also added a bunch of new test cases)
---
lib/fs.js | 190 +++++++++++++--------
test/simple/test-fs-realpath.js | 289 ++++++++++++++++++++++++++------
2 files changed, 360 insertions(+), 119 deletions(-)
diff --git a/lib/fs.js b/lib/fs.js
index 5687424dc2..3fd95de4d5 100644
--- a/lib/fs.js
+++ b/lib/fs.js
@@ -292,86 +292,132 @@ exports.unwatchFile = function (filename) {
// Realpath
var path = require('path');
-var dirname = path.dirname,
- basename = path.basename,
- normalize = path.normalize;
-
-function readlinkDeepSync(path, stats) {
- var seen_links = {}, resolved_link, stats, file_id;
- while (true) {
- stats = stats || exports.lstatSync(path);
- file_id = stats.dev.toString(32)+":"+stats.ino.toString(32);
- if (file_id in seen_links) {
- throw new Error("cyclic symbolic link at "+path);
- } else {
- seen_links[file_id] = 1;
- if (stats.isSymbolicLink()) {
- var newpath = exports.readlinkSync(path);
- if (newpath.charAt(0) === '/') {
- path = newpath;
+var normalize = path.normalize
+ normalizeArray = path.normalizeArray;
+
+exports.realpathSync = function (path) {
+ var seen_links = {}, knownHards = {}, buf, i = 0, part, x, stats;
+ if (path.charAt(0) !== '/') {
+ var cwd = process.cwd().split('/');
+ path = cwd.concat(path.split('/'));
+ path = normalizeArray(path);
+ i = cwd.length;
+ buf = [].concat(cwd);
+ } else {
+ path = normalizeArray(path.split('/'));
+ buf = [''];
+ }
+ for (; i 0) buf.splice(y, delta);
+ } else {
+ i--;
+ }
+ }
} else {
- var dir = dirname(path);
- path = (dir !== '') ? dir + '/' + newpath : newpath;
+ buf.push(path[i]);
+ knownHards[buf.join('/')] = true;
}
- } else {
- return normalize(path);
}
}
- stats = null;
}
+ return buf.join('/');
}
-function readlinkDeep(path, stats, callback) {
- var seen_links = {}, resolved_link, file_id;
- function next(stats) {
- file_id = stats.dev.toString(32)+":"+stats.ino.toString(32);
- if (file_id in seen_links) {
- callback(new Error("cyclic symbolic link at "+path));
- } else {
- seen_links[file_id] = 1;
- if (stats.isSymbolicLink()) {
- exports.readlink(path, function(err, newpath) {
- if (err) callback(err);
- if (newpath.charAt(0) === '/') {
- path = newpath;
- } else {
- var dir = dirname(path);
- path = (dir !== '') ? dir + '/' + newpath : newpath;
- }
- _next();
- });
- } else {
- callback(null, normalize(path));
- }
+
+exports.realpath = function (path, callback) {
+ var seen_links = {}, knownHards = {}, buf = [''], i = 0, part, x;
+ if (path.charAt(0) !== '/') {
+ // assumes cwd is canonical
+ var cwd = process.cwd().split('/');
+ path = cwd.concat(path.split('/'));
+ path = normalizeArray(path);
+ i = cwd.length-1;
+ buf = [].concat(cwd);
+ } else {
+ path = normalizeArray(path.split('/'));
+ }
+ function done(err) {
+ if (callback) {
+ if (!err) callback(err, buf.join('/'));
+ else callback(err);
}
}
- function _next() {
- exports.lstat(path, function(err, stats){
- if (err) callback(err);
- else next(stats);
- });
+ function next() {
+ if (++i === path.length) return done();
+ part = path.slice(0, i+1).join('/');
+ if (part.length === 0) return next();
+ if (part in knownHards) {
+ buf.push(path[i]);
+ next();
+ } else {
+ exports.lstat(part, function(err, stats){
+ if (err) return done(err);
+ if (stats.isSymbolicLink()) {
+ x = stats.dev.toString(32)+":"+stats.ino.toString(32);
+ if (x in seen_links)
+ return done(new Error("cyclic link at "+part));
+ seen_links[x] = true;
+ exports.readlink(part, function(err, npart){
+ if (err) return done(err);
+ part = npart;
+ if (part.charAt(0) === '/') {
+ // absolute
+ path = normalizeArray(part.split('/'));
+ buf = [''];
+ i = 0;
+ } else {
+ // relative
+ Array.prototype.splice.apply(path, [i, 1].concat(part.split('/')));
+ part = normalizeArray(path);
+ var y = 0, L = Math.max(path.length, part.length), delta;
+ for (; y 0) buf.splice(y, delta);
+ }
+ else {
+ i--; // resolve new node if needed
+ }
+ }
+ next();
+ }); // fs.readlink
+ }
+ else {
+ buf.push(path[i]);
+ knownHards[buf.join('/')] = true;
+ next();
+ }
+ }); // fs.lstat
+ }
}
- if (stats) next(stats);
- else _next();
-}
-
-exports.realpathSync = function(path) {
- var stats = exports.lstatSync(path);
- if (stats.isSymbolicLink())
- return readlinkDeepSync(path, stats);
- else
- return normalize(path);
-}
-
-exports.realpath = function(path, callback) {
- var resolved_path = path;
- if (!callback) return;
- exports.lstat(path, function(err, stats){
- if (err)
- callback(err);
- else if (stats.isSymbolicLink())
- readlinkDeep(path, stats, callback);
- else
- callback(null, normalize(path));
- });
+ next();
}
diff --git a/test/simple/test-fs-realpath.js b/test/simple/test-fs-realpath.js
index 384faebf4f..d6c1c3fc89 100644
--- a/test/simple/test-fs-realpath.js
+++ b/test/simple/test-fs-realpath.js
@@ -1,56 +1,251 @@
process.mixin(require("../common"));
-var async_completed = 0, async_expected = 0;
-
-// a. deep relative file symlink
-var dstPath = path.join(fixturesDir, 'cycles', 'root.js');
-var linkData1 = "../../cycles/root.js";
-var linkPath1 = path.join(fixturesDir, "nested-index", 'one', 'symlink1.js');
-try {fs.unlinkSync(linkPath1);}catch(e){}
-fs.symlinkSync(linkData1, linkPath1);
-
-var linkData2 = "../one/symlink1.js";
-var linkPath2 = path.join(fixturesDir, "nested-index", 'two', 'symlink1-b.js');
-try {fs.unlinkSync(linkPath2);}catch(e){}
-fs.symlinkSync(linkData2, linkPath2);
-
-// b. deep relative directory symlink
-var dstPath_b = path.join(fixturesDir, 'cycles', 'folder');
-var linkData1b = "../../cycles/folder";
-var linkPath1b = path.join(fixturesDir, "nested-index", 'one', 'symlink1-dir');
-try {fs.unlinkSync(linkPath1b);}catch(e){}
-fs.symlinkSync(linkData1b, linkPath1b);
-
-var linkData2b = "../one/symlink1-dir";
-var linkPath2b = path.join(fixturesDir, "nested-index", 'two', 'symlink12-dir');
-try {fs.unlinkSync(linkPath2b);}catch(e){}
-fs.symlinkSync(linkData2b, linkPath2b);
-
-assert.equal(fs.realpathSync(linkPath2), dstPath);
-assert.equal(fs.realpathSync(linkPath2b), dstPath_b);
-
-async_expected++;
-fs.realpath(linkPath2, function(err, rpath) {
- if (err) throw err;
- assert.equal(rpath, dstPath);
- async_completed++;
-});
+var async_completed = 0, async_expected = 0, unlink = [];
-async_expected++;
-fs.realpath(linkPath2b, function(err, rpath) {
- if (err) throw err;
- assert.equal(rpath, dstPath_b);
- async_completed++;
-});
+function asynctest(testBlock, args, callback, assertBlock) {
+ async_expected++;
+ testBlock.apply(testBlock, args.concat([function(err){
+ var ignoreError = false;
+ if (assertBlock) {
+ try {
+ ignoreError = assertBlock.apply(assertBlock,
+ Array.prototype.slice.call(arguments));
+ }
+ catch (e) {
+ err = e;
+ }
+ }
+ async_completed++;
+ callback(ignoreError ? null : err);
+ }]));
+}
-// todo: test shallow symlinks (file & dir)
-// todo: test non-symlinks (file & dir)
-// todo: test error on cyclic symlinks
+function bashRealpath(path, callback) {
+ exec("cd '"+path.replace("'","\\'")+"' && pwd -P",function (err, o) {
+ callback(err, o.trim());
+ });
+}
-process.addListener("exit", function () {
+// sub-tests:
+
+function test_simple_relative_symlink(callback) {
+ var entry = fixturesDir+'/cycles/symlink',
+ expected = fixturesDir+'/cycles/root.js';
+ [
+ [entry, 'root.js'],
+ ].forEach(function(t) {
+ try {fs.unlinkSync(t[0]);}catch(e){}
+ fs.symlinkSync(t[1], t[0]);
+ unlink.push(t[0]);
+ });
+ var result = fs.realpathSync(entry);
+ assert.equal(result, expected,
+ 'got '+inspect(result)+' expected '+inspect(expected));
+ asynctest(fs.realpath, [entry], callback, function(err, result){
+ assert.equal(result, expected,
+ 'got '+inspect(result)+' expected '+inspect(expected));
+ });
+}
+
+function test_simple_absolute_symlink(callback) {
+ bashRealpath(fixturesDir, function(err, fixturesAbsDir) {
+ if (err) return callback(err);
+ var entry = fixturesAbsDir+'/cycles/symlink',
+ expected = fixturesAbsDir+'/nested-index/one/index.js';
+ [
+ [entry, expected],
+ ].forEach(function(t) {
+ try {fs.unlinkSync(t[0]);}catch(e){}
+ fs.symlinkSync(t[1], t[0]);
+ unlink.push(t[0]);
+ });
+ var result = fs.realpathSync(entry);
+ assert.equal(result, expected,
+ 'got '+inspect(result)+' expected '+inspect(expected));
+ asynctest(fs.realpath, [entry], callback, function(err, result){
+ assert.equal(result, expected,
+ 'got '+inspect(result)+' expected '+inspect(expected));
+ });
+ });
+}
+
+function test_deep_relative_file_symlink(callback) {
+ var expected = path.join(fixturesDir, 'cycles', 'root.js');
+ var linkData1 = "../../cycles/root.js";
+ var linkPath1 = path.join(fixturesDir, "nested-index", 'one', 'symlink1.js');
try {fs.unlinkSync(linkPath1);}catch(e){}
- try {fs.unlinkSync(linkPath2);}catch(e){}
+ fs.symlinkSync(linkData1, linkPath1);
+
+ var linkData2 = "../one/symlink1.js";
+ var entry = path.join(fixturesDir, "nested-index", 'two', 'symlink1-b.js');
+ try {fs.unlinkSync(entry);}catch(e){}
+ fs.symlinkSync(linkData2, entry);
+ unlink.push(linkPath1);
+ unlink.push(entry);
+
+ assert.equal(fs.realpathSync(entry), expected);
+ asynctest(fs.realpath, [entry], callback, function(err, result){
+ assert.equal(result, expected,
+ 'got '+inspect(result)+' expected '+inspect(expected));
+ });
+}
+
+function test_deep_relative_dir_symlink(callback) {
+ var expected = path.join(fixturesDir, 'cycles', 'folder');
+ var linkData1b = "../../cycles/folder";
+ var linkPath1b = path.join(fixturesDir, "nested-index", 'one', 'symlink1-dir');
try {fs.unlinkSync(linkPath1b);}catch(e){}
- try {fs.unlinkSync(linkPath2b);}catch(e){}
+ fs.symlinkSync(linkData1b, linkPath1b);
+
+ var linkData2b = "../one/symlink1-dir";
+ var entry = path.join(fixturesDir, "nested-index", 'two', 'symlink12-dir');
+ try {fs.unlinkSync(entry);}catch(e){}
+ fs.symlinkSync(linkData2b, entry);
+ unlink.push(linkPath1b);
+ unlink.push(entry);
+
+ assert.equal(fs.realpathSync(entry), expected);
+
+ asynctest(fs.realpath, [entry], callback, function(err, result){
+ assert.equal(result, expected,
+ 'got '+inspect(result)+' expected '+inspect(expected));
+ });
+}
+
+function test_cyclic_link_protection(callback) {
+ var entry = fixturesDir+'/cycles/realpath-3a';
+ [
+ [entry, '../cycles/realpath-3b'],
+ [fixturesDir+'/cycles/realpath-3b', '../cycles/realpath-3c'],
+ [fixturesDir+'/cycles/realpath-3c', '../cycles/realpath-3a'],
+ ].forEach(function(t) {
+ try {fs.unlinkSync(t[0]);}catch(e){}
+ fs.symlinkSync(t[1], t[0]);
+ unlink.push(t[0]);
+ });
+ assert.throws(function(){ fs.realpathSync(entry); });
+ asynctest(fs.realpath, [entry], callback, function(err, result){
+ assert.ok(err && true);
+ return true;
+ });
+}
+
+function test_relative_input_cwd(callback) {
+ var p = fixturesDir.lastIndexOf('/');
+ var entrydir = fixturesDir.substr(0, p);
+ var entry = fixturesDir.substr(p+1)+'/cycles/realpath-3a';
+ var expected = fixturesDir+'/cycles/root.js';
+ [
+ [entry, '../cycles/realpath-3b'],
+ [fixturesDir+'/cycles/realpath-3b', '../cycles/realpath-3c'],
+ [fixturesDir+'/cycles/realpath-3c', 'root.js'],
+ ].forEach(function(t) {
+ var fn = t[0];
+ if (fn.charAt(0) !== '/') fn = entrydir + '/' + fn;
+ try {fs.unlinkSync(fn);}catch(e){}
+ fs.symlinkSync(t[1], fn);
+ unlink.push(fn);
+ });
+ var origcwd = process.cwd();
+ process.chdir(entrydir);
+ assert.equal(fs.realpathSync(entry), expected);
+ asynctest(fs.realpath, [entry], callback, function(err, result){
+ process.chdir(origcwd);
+ assert.equal(result, expected,
+ 'got '+inspect(result)+' expected '+inspect(expected));
+ return true;
+ });
+}
+
+function test_deep_symlink_mix(callback) {
+ // todo: check to see that fixturesDir is not rooted in the
+ // same directory as our test symlink.
+ // obtain our current realpath using bash (so we can test ourselves)
+ bashRealpath(fixturesDir, function(err, fixturesAbsDir) {
+ if (err) return callback(err);
+ /*
+ /tmp/node-test-realpath-f1 -> ../tmp/node-test-realpath-d1/foo
+ /tmp/node-test-realpath-d1 -> ../node-test-realpath-d2
+ /tmp/node-test-realpath-d2/foo -> ../node-test-realpath-f2
+ /tmp/node-test-realpath-f2
+ -> /node/test/fixtures/nested-index/one/realpath-c
+ /node/test/fixtures/nested-index/one/realpath-c
+ -> /node/test/fixtures/nested-index/two/realpath-c
+ /node/test/fixtures/nested-index/two/realpath-c -> ../../cycles/root.js
+ /node/test/fixtures/cycles/root.js (hard)
+ */
+ var entry = '/tmp/node-test-realpath-f1';
+ try {fs.unlinkSync('/tmp/node-test-realpath-d2/foo');}catch(e){}
+ try {fs.rmdirSync('/tmp/node-test-realpath-d2');}catch(e){}
+ fs.mkdirSync('/tmp/node-test-realpath-d2', 0700);
+ try {
+ [
+ [entry, '../tmp/node-test-realpath-d1/foo'],
+ ['/tmp/node-test-realpath-d1', '../tmp/node-test-realpath-d2'],
+ ['/tmp/node-test-realpath-d2/foo', '../node-test-realpath-f2'],
+ ['/tmp/node-test-realpath-f2', fixturesAbsDir+'/nested-index/one/realpath-c'],
+ [fixturesAbsDir+'/nested-index/one/realpath-c', fixturesAbsDir+'/nested-index/two/realpath-c'],
+ [fixturesAbsDir+'/nested-index/two/realpath-c', '../../cycles/root.js'],
+ ].forEach(function(t) {
+ //debug('setting up '+t[0]+' -> '+t[1]);
+ try {fs.unlinkSync(t[0]);}catch(e){}
+ fs.symlinkSync(t[1], t[0]);
+ unlink.push(t[0]);
+ });
+ } finally {
+ unlink.push('/tmp/node-test-realpath-d2');
+ }
+ var expected = fixturesAbsDir+'/cycles/root.js';
+ assert.equal(fs.realpathSync(entry), expected);
+ asynctest(fs.realpath, [entry], callback, function(err, result){
+ assert.equal(result, expected,
+ 'got '+inspect(result)+' expected '+inspect(expected));
+ return true;
+ });
+ });
+}
+
+function test_non_symlinks(callback) {
+ bashRealpath(fixturesDir, function(err, fixturesAbsDir) {
+ if (err) return callback(err);
+ var p = fixturesAbsDir.lastIndexOf('/');
+ var entrydir = fixturesAbsDir.substr(0, p);
+ var entry = fixturesAbsDir.substr(p+1)+'/cycles/root.js';
+ var expected = fixturesAbsDir+'/cycles/root.js';
+ var origcwd = process.cwd();
+ process.chdir(entrydir);
+ assert.equal(fs.realpathSync(entry), expected);
+ asynctest(fs.realpath, [entry], callback, function(err, result){
+ process.chdir(origcwd);
+ assert.equal(result, expected,
+ 'got '+inspect(result)+' expected '+inspect(expected));
+ return true;
+ });
+ });
+}
+
+// ----------------------------------------------------------------------------
+
+var tests = [
+ test_simple_relative_symlink,
+ test_simple_absolute_symlink,
+ test_deep_relative_file_symlink,
+ test_deep_relative_dir_symlink,
+ test_cyclic_link_protection,
+ test_relative_input_cwd,
+ test_deep_symlink_mix,
+ test_non_symlinks,
+];
+var numtests = tests.length;
+function runNextTest(err) {
+ if (err) throw err;
+ var test = tests.shift()
+ if (!test) puts(numtests+' subtests completed OK for fs.realpath');
+ else test(runNextTest);
+}
+runNextTest();
+
+process.addListener("exit", function () {
+ unlink.forEach(function(path){ try {fs.unlinkSync(path);}catch(e){} });
assert.equal(async_completed, async_expected);
});
From 0e844d3bcbcded9792774e38e8032ac493482e63 Mon Sep 17 00:00:00 2001
From: Ryan Dahl
Date: Tue, 2 Mar 2010 21:14:23 -0800
Subject: [PATCH 05/31] tcp.Connection.prototype.write should return boolean
---
deps/evcom/evcom.c | 10 ++-
deps/evcom/evcom.h | 2 +-
doc/api.txt | 4 +
src/node_net.cc | 8 +-
src/node_net.h | 4 +-
test/pummel/test-tcp-pause.js | 67 ++++++++++++++++
.../pummel/test-tcp-throttle-kernel-buffer.js | 55 -------------
test/pummel/test-tcp-throttle.js | 78 ++++++++-----------
8 files changed, 117 insertions(+), 111 deletions(-)
create mode 100644 test/pummel/test-tcp-pause.js
delete mode 100644 test/pummel/test-tcp-throttle-kernel-buffer.js
diff --git a/deps/evcom/evcom.c b/deps/evcom/evcom.c
index 1ee02a1f0a..1088181cef 100644
--- a/deps/evcom/evcom.c
+++ b/deps/evcom/evcom.c
@@ -1139,12 +1139,13 @@ void evcom_stream_force_close (evcom_stream *stream)
evcom_stream_detach(stream);
}
-void
+/* Returns the number of bytes flushed to the buffer */
+ssize_t
evcom_stream_write (evcom_stream *stream, const char *str, size_t len)
{
if (!WRITABLE(stream) || GOT_CLOSE(stream)) {
assert(0 && "Do not write to a closed stream");
- return;
+ return -1;
}
ssize_t sent = 0;
@@ -1188,7 +1189,7 @@ evcom_stream_write (evcom_stream *stream, const char *str, size_t len)
} /* TODO else { memcpy to last buffer on head } */
assert(sent >= 0);
- if ((size_t)sent == len) return; /* sent the whole buffer */
+ if ((size_t)sent == len) return sent; /* sent the whole buffer */
len -= sent;
str += sent;
@@ -1202,7 +1203,7 @@ evcom_stream_write (evcom_stream *stream, const char *str, size_t len)
if (ATTACHED(stream)) {
ev_io_start(D_LOOP_(stream) &stream->write_watcher);
}
- return;
+ return sent;
close:
stream->send_action = stream_send__close;
@@ -1210,6 +1211,7 @@ close:
if (ATTACHED(stream)) {
ev_io_start(D_LOOP_(stream) &stream->write_watcher);
}
+ return -1;
}
void
diff --git a/deps/evcom/evcom.h b/deps/evcom/evcom.h
index fd03a5bf05..3b07289fb5 100644
--- a/deps/evcom/evcom.h
+++ b/deps/evcom/evcom.h
@@ -194,7 +194,7 @@ void evcom_stream_read_resume (evcom_stream *);
void evcom_stream_read_pause (evcom_stream *);
void evcom_stream_reset_timeout (evcom_stream *, float timeout);
void evcom_stream_set_no_delay (evcom_stream *, int no_delay);
-void evcom_stream_write (evcom_stream *, const char *str, size_t len);
+ssize_t evcom_stream_write (evcom_stream *, const char *str, size_t len);
/* Once the write buffer is drained, evcom_stream_close will shutdown the
* writing end of the stream and will close the read end once the server
* replies with an EOF.
diff --git a/doc/api.txt b/doc/api.txt
index 9466fc6e45..1e7abcfb20 100644
--- a/doc/api.txt
+++ b/doc/api.txt
@@ -1493,6 +1493,10 @@ Sets the encoding (either +"ascii"+, +"utf8"+, or +"binary"+) for data that is r
Sends data on the connection. The second parameter specifies the encoding
in the case of a string--it defaults to ASCII because encoding to UTF8 is
rather slow.
++
+Returns +true+ if the entire data was flushed successfully to the kernel
+buffer. Returns +false+ if all or part of the data was queued in user memory.
++'drain'+ will be emitted when the buffer is again free.
+connection.close()+::
diff --git a/src/node_net.cc b/src/node_net.cc
index b9f739c0e5..bc0f67315b 100644
--- a/src/node_net.cc
+++ b/src/node_net.cc
@@ -633,12 +633,12 @@ Handle Connection::Write(const Arguments& args) {
}
char * buf = new char[len];
- ssize_t written = DecodeWrite(buf, len, args[0], enc);
- assert(written == len);
- connection->Write(buf, written);
+ ssize_t bufsize = DecodeWrite(buf, len, args[0], enc);
+ assert(bufsize == len);
+ ssize_t sent = connection->Write(buf, bufsize);
delete [] buf;
- return scope.Close(Integer::New(written));
+ return sent == bufsize ? True() : False();
}
void Connection::OnReceive(const void *buf, size_t len) {
diff --git a/src/node_net.h b/src/node_net.h
index 12e9e1dbd6..8ad1471a6a 100644
--- a/src/node_net.h
+++ b/src/node_net.h
@@ -62,8 +62,8 @@ class Connection : public EventEmitter {
return evcom_stream_connect(&stream_, address);
}
- void Write(const char *buf, size_t len) {
- evcom_stream_write(&stream_, buf, len);
+ ssize_t Write(const char *buf, size_t len) {
+ return evcom_stream_write(&stream_, buf, len);
}
void Close() {
diff --git a/test/pummel/test-tcp-pause.js b/test/pummel/test-tcp-pause.js
new file mode 100644
index 0000000000..0316e7797d
--- /dev/null
+++ b/test/pummel/test-tcp-pause.js
@@ -0,0 +1,67 @@
+process.mixin(require("../common"));
+tcp = require("tcp");
+N = 200;
+
+server = tcp.createServer(function (connection) {
+ function write (j) {
+ if (j >= N) {
+ connection.close();
+ return;
+ }
+ setTimeout(function () {
+ connection.write("C");
+ write(j+1);
+ }, 10);
+ }
+ write(0);
+});
+server.listen(PORT);
+
+
+recv = "";
+chars_recved = 0;
+
+client = tcp.createConnection(PORT);
+client.setEncoding("ascii");
+client.addListener("data", function (d) {
+ print(d);
+ recv += d;
+});
+
+setTimeout(function () {
+ chars_recved = recv.length;
+ puts("pause at: " + chars_recved);
+ assert.equal(true, chars_recved > 1);
+ client.pause();
+ setTimeout(function () {
+ puts("resume at: " + chars_recved);
+ assert.equal(chars_recved, recv.length);
+ client.resume();
+
+ setTimeout(function () {
+ chars_recved = recv.length;
+ puts("pause at: " + chars_recved);
+ client.pause();
+
+ setTimeout(function () {
+ puts("resume at: " + chars_recved);
+ assert.equal(chars_recved, recv.length);
+ client.resume();
+
+ }, 500);
+
+ }, 500);
+
+ }, 500);
+
+}, 500);
+
+client.addListener("end", function () {
+ server.close();
+ client.close();
+});
+
+process.addListener("exit", function () {
+ assert.equal(N, recv.length);
+ debug("Exit");
+});
diff --git a/test/pummel/test-tcp-throttle-kernel-buffer.js b/test/pummel/test-tcp-throttle-kernel-buffer.js
deleted file mode 100644
index 8914e3556d..0000000000
--- a/test/pummel/test-tcp-throttle-kernel-buffer.js
+++ /dev/null
@@ -1,55 +0,0 @@
-process.mixin(require("../common"));
-tcp = require("tcp");
-N = 30*1024; // 500kb
-
-puts("build big string");
-var body = "";
-for (var i = 0; i < N; i++) {
- body += "C";
-}
-
-puts("start server on port " + PORT);
-
-server = tcp.createServer(function (connection) {
- connection.addListener("connect", function () {
- connection.write(body);
- connection.close();
- });
-});
-server.listen(PORT);
-
-
-chars_recved = 0;
-npauses = 0;
-
-
-var paused = false;
-client = tcp.createConnection(PORT);
-client.setEncoding("ascii");
-client.addListener("data", function (d) {
- chars_recved += d.length;
- puts("got " + chars_recved);
- if (!paused) {
- client.pause();
- npauses += 1;
- paused = true;
- puts("pause");
- x = chars_recved;
- setTimeout(function () {
- assert.equal(chars_recved, x);
- client.resume();
- puts("resume");
- paused = false;
- }, 100);
- }
-});
-
-client.addListener("end", function () {
- server.close();
- client.close();
-});
-
-process.addListener("exit", function () {
- assert.equal(N, chars_recved);
- assert.equal(true, npauses > 2);
-});
diff --git a/test/pummel/test-tcp-throttle.js b/test/pummel/test-tcp-throttle.js
index 0316e7797d..32a7363722 100644
--- a/test/pummel/test-tcp-throttle.js
+++ b/test/pummel/test-tcp-throttle.js
@@ -1,60 +1,48 @@
process.mixin(require("../common"));
tcp = require("tcp");
-N = 200;
+N = 60*1024; // 30kb
+
+puts("build big string");
+var body = "";
+for (var i = 0; i < N; i++) {
+ body += "C";
+}
+
+puts("start server on port " + PORT);
server = tcp.createServer(function (connection) {
- function write (j) {
- if (j >= N) {
- connection.close();
- return;
- }
- setTimeout(function () {
- connection.write("C");
- write(j+1);
- }, 10);
- }
- write(0);
+ connection.addListener("connect", function () {
+ assert.equal(false, connection.write(body));
+ connection.close();
+ });
});
server.listen(PORT);
-recv = "";
chars_recved = 0;
+npauses = 0;
+
+var paused = false;
client = tcp.createConnection(PORT);
client.setEncoding("ascii");
client.addListener("data", function (d) {
- print(d);
- recv += d;
-});
-
-setTimeout(function () {
- chars_recved = recv.length;
- puts("pause at: " + chars_recved);
- assert.equal(true, chars_recved > 1);
- client.pause();
- setTimeout(function () {
- puts("resume at: " + chars_recved);
- assert.equal(chars_recved, recv.length);
- client.resume();
-
+ chars_recved += d.length;
+ puts("got " + chars_recved);
+ if (!paused) {
+ client.pause();
+ npauses += 1;
+ paused = true;
+ puts("pause");
+ x = chars_recved;
setTimeout(function () {
- chars_recved = recv.length;
- puts("pause at: " + chars_recved);
- client.pause();
-
- setTimeout(function () {
- puts("resume at: " + chars_recved);
- assert.equal(chars_recved, recv.length);
- client.resume();
-
- }, 500);
-
- }, 500);
-
- }, 500);
-
-}, 500);
+ assert.equal(chars_recved, x);
+ client.resume();
+ puts("resume");
+ paused = false;
+ }, 100);
+ }
+});
client.addListener("end", function () {
server.close();
@@ -62,6 +50,6 @@ client.addListener("end", function () {
});
process.addListener("exit", function () {
- assert.equal(N, recv.length);
- debug("Exit");
+ assert.equal(N, chars_recved);
+ assert.equal(true, npauses > 2);
});
From 548d59d07e21a54fb869345ff71ed08cbe2ffad1 Mon Sep 17 00:00:00 2001
From: Jacek Becela
Date: Wed, 3 Mar 2010 11:17:45 -0700
Subject: [PATCH 06/31] Fix fs.readFile handling encoding. Should close issue
#72
---
src/node.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/node.js b/src/node.js
index 221a5a2657..12686e674c 100644
--- a/src/node.js
+++ b/src/node.js
@@ -310,7 +310,7 @@ function readAll (fd, pos, content, encoding, callback) {
}
process.fs.readFile = function (path, encoding_, callback) {
- var encoding = typeof(encoding_) == 'string' ? encoding : 'utf8';
+ var encoding = typeof(encoding_) == 'string' ? encoding_ : 'utf8';
var callback_ = arguments[arguments.length - 1];
var callback = (typeof(callback_) == 'function' ? callback_ : null);
process.fs.open(path, process.O_RDONLY, 0666, function (err, fd) {
From 769a35024f399e051f16dd6c8e663f82ed37cbf2 Mon Sep 17 00:00:00 2001
From: Ryan Dahl
Date: Wed, 3 Mar 2010 10:45:58 -0800
Subject: [PATCH 07/31] Allow passing env to child process
---
src/node_child_process.cc | 8 +++++---
src/node_child_process.h | 2 +-
test/simple/test-child-process-env.js | 12 ++++++++++++
3 files changed, 18 insertions(+), 4 deletions(-)
create mode 100644 test/simple/test-child-process-env.js
diff --git a/src/node_child_process.cc b/src/node_child_process.cc
index 4dc3d437af..6902455dd0 100644
--- a/src/node_child_process.cc
+++ b/src/node_child_process.cc
@@ -9,6 +9,8 @@
#include
#include
+extern char **environ;
+
namespace node {
using namespace v8;
@@ -276,7 +278,7 @@ static inline int SetNonBlocking(int fd) {
// Note that args[0] must be the same as the "file" param. This is an
// execvp() requirement.
-int ChildProcess::Spawn(const char *file, char *const args[], char *const env[]) {
+int ChildProcess::Spawn(const char *file, char *const args[], char **env) {
assert(pid_ == 0);
assert(stdout_fd_ == -1);
assert(stderr_fd_ == -1);
@@ -315,11 +317,11 @@ int ChildProcess::Spawn(const char *file, char *const args[], char *const env[])
close(stdin_pipe[1]); // close write end
dup2(stdin_pipe[0], STDIN_FILENO);
+ environ = env;
+
execvp(file, args);
perror("execvp()");
_exit(127);
-
- // TODO search PATH and use: execve(file, argv, env);
}
// Parent.
diff --git a/src/node_child_process.h b/src/node_child_process.h
index 1cdd974d09..b37db9f36a 100644
--- a/src/node_child_process.h
+++ b/src/node_child_process.h
@@ -28,7 +28,7 @@ class ChildProcess : EventEmitter {
ChildProcess();
~ChildProcess();
- int Spawn(const char *file, char *const argv[], char *const env[]);
+ int Spawn(const char *file, char *const argv[], char **env);
int Write(const char *str, size_t len);
int Close(void);
int Kill(int sig);
diff --git a/test/simple/test-child-process-env.js b/test/simple/test-child-process-env.js
new file mode 100644
index 0000000000..4b5d902030
--- /dev/null
+++ b/test/simple/test-child-process-env.js
@@ -0,0 +1,12 @@
+process.mixin(require("../common"));
+child = process.createChildProcess('/usr/bin/env', [], {'HELLO' : 'WORLD'});
+response = "";
+
+child.addListener("output", function (chunk) {
+ puts("stdout: " + JSON.stringify(chunk));
+ if (chunk) response += chunk;
+});
+
+process.addListener('exit', function () {
+ assert.ok(response.indexOf('HELLO=WORLD') >= 0);
+});
From 0dba38eef0f026df3f232a4f5cf1cbbcaa57fc1b Mon Sep 17 00:00:00 2001
From: isaacs
Date: Wed, 3 Mar 2010 01:11:47 -0800
Subject: [PATCH 08/31] Fix a bug that was suppressing the error in setgid,
allowing it to fail silently.
---
src/node.cc | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/node.cc b/src/node.cc
index d760c72976..11c6a7de8e 100644
--- a/src/node.cc
+++ b/src/node.cc
@@ -506,7 +506,7 @@ static Handle SetGid(const Arguments& args) {
Local given_gid = args[0]->ToInteger();
int gid = given_gid->Int32Value();
int result;
- if ((result == setgid(gid)) != 0) {
+ if ((result = setgid(gid)) != 0) {
return ThrowException(Exception::Error(String::New(strerror(errno))));
}
return Undefined();
From 64d0e328e8a03251223dcbba8c4cc92319036d16 Mon Sep 17 00:00:00 2001
From: Ryan Dahl
Date: Wed, 3 Mar 2010 12:41:31 -0800
Subject: [PATCH 09/31] Remove unused EventEmitter object
---
lib/http.js | 8 +-------
1 file changed, 1 insertion(+), 7 deletions(-)
diff --git a/lib/http.js b/lib/http.js
index d9eab022ee..109351371d 100644
--- a/lib/http.js
+++ b/lib/http.js
@@ -317,10 +317,6 @@ ClientRequest.prototype.close = function () {
function createIncomingMessageStream (connection, incoming_listener) {
- var stream = new events.EventEmitter();
-
- stream.addListener("incoming", incoming_listener);
-
var incoming, field, value;
connection.addListener("messageBegin", function () {
@@ -372,7 +368,7 @@ function createIncomingMessageStream (connection, incoming_listener) {
incoming.statusCode = info.statusCode;
}
- stream.emit("incoming", incoming, info.should_keep_alive);
+ incoming_listener(incoming, info.should_keep_alive);
});
connection.addListener("body", function (chunk) {
@@ -382,8 +378,6 @@ function createIncomingMessageStream (connection, incoming_listener) {
connection.addListener("messageComplete", function () {
incoming.emit('end');
});
-
- return stream;
}
/* Returns true if the message queue is finished and the connection
From 9d4d232eaaa1ae0403eb4727ee03e8749fa1c2c3 Mon Sep 17 00:00:00 2001
From: Ryan Dahl
Date: Wed, 3 Mar 2010 12:49:06 -0800
Subject: [PATCH 10/31] Factor out a http.Client._reconnect() function
---
lib/http.js | 55 ++++++++++++++++++++++++++---------------------------
1 file changed, 27 insertions(+), 28 deletions(-)
diff --git a/lib/http.js b/lib/http.js
index 109351371d..33cab5143e 100644
--- a/lib/http.js
+++ b/lib/http.js
@@ -457,18 +457,35 @@ exports.createClient = function (port, host) {
var requests = [];
var currentRequest;
+ client.tcpSetSecure = client.setSecure;
+ client.setSecure = function(format_type, ca_certs, crl_list, private_key, certificate) {
+ secure_credentials.secure = true;
+ secure_credentials.format_type = format_type;
+ secure_credentials.ca_certs = ca_certs;
+ secure_credentials.crl_list = crl_list;
+ secure_credentials.private_key = private_key;
+ secure_credentials.certificate = certificate;
+ }
+
+ client._reconnect = function () {
+ if (client.readyState != "opening") {
+ //sys.debug("HTTP CLIENT: reconnecting readyState = " + client.readyState);
+ client.connect(port, host);
+ if (secure_credentials.secure) {
+ client.tcpSetSecure(secure_credentials.format_type,
+ secure_credentials.ca_certs,
+ secure_credentials.crl_list,
+ secure_credentials.private_key,
+ secure_credentials.certificate);
+ }
+ }
+ };
+
client._pushRequest = function (req) {
req.addListener("flush", function () {
if (client.readyState == "closed") {
//sys.debug("HTTP CLIENT request flush. reconnect. readyState = " + client.readyState);
- client.connect(port, host); // reconnect
- if (secure_credentials.secure) {
- client.tcpSetSecure(secure_credentials.format_type,
- secure_credentials.ca_certs,
- secure_credentials.crl_list,
- secure_credentials.private_key,
- secure_credentials.certificate);
- }
+ client._reconnect();
return;
}
//sys.debug("client flush readyState = " + client.readyState);
@@ -477,16 +494,6 @@ exports.createClient = function (port, host) {
requests.push(req);
};
- client.tcpSetSecure = client.setSecure;
- client.setSecure = function(format_type, ca_certs, crl_list, private_key, certificate) {
- secure_credentials.secure = true;
- secure_credentials.format_type = format_type;
- secure_credentials.ca_certs = ca_certs;
- secure_credentials.crl_list = crl_list;
- secure_credentials.private_key = private_key;
- secure_credentials.certificate = certificate;
- }
-
client.addListener("connect", function () {
client.resetParser();
currentRequest = requests.shift();
@@ -507,16 +514,8 @@ exports.createClient = function (port, host) {
//sys.debug("HTTP CLIENT onClose. readyState = " + client.readyState);
// If there are more requests to handle, reconnect.
- if (requests.length > 0 && client.readyState != "opening") {
- //sys.debug("HTTP CLIENT: reconnecting readyState = " + client.readyState);
- client.connect(port, host); // reconnect
- if (secure_credentials.secure) {
- client.tcpSetSecure(secure_credentials.format_type,
- secure_credentials.ca_certs,
- secure_credentials.crl_list,
- secure_credentials.private_key,
- secure_credentials.certificate);
- }
+ if (requests.length > 0) {
+ client._reconnect();
}
});
From d1500cee6e2750bb57f73f8e8bfd8ca034f96110 Mon Sep 17 00:00:00 2001
From: Ryan Dahl
Date: Wed, 3 Mar 2010 13:06:19 -0800
Subject: [PATCH 11/31] Store connection in OutgoingMessage
---
lib/http.js | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/lib/http.js b/lib/http.js
index 33cab5143e..ca196bc176 100644
--- a/lib/http.js
+++ b/lib/http.js
@@ -96,8 +96,10 @@ IncomingMessage.prototype._addHeaderLine = function (field, value) {
}
};
-function OutgoingMessage () {
- events.EventEmitter.call(this);
+function OutgoingMessage (connection) {
+ events.EventEmitter.call(this, connection);
+
+ this.connection = connection;
this.output = [];
this.outputEncodings = [];
@@ -246,7 +248,7 @@ OutgoingMessage.prototype.close = function () {
function ServerResponse (req) {
- OutgoingMessage.call(this);
+ OutgoingMessage.call(this, req.connection);
if (req.httpVersionMajor < 1 || req.httpVersionMinor < 1) {
this.use_chunked_encoding_by_default = false;
@@ -283,8 +285,8 @@ ServerResponse.prototype.writeHead = function (statusCode) {
ServerResponse.prototype.sendHeader = ServerResponse.prototype.writeHead;
ServerResponse.prototype.writeHeader = ServerResponse.prototype.writeHead;
-function ClientRequest (method, url, headers) {
- OutgoingMessage.call(this);
+function ClientRequest (connection, method, url, headers) {
+ OutgoingMessage.call(this, connection);
this.should_keep_alive = false;
if (method === "GET" || method === "HEAD") {
@@ -559,7 +561,7 @@ process.http.Client.prototype.request = function (method, url, headers) {
url = method;
method = null;
}
- var req = new ClientRequest(method || "GET", url, headers);
+ var req = new ClientRequest(this, method || "GET", url, headers);
this._pushRequest(req);
return req;
};
From d5ee777af2b6a89ab866c74171f6059c85079f88 Mon Sep 17 00:00:00 2001
From: Ryan Dahl
Date: Wed, 3 Mar 2010 15:34:57 -0800
Subject: [PATCH 12/31] Don't allow child process to clobber environ
---
src/node_child_process.cc | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/src/node_child_process.cc b/src/node_child_process.cc
index 6902455dd0..6c09ee0f00 100644
--- a/src/node_child_process.cc
+++ b/src/node_child_process.cc
@@ -302,6 +302,10 @@ int ChildProcess::Spawn(const char *file, char *const args[], char **env) {
return -3;
}
+ // Save environ in the case that we get it clobbered
+ // by the child process.
+ char **save_our_env = environ;
+
switch (pid_ = vfork()) {
case -1: // Error.
Shutdown();
@@ -324,6 +328,9 @@ int ChildProcess::Spawn(const char *file, char *const args[], char **env) {
_exit(127);
}
+ // Restore environment.
+ environ = save_our_env;
+
// Parent.
ev_child_set(&child_watcher_, pid_, 0);
From 409020a67d3388e4eda90af546e0fbe25b0adec3 Mon Sep 17 00:00:00 2001
From: Ryan Dahl
Date: Thu, 4 Mar 2010 09:58:31 -0800
Subject: [PATCH 13/31] Use kqueue on macintosh
---
benchmark/http_simple.rb | 8 +++++---
src/node.cc | 4 +++-
2 files changed, 8 insertions(+), 4 deletions(-)
diff --git a/benchmark/http_simple.rb b/benchmark/http_simple.rb
index ee33f57f1d..d1176e5abe 100644
--- a/benchmark/http_simple.rb
+++ b/benchmark/http_simple.rb
@@ -86,10 +86,12 @@ end
if $0 == __FILE__
#require DIR + '/../lib/ebb'
require 'rubygems'
- require 'rack'
- require 'thin'
- require 'ebb'
+# require 'rack'
# Rack::Handler::Mongrel.run(SimpleApp.new, :Port => 8000)
+
+ require 'thin'
Thin::Server.start("0.0.0.0", 8000, SimpleApp.new)
+
+# require 'ebb'
# Ebb::start_server(SimpleApp.new, :port => 8000)
end
diff --git a/src/node.cc b/src/node.cc
index 11c6a7de8e..205c8aeab5 100644
--- a/src/node.cc
+++ b/src/node.cc
@@ -1240,10 +1240,12 @@ int main(int argc, char *argv[]) {
evcom_ignore_sigpipe();
// Initialize the default ev loop.
-#ifdef __sun
+#if defined(__sun)
// TODO(Ryan) I'm experiencing abnormally high load using Solaris's
// EVBACKEND_PORT. Temporarally forcing select() until I debug.
ev_default_loop(EVBACKEND_SELECT);
+#elif defined(__APPLE__)
+ ev_default_loop(EVBACKEND_KQUEUE);
#else
ev_default_loop(EVFLAG_AUTO);
#endif
From 1e710cafa704cf451782e8c96e1b57418561600e Mon Sep 17 00:00:00 2001
From: Ryan Dahl
Date: Thu, 4 Mar 2010 11:51:39 -0800
Subject: [PATCH 14/31] Remove process.unloop()
---
src/node.cc | 14 --------------
1 file changed, 14 deletions(-)
diff --git a/src/node.cc b/src/node.cc
index 205c8aeab5..e6904a1e2f 100644
--- a/src/node.cc
+++ b/src/node.cc
@@ -424,19 +424,6 @@ static Handle Loop(const Arguments& args) {
return Undefined();
}
-static Handle Unloop(const Arguments& args) {
- HandleScope scope;
- int how = EVUNLOOP_ONE;
- if (args[0]->IsString()) {
- String::Utf8Value how_s(args[0]->ToString());
- if (0 == strcmp(*how_s, "all")) {
- how = EVUNLOOP_ALL;
- }
- }
- ev_unloop(EV_DEFAULT_ how);
- return Undefined();
-}
-
static Handle Chdir(const Arguments& args) {
HandleScope scope;
@@ -1049,7 +1036,6 @@ static void Load(int argc, char *argv[]) {
// define various internal methods
NODE_SET_METHOD(process, "loop", Loop);
- NODE_SET_METHOD(process, "unloop", Unloop);
NODE_SET_METHOD(process, "compile", Compile);
NODE_SET_METHOD(process, "_byteLength", ByteLength);
NODE_SET_METHOD(process, "reallyExit", Exit);
From e6dbf8d632b9fd6797db00e59b02d2179f221fb5 Mon Sep 17 00:00:00 2001
From: Ryan Dahl
Date: Thu, 4 Mar 2010 13:00:37 -0800
Subject: [PATCH 15/31] Revert "Remove process.unloop()"
People need this for backwards compatibility. Will be removed soon though!
This reverts commit 1e710cafa704cf451782e8c96e1b57418561600e.
---
src/node.cc | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/src/node.cc b/src/node.cc
index e6904a1e2f..205c8aeab5 100644
--- a/src/node.cc
+++ b/src/node.cc
@@ -424,6 +424,19 @@ static Handle Loop(const Arguments& args) {
return Undefined();
}
+static Handle Unloop(const Arguments& args) {
+ HandleScope scope;
+ int how = EVUNLOOP_ONE;
+ if (args[0]->IsString()) {
+ String::Utf8Value how_s(args[0]->ToString());
+ if (0 == strcmp(*how_s, "all")) {
+ how = EVUNLOOP_ALL;
+ }
+ }
+ ev_unloop(EV_DEFAULT_ how);
+ return Undefined();
+}
+
static Handle Chdir(const Arguments& args) {
HandleScope scope;
@@ -1036,6 +1049,7 @@ static void Load(int argc, char *argv[]) {
// define various internal methods
NODE_SET_METHOD(process, "loop", Loop);
+ NODE_SET_METHOD(process, "unloop", Unloop);
NODE_SET_METHOD(process, "compile", Compile);
NODE_SET_METHOD(process, "_byteLength", ByteLength);
NODE_SET_METHOD(process, "reallyExit", Exit);
From 9415ca909ea0e5e5c0b390fa60759952143beed3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Felix=20Geisend=C3=B6rfer?=
Date: Wed, 3 Mar 2010 12:39:17 +0100
Subject: [PATCH 16/31] Use process.mixin instead of sys.mixin
The process namespace has not been cleaned up yet, so mixin is still
attached to process.
---
lib/fs.js | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/lib/fs.js b/lib/fs.js
index 00f6168c15..24542ae0c4 100644
--- a/lib/fs.js
+++ b/lib/fs.js
@@ -1,5 +1,3 @@
-var sys = require('sys');
-
exports.Stats = process.Stats;
process.Stats.prototype._checkModeProperty = function (property) {
@@ -391,7 +389,7 @@ var FileWriteStream = exports.FileWriteStream = function(path, options) {
this.encoding = 'binary';
this.mode = 0666;
- sys.mixin(this, options || {});
+ process.mixin(this, options || {});
var
self = this,
From f6e00759effd1d550657aa6ce0208ee04eda87bd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Felix=20Geisend=C3=B6rfer?=
Date: Wed, 3 Mar 2010 12:39:41 +0100
Subject: [PATCH 17/31] Initial read stream implementation
---
lib/fs.js | 90 ++++++++++++++++++++++++++++
test/simple/test-file-read-stream.js | 54 +++++++++++++++++
2 files changed, 144 insertions(+)
create mode 100644 test/simple/test-file-read-stream.js
diff --git a/lib/fs.js b/lib/fs.js
index 24542ae0c4..ac13b2670f 100644
--- a/lib/fs.js
+++ b/lib/fs.js
@@ -376,6 +376,96 @@ exports.realpath = function(path, callback) {
});
}
+exports.fileReadStream = function(path, options) {
+ return new FileReadStream(path, options);
+};
+
+var FileReadStream = exports.FileReadStream = function(path, options) {
+ this.path = path;
+ this.fd = null;
+ this.readable = true;
+ this.paused = false;
+
+ this.flags = 'r';
+ this.encoding = 'binary';
+ this.mode = 0666;
+ this.bufferSize = 4 * 1024;
+
+ process.mixin(this, options || {});
+
+ var
+ self = this,
+ buffer = [];
+
+ function read() {
+ if (!self.readable || self.paused) {
+ return;
+ }
+
+ fs.read(self.fd, self.bufferSize, undefined, self.encoding, function(err, data, bytesRead) {
+ if (bytesRead === 0) {
+ self.emit('end');
+ self.close();
+ return;
+ }
+
+ // do not emit events if the stream is paused
+ if (self.paused) {
+ buffer.push(data);
+ return;
+ }
+
+ self.emit('data', data);
+ read();
+ });
+ }
+
+ fs.open(this.path, this.flags, this.mode, function(err, fd) {
+ if (err) {
+ self.emit('error', err);
+ return;
+ }
+
+ self.fd = fd;
+ self.emit('open', fd);
+ read();
+ });
+
+ this.close = function() {
+ this.readable = false;
+ fs.close(this.fd, function(err) {
+ if (err) {
+ self.emit('error', err);
+ return;
+ }
+
+ self.emit('close');
+ });
+ };
+
+ this.pause = function() {
+ this.paused = true;
+ };
+
+ this.resume = function() {
+ this.paused = false;
+
+ // emit any buffered read events before continuing
+ var data;
+ while (!this.paused) {
+ data = buffer.shift();
+ if (data === undefined) {
+ break;
+ }
+
+ self.emit('data', data);
+ }
+
+ read();
+ };
+};
+FileReadStream.prototype.__proto__ = process.EventEmitter.prototype;
+
exports.fileWriteStream = function(path, options) {
return new FileWriteStream(path, options);
};
diff --git a/test/simple/test-file-read-stream.js b/test/simple/test-file-read-stream.js
new file mode 100644
index 0000000000..3b358b5c5c
--- /dev/null
+++ b/test/simple/test-file-read-stream.js
@@ -0,0 +1,54 @@
+process.mixin(require('../common'));
+
+var
+ fn = path.join(fixturesDir, 'multipart.js'),
+ file = fs.fileReadStream(fn),
+
+ callbacks = {
+ open: -1,
+ end: -1,
+ close: -1
+ },
+
+ paused = false,
+
+ fileContent = '';
+
+file
+ .addListener('open', function(fd) {
+ callbacks.open++;
+ assert.equal('number', typeof fd);
+ assert.ok(file.readable);
+ })
+ .addListener('error', function(err) {
+ throw err;
+ })
+ .addListener('data', function(data) {
+ assert.ok(!paused);
+ fileContent += data;
+
+ paused = true;
+ file.pause();
+ assert.ok(file.paused);
+
+ setTimeout(function() {
+ paused = false;
+ file.resume();
+ assert.ok(!file.paused);
+ }, 10);
+ })
+ .addListener('end', function(chunk) {
+ callbacks.end++;
+ })
+ .addListener('close', function() {
+ callbacks.close++;
+ assert.ok(!file.readable);
+
+ assert.equal(fs.readFileSync(fn), fileContent);
+ });
+
+process.addListener('exit', function() {
+ for (var k in callbacks) {
+ assert.equal(0, callbacks[k], k+' count off by '+callbacks[k]);
+ }
+});
\ No newline at end of file
From b4fba5fe8ee5fe3a43405fbd8838feab5f259bc8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Felix=20Geisend=C3=B6rfer?=
Date: Thu, 4 Mar 2010 14:25:59 +0100
Subject: [PATCH 18/31] Simplify buffering
There is no way more than one read event would be buffered.
---
lib/fs.js | 16 +++++-----------
1 file changed, 5 insertions(+), 11 deletions(-)
diff --git a/lib/fs.js b/lib/fs.js
index ac13b2670f..de1bf94ce3 100644
--- a/lib/fs.js
+++ b/lib/fs.js
@@ -395,7 +395,7 @@ var FileReadStream = exports.FileReadStream = function(path, options) {
var
self = this,
- buffer = [];
+ buffer = null;
function read() {
if (!self.readable || self.paused) {
@@ -411,7 +411,7 @@ var FileReadStream = exports.FileReadStream = function(path, options) {
// do not emit events if the stream is paused
if (self.paused) {
- buffer.push(data);
+ buffer = data;
return;
}
@@ -450,15 +450,9 @@ var FileReadStream = exports.FileReadStream = function(path, options) {
this.resume = function() {
this.paused = false;
- // emit any buffered read events before continuing
- var data;
- while (!this.paused) {
- data = buffer.shift();
- if (data === undefined) {
- break;
- }
-
- self.emit('data', data);
+ if (buffer !== null) {
+ self.emit('data', buffer);
+ buffer = null;
}
read();
From 48562fa9389946ae7ac5b977004fc0d47e5af947 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Felix=20Geisend=C3=B6rfer?=
Date: Thu, 4 Mar 2010 22:06:06 +0100
Subject: [PATCH 19/31] Updated file streams
Read streams now only support forceClose()
Write streams support close() and forceClose()
---
lib/fs.js | 22 ++++++++++++++++++++--
test/simple/test-file-write-stream.js | 3 +++
2 files changed, 23 insertions(+), 2 deletions(-)
diff --git a/lib/fs.js b/lib/fs.js
index de1bf94ce3..cf133158e8 100644
--- a/lib/fs.js
+++ b/lib/fs.js
@@ -405,7 +405,7 @@ var FileReadStream = exports.FileReadStream = function(path, options) {
fs.read(self.fd, self.bufferSize, undefined, self.encoding, function(err, data, bytesRead) {
if (bytesRead === 0) {
self.emit('end');
- self.close();
+ self.forceClose();
return;
}
@@ -415,6 +415,11 @@ var FileReadStream = exports.FileReadStream = function(path, options) {
return;
}
+ // do not emit events anymore after we declared the stream unreadable
+ if (!self.readable) {
+ return;
+ }
+
self.emit('data', data);
read();
});
@@ -431,7 +436,7 @@ var FileReadStream = exports.FileReadStream = function(path, options) {
read();
});
- this.close = function() {
+ this.forceClose = function() {
this.readable = false;
fs.close(this.fd, function(err) {
if (err) {
@@ -544,6 +549,19 @@ var FileWriteStream = exports.FileWriteStream = function(path, options) {
flush();
};
+ this.forceClose = function() {
+ this.writeable = false;
+ fs.close(self.fd, function(err) {
+ if (err) {
+ self.emit('error', err);
+ return;
+ }
+
+ self.emit('close');
+ });
+ };
+
+
flush();
};
FileWriteStream.prototype.__proto__ = process.EventEmitter.prototype;
\ No newline at end of file
diff --git a/test/simple/test-file-write-stream.js b/test/simple/test-file-write-stream.js
index 669985e178..5f15bfe1c3 100644
--- a/test/simple/test-file-write-stream.js
+++ b/test/simple/test-file-write-stream.js
@@ -17,6 +17,9 @@ file
callbacks.open++;
assert.equal('number', typeof fd);
})
+ .addListener('error', function(err) {
+ throw err;
+ })
.addListener('drain', function() {
callbacks.drain++;
if (callbacks.drain == -1) {
From 0fcc94525a59df4ae1a90a7c3d4c62df0e19b36c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Felix=20Geisend=C3=B6rfer?=
Date: Fri, 5 Mar 2010 18:56:25 +0100
Subject: [PATCH 20/31] Renamed fileReadStream -> createReadStream
Did the same for fileWriteStream as well.
---
lib/fs.js | 4 ++--
test/simple/test-file-read-stream.js | 2 +-
test/simple/test-file-write-stream.js | 2 +-
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/lib/fs.js b/lib/fs.js
index cf133158e8..579199d93e 100644
--- a/lib/fs.js
+++ b/lib/fs.js
@@ -376,7 +376,7 @@ exports.realpath = function(path, callback) {
});
}
-exports.fileReadStream = function(path, options) {
+exports.createReadStream = function(path, options) {
return new FileReadStream(path, options);
};
@@ -465,7 +465,7 @@ var FileReadStream = exports.FileReadStream = function(path, options) {
};
FileReadStream.prototype.__proto__ = process.EventEmitter.prototype;
-exports.fileWriteStream = function(path, options) {
+exports.createWriteStream = function(path, options) {
return new FileWriteStream(path, options);
};
diff --git a/test/simple/test-file-read-stream.js b/test/simple/test-file-read-stream.js
index 3b358b5c5c..447629e41b 100644
--- a/test/simple/test-file-read-stream.js
+++ b/test/simple/test-file-read-stream.js
@@ -2,7 +2,7 @@ process.mixin(require('../common'));
var
fn = path.join(fixturesDir, 'multipart.js'),
- file = fs.fileReadStream(fn),
+ file = fs.createReadStream(fn),
callbacks = {
open: -1,
diff --git a/test/simple/test-file-write-stream.js b/test/simple/test-file-write-stream.js
index 5f15bfe1c3..89059033ab 100644
--- a/test/simple/test-file-write-stream.js
+++ b/test/simple/test-file-write-stream.js
@@ -2,7 +2,7 @@ process.mixin(require('../common'));
var
fn = path.join(fixturesDir, "write.txt"),
- file = fs.fileWriteStream(fn),
+ file = fs.createWriteStream(fn),
EXPECTED = '0123456789',
From 145fac2b56a29270c798996db508a9fc6338c1f2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Felix=20Geisend=C3=B6rfer?=
Date: Fri, 5 Mar 2010 19:24:20 +0100
Subject: [PATCH 21/31] Use sys inherits
Also use events.EventEmitter instead of process.EventEmitter.
---
lib/fs.js | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/lib/fs.js b/lib/fs.js
index 579199d93e..165ece8155 100644
--- a/lib/fs.js
+++ b/lib/fs.js
@@ -1,3 +1,7 @@
+var
+ sys = require('sys'),
+ events = require('events');
+
exports.Stats = process.Stats;
process.Stats.prototype._checkModeProperty = function (property) {
@@ -381,6 +385,8 @@ exports.createReadStream = function(path, options) {
};
var FileReadStream = exports.FileReadStream = function(path, options) {
+ events.EventEmitter.call(this);
+
this.path = path;
this.fd = null;
this.readable = true;
@@ -463,13 +469,15 @@ var FileReadStream = exports.FileReadStream = function(path, options) {
read();
};
};
-FileReadStream.prototype.__proto__ = process.EventEmitter.prototype;
+sys.inherits(FileReadStream, events.EventEmitter);
exports.createWriteStream = function(path, options) {
return new FileWriteStream(path, options);
};
var FileWriteStream = exports.FileWriteStream = function(path, options) {
+ events.EventEmitter.call(this);
+
this.path = path;
this.fd = null;
this.writeable = true;
@@ -564,4 +572,4 @@ var FileWriteStream = exports.FileWriteStream = function(path, options) {
flush();
};
-FileWriteStream.prototype.__proto__ = process.EventEmitter.prototype;
\ No newline at end of file
+sys.inherits(FileWriteStream, events.EventEmitter);
\ No newline at end of file
From 78c61000c20e0456a3d887bc397fe29815f2f9e5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Felix=20Geisend=C3=B6rfer?=
Date: Fri, 5 Mar 2010 19:53:59 +0100
Subject: [PATCH 22/31] Properly handle read errors
Also set readable to false if the initial fs.open call failed.
---
lib/fs.js | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/lib/fs.js b/lib/fs.js
index 165ece8155..56c739389b 100644
--- a/lib/fs.js
+++ b/lib/fs.js
@@ -409,6 +409,12 @@ var FileReadStream = exports.FileReadStream = function(path, options) {
}
fs.read(self.fd, self.bufferSize, undefined, self.encoding, function(err, data, bytesRead) {
+ if (err) {
+ self.emit('error', err);
+ self.readable = false;
+ return;
+ }
+
if (bytesRead === 0) {
self.emit('end');
self.forceClose();
@@ -434,6 +440,7 @@ var FileReadStream = exports.FileReadStream = function(path, options) {
fs.open(this.path, this.flags, this.mode, function(err, fd) {
if (err) {
self.emit('error', err);
+ self.readable = false;
return;
}
From a96b5c792e49c0e2e425ce14746276d1c59c45ff Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Felix=20Geisend=C3=B6rfer?=
Date: Fri, 5 Mar 2010 19:54:28 +0100
Subject: [PATCH 23/31] Documentation for FileReadStream
---
doc/api.txt | 41 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 41 insertions(+)
diff --git a/doc/api.txt b/doc/api.txt
index 73f4f54c2e..c4a0ec355c 100644
--- a/doc/api.txt
+++ b/doc/api.txt
@@ -783,6 +783,47 @@ Objects returned from +fs.stat()+ and +fs.lstat()+ are of this type.
+stats.isSocket()+:: ...
+=== +fs.FileReadStream+
+
+[cols="1,2,10",options="header"]
+|=========================================================
+|Event | Parameters | Notes
+
+|+"open"+ | +fd+ | The file descriptor was opened.
+|+"data"+ | +chunk+ | A chunk of data was read.
+|+"error"+ | +err+ | An error occured. This stops the stream.
+|+"end"+ | | The end of the file was reached.
+|+"close"+ | | The file descriptor was closed.
+|=========================================================
+
++fs.createReadStream(path, [options]);+ ::
+Returns a new FileReadStream object.
++
++options+ is an object with the following defaults:
++
+----------------------------------------
+{ "flags": "r"
+, "encoding": "binary"
+, "mode": 0666
+, "bufferSize": 4 * 1024
+}
+----------------------------------------
+
++readStream.readable+ ::
+A boolean that is +true+ by default, but turns +false+ after an +"error"+
+occured, the stream came to an "end", or +forceClose()+ was called.
+
++readStream.pause()+ ::
+Stops the stream from reading further data. No +"data"+ event will be fired
+until the stream is resumed.
+
++readStream.resume()+ ::
+Resumes the stream. Together with +pause()+ this useful to throttle reading.
+
++readStream.forceClose()+ ::
+Allows to close the stream before the +"end"+ is reached. No more events other
+than +"close"+ will be fired after this method has been called.
+
== HTTP
To use the HTTP server and client one must +require("http")+.
From dbf9e466bc263cc08b217403cdf4a14a66f82935 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Felix=20Geisend=C3=B6rfer?=
Date: Fri, 5 Mar 2010 20:04:19 +0100
Subject: [PATCH 24/31] Documentation for FileWriteStream
---
doc/api.txt | 39 +++++++++++++++++++++++++++++++++++++++
lib/fs.js | 1 -
2 files changed, 39 insertions(+), 1 deletion(-)
diff --git a/doc/api.txt b/doc/api.txt
index c4a0ec355c..c8d62c2b1a 100644
--- a/doc/api.txt
+++ b/doc/api.txt
@@ -824,6 +824,45 @@ Resumes the stream. Together with +pause()+ this useful to throttle reading.
Allows to close the stream before the +"end"+ is reached. No more events other
than +"close"+ will be fired after this method has been called.
+=== +fs.FileWriteStream+
+
+[cols="1,2,10",options="header"]
+|=========================================================
+|Event | Parameters | Notes
+
+|+"open"+ | +fd+ | The file descriptor was opened.
+|+"drain"+ | | No more data needs to be written.
+|+"error"+ | +err+ | An error occured. This stops the stream.
+|+"close"+ | | The file descriptor was closed.
+|=========================================================
+
++fs.createWriteStream(path, [options]);+ ::
+Returns a new FileWriteStream object.
++
++options+ is an object with the following defaults:
++
+----------------------------------------
+{ "flags": "r"
+, "encoding": "binary"
+, "mode": 0666
+}
+----------------------------------------
+
++writeStream.writeable+ ::
+A boolean that is +true+ by default, but turns +false+ after an +"error"+
+occured or +close()+ / +forceClose()+ was called.
+
++writeStream.write(data)+ ::
+Returns +true+ if the data was flushed to the kernel, and +false+ if it was
+queued up for being written later. A +"drain"+ will fire after all queued data
+has been written.
+
++writeStream.close()+ ::
+Closes the stream right after all queued +write()+ calls have finished.
+
++writeStream.forceClose()+ ::
+Allows to close the stream regardless of its current state.
+
== HTTP
To use the HTTP server and client one must +require("http")+.
diff --git a/lib/fs.js b/lib/fs.js
index 56c739389b..fa8ba89bda 100644
--- a/lib/fs.js
+++ b/lib/fs.js
@@ -576,7 +576,6 @@ var FileWriteStream = exports.FileWriteStream = function(path, options) {
});
};
-
flush();
};
sys.inherits(FileWriteStream, events.EventEmitter);
\ No newline at end of file
From 6d60d2db00d25b385bd978ea074486651ae10c50 Mon Sep 17 00:00:00 2001
From: Ryan Dahl
Date: Fri, 5 Mar 2010 14:36:13 -0800
Subject: [PATCH 25/31] Revert "Use kqueue on macintosh"
Experiencing bugs http://github.com/ry/node/issues/#issue/74
This reverts commit 409020a67d3388e4eda90af546e0fbe25b0adec3.
---
benchmark/http_simple.rb | 8 +++-----
src/node.cc | 4 +---
2 files changed, 4 insertions(+), 8 deletions(-)
diff --git a/benchmark/http_simple.rb b/benchmark/http_simple.rb
index d1176e5abe..ee33f57f1d 100644
--- a/benchmark/http_simple.rb
+++ b/benchmark/http_simple.rb
@@ -86,12 +86,10 @@ end
if $0 == __FILE__
#require DIR + '/../lib/ebb'
require 'rubygems'
-# require 'rack'
-# Rack::Handler::Mongrel.run(SimpleApp.new, :Port => 8000)
-
+ require 'rack'
require 'thin'
+ require 'ebb'
+# Rack::Handler::Mongrel.run(SimpleApp.new, :Port => 8000)
Thin::Server.start("0.0.0.0", 8000, SimpleApp.new)
-
-# require 'ebb'
# Ebb::start_server(SimpleApp.new, :port => 8000)
end
diff --git a/src/node.cc b/src/node.cc
index 205c8aeab5..11c6a7de8e 100644
--- a/src/node.cc
+++ b/src/node.cc
@@ -1240,12 +1240,10 @@ int main(int argc, char *argv[]) {
evcom_ignore_sigpipe();
// Initialize the default ev loop.
-#if defined(__sun)
+#ifdef __sun
// TODO(Ryan) I'm experiencing abnormally high load using Solaris's
// EVBACKEND_PORT. Temporarally forcing select() until I debug.
ev_default_loop(EVBACKEND_SELECT);
-#elif defined(__APPLE__)
- ev_default_loop(EVBACKEND_KQUEUE);
#else
ev_default_loop(EVFLAG_AUTO);
#endif
From e72b072d5333c87e2ca972f0d88dabfa0cedbec2 Mon Sep 17 00:00:00 2001
From: Ryan Dahl
Date: Fri, 5 Mar 2010 15:31:21 -0800
Subject: [PATCH 26/31] Decouple timer from EventEmitter
---
src/node.js | 14 ++++++--------
src/node_timer.cc | 42 ++++++++++++++++++++++++++++++++----------
src/node_timer.h | 7 ++++---
3 files changed, 42 insertions(+), 21 deletions(-)
diff --git a/src/node.js b/src/node.js
index 12686e674c..afc70bf88f 100644
--- a/src/node.js
+++ b/src/node.js
@@ -246,35 +246,33 @@ function addTimerListener (callback) {
// Special case the no param case to avoid the extra object creation.
if (arguments.length > 2) {
var args = Array.prototype.slice.call(arguments, 2);
- timer.addListener("timeout", function(){
- callback.apply(timer, args);
- });
+ timer.callback = function () { callback.apply(timer, args); };
} else {
- timer.addListener("timeout", callback);
+ timer.callback = callback;
}
}
-GLOBAL.setTimeout = function (callback, after) {
+global.setTimeout = function (callback, after) {
var timer = new process.Timer();
addTimerListener.apply(timer, arguments);
timer.start(after, 0);
return timer;
};
-GLOBAL.setInterval = function (callback, repeat) {
+global.setInterval = function (callback, repeat) {
var timer = new process.Timer();
addTimerListener.apply(timer, arguments);
timer.start(repeat, repeat);
return timer;
};
-GLOBAL.clearTimeout = function (timer) {
+global.clearTimeout = function (timer) {
if (timer instanceof process.Timer) {
timer.stop();
}
};
-GLOBAL.clearInterval = GLOBAL.clearTimeout;
+global.clearInterval = global.clearTimeout;
diff --git a/src/node_timer.cc b/src/node_timer.cc
index 5d60126c8a..1fa470aad3 100644
--- a/src/node_timer.cc
+++ b/src/node_timer.cc
@@ -9,6 +9,7 @@ Persistent Timer::constructor_template;
static Persistent timeout_symbol;
static Persistent repeat_symbol;
+static Persistent callback_symbol;
void
Timer::Initialize (Handle target)
@@ -17,12 +18,12 @@ Timer::Initialize (Handle target)
Local t = FunctionTemplate::New(Timer::New);
constructor_template = Persistent::New(t);
- constructor_template->Inherit(EventEmitter::constructor_template);
constructor_template->InstanceTemplate()->SetInternalFieldCount(1);
constructor_template->SetClassName(String::NewSymbol("Timer"));
timeout_symbol = NODE_PSYMBOL("timeout");
repeat_symbol = NODE_PSYMBOL("repeat");
+ callback_symbol = NODE_PSYMBOL("callback");
NODE_SET_PROTOTYPE_METHOD(constructor_template, "start", Timer::Start);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "stop", Timer::Stop);
@@ -66,7 +67,23 @@ Timer::OnTimeout (EV_P_ ev_timer *watcher, int revents)
assert(revents == EV_TIMEOUT);
- timer->Emit(timeout_symbol, 0, NULL);
+ HandleScope scope;
+
+ Local callback_v = timer->handle_->Get(callback_symbol);
+ if (!callback_v->IsFunction()) {
+ timer->Stop();
+ return;
+ }
+
+ Local callback = Local::Cast(callback_v);
+
+ TryCatch try_catch;
+
+ callback->Call(timer->handle_, 0, NULL);
+
+ if (try_catch.HasCaught()) {
+ FatalException(try_catch);
+ }
if (timer->watcher_.repeat == 0) timer->Unref();
}
@@ -90,8 +107,8 @@ Timer::New (const Arguments& args)
Handle
Timer::Start (const Arguments& args)
{
- Timer *timer = ObjectWrap::Unwrap(args.Holder());
HandleScope scope;
+ Timer *timer = ObjectWrap::Unwrap(args.Holder());
if (args.Length() != 2)
return ThrowException(String::New("Bad arguments"));
@@ -108,13 +125,18 @@ Timer::Start (const Arguments& args)
return Undefined();
}
-Handle
-Timer::Stop (const Arguments& args)
-{
+
+Handle Timer::Stop(const Arguments& args) {
+ HandleScope scope;
Timer *timer = ObjectWrap::Unwrap(args.Holder());
- if (ev_is_active(&timer->watcher_)) {
- ev_timer_stop(EV_DEFAULT_UC_ &timer->watcher_);
- timer->Unref();
- }
+ timer->Stop();
return Undefined();
}
+
+
+void Timer::Stop () {
+ if (watcher_.active) {
+ ev_timer_stop(EV_DEFAULT_UC_ &watcher_);
+ Unref();
+ }
+}
diff --git a/src/node_timer.h b/src/node_timer.h
index d662f9a0fa..0472fdb0d6 100644
--- a/src/node_timer.h
+++ b/src/node_timer.h
@@ -2,20 +2,20 @@
#define node_timer_h
#include
-#include
+#include
#include
#include
namespace node {
-class Timer : EventEmitter {
+class Timer : ObjectWrap {
public:
static void Initialize (v8::Handle target);
protected:
static v8::Persistent constructor_template;
- Timer () : EventEmitter () { }
+ Timer () : ObjectWrap () { }
~Timer();
static v8::Handle New (const v8::Arguments& args);
@@ -26,6 +26,7 @@ class Timer : EventEmitter {
private:
static void OnTimeout (EV_P_ ev_timer *watcher, int revents);
+ void Stop ();
ev_timer watcher_;
};
From 939a6c7484b31557878ae54faf0a6e5a31d6454e Mon Sep 17 00:00:00 2001
From: Ryan Dahl
Date: Fri, 5 Mar 2010 15:59:31 -0800
Subject: [PATCH 27/31] Clean up homepage
---
doc/index.html | 53 +++++++++++++++++++++++++++-----------------------
1 file changed, 29 insertions(+), 24 deletions(-)
diff --git a/doc/index.html b/doc/index.html
index e28ae2e8d4..e18522b1a2 100644
--- a/doc/index.html
+++ b/doc/index.html
@@ -21,10 +21,8 @@
ChangeLog
Build
About
- Demo
- Community
- Contribute
- Benchmarks
+ Links
+ Contributing
Documentation
@@ -208,32 +206,42 @@ make install
- Demo
-
- A chat room demo is running at Links
+
+
+
-
-
- For help and discussion subscribe to the mailing list at
+
+ For help and discussion, subscribe to the mailing list at
http://groups.google.com/group/nodejs
or send an email to nodejs+subscribe@googlegroups.com .
-
+ For real-time discussion, check irc.freenode.net #node.js
.
+
-
- For real-time discussion, check irc.freenode.net #node.js.
-
+
+ IRC logs
+
-
+
Projects/libraries which are using/for Node.js
-
- Contribute
+
+
+
+ Node.js buildbot
+
+
+
+
+ Contributing
- Patches are always welcome. The process is simple:
+ Patches are welcome. The process is simple:
@@ -253,13 +261,10 @@ git format-patch HEAD^
- Feature patches should usually be discussed before putting in the work.
+ You should ask the mailing list if a new feature is wanted before
+ working on a patch.
- Benchmarks
-
- 2009.09.06 narwhal, node, v8cgi, thin/eventmachine
-