Browse Source

Merge branch 'master' into net2

v0.7.4-release
Ryan Dahl 15 years ago
parent
commit
681733d905
  1. 6
      AUTHORS
  2. 30
      ChangeLog
  3. 10
      deps/evcom/evcom.c
  4. 2
      deps/evcom/evcom.h
  5. 88
      doc/api.txt
  6. 57
      doc/index.html
  7. 370
      lib/fs.js
  8. 71
      lib/http.js
  9. 26
      lib/ini.js
  10. 2
      lib/querystring.js
  11. 19
      lib/sys.js
  12. 3
      src/node.cc
  13. 16
      src/node.js
  14. 15
      src/node_child_process.cc
  15. 2
      src/node_child_process.h
  16. 8
      src/node_net.cc
  17. 4
      src/node_net.h
  18. 42
      src/node_timer.cc
  19. 7
      src/node_timer.h
  20. 11
      test/fixtures/fixture.ini
  21. 67
      test/pummel/test-tcp-pause.js
  22. 55
      test/pummel/test-tcp-throttle-kernel-buffer.js
  23. 72
      test/pummel/test-tcp-throttle.js
  24. 12
      test/simple/test-child-process-env.js
  25. 54
      test/simple/test-file-read-stream.js
  26. 50
      test/simple/test-file-write-stream.js
  27. 287
      test/simple/test-fs-realpath.js
  28. 22
      test/simple/test-ini.js
  29. 4
      test/simple/test-querystring.js
  30. 2
      wscript

6
AUTHORS

@ -52,3 +52,9 @@ Yuichiro MASUI <masui@masuidrive.jp>
Mark Hansen <mark@markhansen.co.nz>
Zoran Tomicic <ztomicic@gmail.com>
Jeremy Ashkenas <jashkenas@gmail.com>
Scott González <scott.gonzalez@gmail.com>
James Duncan <james@joyent.com>
Arlo Breault <arlolra@gmail.com>
Kris Kowal <kris.kowal@cixar.com>
Jacek Becela <jacek.becela@gmail.com>
Rob Ellis <kazoomer@gmail.com>

30
ChangeLog

@ -1,4 +1,32 @@
2010.02.22, Version 0.1.30
2010.03.05, Version 0.1.31
* API: - Move process.watchFile into fs module
- Move process.inherits to sys
* Improve Solaris port
* tcp.Connection.prototype.write now returns boolean to indicate if
argument was flushed to the kernel buffer.
* Added fs.link, fs.symlink, fs.readlink, fs.realpath
(Rasmus Andersson)
* Add setgid,getgid (James Duncan)
* Improve sys.inspect (Benjamin Thomas)
* Allow passing env to child process (Isaac Schlueter)
* fs.createWriteStream, fs.createReadStream (Felix Geisendörfer)
* Add INI parser (Rob Ellis)
* Bugfix: fs.readFile handling encoding (Jacek Becela)
* Upgrade V8 to 2.1.2
2010.02.22, Version 0.1.30, bb0d1e65e1671aaeb21fac186b066701da0bc33b
* Major API Changes

10
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

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

88
doc/api.txt

@ -1,7 +1,7 @@
NODE(1)
=======
Ryan Dahl <ry@tinyclouds.org>
Version, 0.1.30, 2010.02.22
Version, 0.1.31, 2010.03.05
== NAME
@ -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)+ ::
@ -783,6 +785,86 @@ 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.
=== +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")+.
@ -1491,6 +1573,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()+::

57
doc/index.html

@ -21,10 +21,8 @@
<li><a href="changelog.html">ChangeLog</a></li>
<li><a href="#build">Build</a></li>
<li><a href="#about">About</a></li>
<li><a href="#demo">Demo</a></li>
<li><a href="#community">Community</a></li>
<li><a href="#contribute">Contribute</a></li>
<li><a href="#benchmarks">Benchmarks</a></li>
<li><a href="#links">Links</a></li>
<li><a href="#contributing">Contributing</a></li>
<li><a href="api.html">Documentation</a></li>
</ol>
</div>
@ -98,8 +96,8 @@ server.listen(7000, "localhost");</pre>
<a href="http://github.com/ry/node/tree/master">git repo</a>
</p>
<p>
2010.02.22
<a href="http://nodejs.org/dist/node-v0.1.30.tar.gz">node-v0.1.30.tar.gz</a>
2010.03.05
<a href="http://nodejs.org/dist/node-v0.1.31.tar.gz">node-v0.1.31.tar.gz</a>
</p>
<h2 id="build">Build</h2>
@ -208,32 +206,42 @@ make install</pre>
</p>
<h2 id="demo">Demo</h2>
<p>
A chat room demo is running at <a
<h2 id="links">Links</h2>
<ul>
<li>
A chat room <b>demo</b> is running at <a
href="http://chat.nodejs.org">chat.nodejs.org</a>. The
source code for the chat room is at <a
href="http://github.com/ry/node_chat/tree/master">http://github.com/ry/node_chat</a>.
The chat room is not stable and might occasionally be down.
</p>
</li>
<h2 id="community">Community</h2>
<p>
For help and discussion subscribe to the mailing list at
<li>
For help and discussion, subscribe to the mailing list at
<a href="http://groups.google.com/group/nodejs">http://groups.google.com/group/nodejs</a>
or send an email to <a href="mailto:nodejs+subscribe@googlegroups.com">nodejs+subscribe@googlegroups.com</a>.
</p>
For real-time discussion, check irc.freenode.net <code>#node.js</code>.
</li>
<p>
For real-time discussion, check irc.freenode.net #node.js.
</p>
<li>
<a href="http://nodejs.debuggable.com/">IRC logs</a>
</li>
<p>
<li>
<a href="http://wiki.github.com/ry/node">Projects/libraries which are using/for Node.js</a>
</p>
<h2 id="contribute">Contribute</h2>
</li>
<li>
<a href="http://buildbot.nodejs.org:8010/">Node.js buildbot</a>
</li>
</ul>
<h2 id="contributing">Contributing</h2>
<p>
Patches are always welcome. The process is simple:
Patches are welcome. The process is simple:
</p>
<pre class="sh_none">
@ -253,13 +261,10 @@ git format-patch HEAD^
</p>
<p>
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.
</p>
<h2 id="benchmarks">Benchmarks</h2>
<p>
2009.09.06 <a href="http://four.livejournal.com/1019177.html">narwhal, node, v8cgi, thin/eventmachine</a>
</p>
</div>
<script type="text/javascript">
var gaJsHost = (("https:" == document.location.protocol) ?

370
lib/fs.js

@ -1,3 +1,7 @@
var
sys = require('sys'),
events = require('events');
exports.Stats = process.Stats;
process.Stats.prototype._checkModeProperty = function (property) {
@ -292,86 +296,332 @@ 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);
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<path.length; i++) {
part = path.slice(0, i+1).join('/');
if (part.length !== 0) {
if (part in knownHards) {
buf.push(path[i]);
} else {
seen_links[file_id] = 1;
stats = exports.lstatSync(part);
if (stats.isSymbolicLink()) {
var newpath = exports.readlinkSync(path);
if (newpath.charAt(0) === '/') {
path = newpath;
x = stats.dev.toString(32)+":"+stats.ino.toString(32);
if (x in seen_links)
throw new Error("cyclic link at "+part);
seen_links[x] = true;
part = exports.readlinkSync(part);
if (part.charAt(0) === '/') {
// absolute
path = normalizeArray(part.split('/'));
buf = [''];
i = 0;
} else {
var dir = dirname(path);
path = (dir !== '') ? dir + '/' + newpath : newpath;
// 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<L && path[y] === part[y]; y++);
if (y !== L) {
path = part;
delta = i-y;
i = y-1;
if (delta > 0) buf.splice(y, delta);
} else {
i--;
}
}
} else {
return normalize(path);
buf.push(path[i]);
knownHards[buf.join('/')] = true;
}
}
}
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));
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() {
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 {
seen_links[file_id] = 1;
exports.lstat(part, function(err, stats){
if (err) return done(err);
if (stats.isSymbolicLink()) {
exports.readlink(path, function(err, newpath) {
if (err) callback(err);
if (newpath.charAt(0) === '/') {
path = newpath;
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 {
var dir = dirname(path);
path = (dir !== '') ? dir + '/' + newpath : newpath;
// 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<L && path[y] === part[y]; y++);
if (y !== L) {
path = part;
delta = i-y;
i = y-1; // resolve new node if needed
if (delta > 0) buf.splice(y, delta);
}
_next();
else {
i--; // resolve new node if needed
}
}
next();
}); // fs.readlink
}
else {
buf.push(path[i]);
knownHards[buf.join('/')] = true;
next();
}
}); // fs.lstat
}
}
next();
}
exports.createReadStream = function(path, options) {
return new FileReadStream(path, options);
};
var FileReadStream = exports.FileReadStream = function(path, options) {
events.EventEmitter.call(this);
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 = null;
function read() {
if (!self.readable || self.paused) {
return;
}
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();
return;
}
// do not emit events if the stream is paused
if (self.paused) {
buffer = data;
return;
}
// do not emit events anymore after we declared the stream unreadable
if (!self.readable) {
return;
}
self.emit('data', data);
read();
});
} else {
callback(null, normalize(path));
}
fs.open(this.path, this.flags, this.mode, function(err, fd) {
if (err) {
self.emit('error', err);
self.readable = false;
return;
}
self.fd = fd;
self.emit('open', fd);
read();
});
this.forceClose = function() {
this.readable = false;
fs.close(this.fd, function(err) {
if (err) {
self.emit('error', err);
return;
}
function _next() {
exports.lstat(path, function(err, stats){
if (err) callback(err);
else next(stats);
self.emit('close');
});
};
this.pause = function() {
this.paused = true;
};
this.resume = function() {
this.paused = false;
if (buffer !== null) {
self.emit('data', buffer);
buffer = null;
}
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);
}
read();
};
};
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;
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 flush() {
if (busy) {
return;
}
var args = queue.shift();
if (!args) {
return self.emit('drain');
}
busy = true;
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));
var method = args.shift();
args.push(function(err) {
busy = false;
if (err) {
self.writeable = false;
self.emit('error', err);
return;
}
// save reference for file pointer
if (method === fs.open) {
self.fd = arguments[1];
self.emit('open', self.fd);
}
// stop flushing after close
if (method === fs.close) {
self.emit('close');
return;
}
flush();
});
}
// Inject the file pointer
if (method !== fs.open) {
args.unshift(self.fd);
}
method.apply(null, args);
};
this.write = function(data) {
if (!this.writeable) {
throw new Error('stream not writeable');
}
queue.push([fs.write, data, undefined, this.encoding]);
flush();
return false;
};
this.close = function() {
this.writeable = false;
queue.push([fs.close,]);
flush();
};
this.forceClose = function() {
this.writeable = false;
fs.close(self.fd, function(err) {
if (err) {
self.emit('error', err);
return;
}
self.emit('close');
});
};
flush();
};
sys.inherits(FileWriteStream, events.EventEmitter);

71
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") {
@ -317,10 +319,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 +370,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 +380,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
@ -463,11 +459,20 @@ exports.createClient = function (port, host) {
var requests = [];
var currentRequest;
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
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,
@ -475,6 +480,14 @@ exports.createClient = function (port, host) {
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._reconnect();
return;
}
//sys.debug("client flush readyState = " + client.readyState);
@ -483,16 +496,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();
@ -513,16 +516,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();
}
});
@ -566,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;
};

26
lib/ini.js

@ -0,0 +1,26 @@
exports.parse = function(d) {
var trim = function(str) { return str.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); }
var ini = {'-':{}};
var section = '-';
var lines = d.split('\n');
for (var i=0; i<lines.length; i++) {
var re = /(.*)=(.*)|\[([a-z:\.0-9_\s]+)\]/i;
var match = lines[i].match(re);
if (match != null) {
if (match[3] != undefined) {
section = match[3];
ini[section] = {};
} else {
var key = trim(match[1]);
var value = trim(match[2]);
ini[section][key] = value;
}
}
}
return ini;
}

2
lib/querystring.js

@ -68,7 +68,7 @@ QueryString.stringify = function (obj, sep, eq, name) {
};
QueryString.parseQuery = QueryString.parse = function (qs, sep, eq) {
return qs
return (qs || '')
.split(sep||"&")
.map(pieceParser(eq||"="))
.reduce(mergeParams);

19
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 = "";

3
src/node.cc

@ -429,6 +429,7 @@ static Handle<Value> Loop(const Arguments& args) {
}
static Handle<Value> Unloop(const Arguments& args) {
fprintf(stderr, "Node.js Depreciation: Don't use process.unloop(). It will be removed soon.\n");
HandleScope scope;
int how = EVUNLOOP_ONE;
if (args[0]->IsString()) {
@ -510,7 +511,7 @@ static Handle<Value> SetGid(const Arguments& args) {
Local<Integer> 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();

16
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;
@ -310,7 +308,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) {

15
src/node_child_process.cc

@ -9,6 +9,8 @@
#include <fcntl.h>
#include <sys/types.h>
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);
@ -300,6 +302,10 @@ int ChildProcess::Spawn(const char *file, char *const args[], char *const 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();
@ -315,13 +321,16 @@ 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);
}
// Restore environment.
environ = save_our_env;
// Parent.
ev_child_set(&child_watcher_, pid_, 0);

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

8
src/node_net.cc

@ -633,12 +633,12 @@ Handle<Value> 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) {

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

42
src/node_timer.cc

@ -9,6 +9,7 @@ Persistent<FunctionTemplate> Timer::constructor_template;
static Persistent<String> timeout_symbol;
static Persistent<String> repeat_symbol;
static Persistent<String> callback_symbol;
void
Timer::Initialize (Handle<Object> target)
@ -17,12 +18,12 @@ Timer::Initialize (Handle<Object> target)
Local<FunctionTemplate> t = FunctionTemplate::New(Timer::New);
constructor_template = Persistent<FunctionTemplate>::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<Value> callback_v = timer->handle_->Get(callback_symbol);
if (!callback_v->IsFunction()) {
timer->Stop();
return;
}
Local<Function> callback = Local<Function>::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<Value>
Timer::Start (const Arguments& args)
{
Timer *timer = ObjectWrap::Unwrap<Timer>(args.Holder());
HandleScope scope;
Timer *timer = ObjectWrap::Unwrap<Timer>(args.Holder());
if (args.Length() != 2)
return ThrowException(String::New("Bad arguments"));
@ -108,13 +125,18 @@ Timer::Start (const Arguments& args)
return Undefined();
}
Handle<Value>
Timer::Stop (const Arguments& args)
{
Handle<Value> Timer::Stop(const Arguments& args) {
HandleScope scope;
Timer *timer = ObjectWrap::Unwrap<Timer>(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();
}
}

7
src/node_timer.h

@ -2,20 +2,20 @@
#define node_timer_h
#include <node.h>
#include <node_events.h>
#include <node_object_wrap.h>
#include <v8.h>
#include <ev.h>
namespace node {
class Timer : EventEmitter {
class Timer : ObjectWrap {
public:
static void Initialize (v8::Handle<v8::Object> target);
protected:
static v8::Persistent<v8::FunctionTemplate> constructor_template;
Timer () : EventEmitter () { }
Timer () : ObjectWrap () { }
~Timer();
static v8::Handle<v8::Value> 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_;
};

11
test/fixtures/fixture.ini

@ -0,0 +1,11 @@
root=something
[section]
one=two
Foo=Bar
this=Your Mother!
blank=
[Section Two]
something else=blah
remove = whitespace

67
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");
});

55
test/pummel/test-tcp-throttle-kernel-buffer.js

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

72
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.addListener("connect", function () {
assert.equal(false, connection.write(body));
connection.close();
return;
}
setTimeout(function () {
connection.write("C");
write(j+1);
}, 10);
}
write(0);
});
});
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();
setTimeout(function () {
chars_recved = recv.length;
puts("pause at: " + chars_recved);
chars_recved += d.length;
puts("got " + chars_recved);
if (!paused) {
client.pause();
npauses += 1;
paused = true;
puts("pause");
x = chars_recved;
setTimeout(function () {
puts("resume at: " + chars_recved);
assert.equal(chars_recved, recv.length);
assert.equal(chars_recved, x);
client.resume();
}, 500);
}, 500);
}, 500);
}, 500);
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);
});

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

54
test/simple/test-file-read-stream.js

@ -0,0 +1,54 @@
process.mixin(require('../common'));
var
fn = path.join(fixturesDir, 'multipart.js'),
file = fs.createReadStream(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]);
}
});

50
test/simple/test-file-write-stream.js

@ -0,0 +1,50 @@
process.mixin(require('../common'));
var
fn = path.join(fixturesDir, "write.txt"),
file = fs.createWriteStream(fn),
EXPECTED = '0123456789',
callbacks = {
open: -1,
drain: -2,
close: -1
};
file
.addListener('open', function(fd) {
callbacks.open++;
assert.equal('number', typeof fd);
})
.addListener('error', function(err) {
throw err;
})
.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]);
}
});

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

22
test/simple/test-ini.js

@ -0,0 +1,22 @@
process.mixin(require("../common"));
require("fs");
parse = require("ini").parse;
debug("load fixtures/fixture.ini");
p = path.join(fixturesDir, "fixture.ini");
fs.readFile(p,function(err, data) {
if (err) throw err;
assert.equal(typeof parse, 'function');
var iniContents = parse(data);
assert.equal(typeof iniContents, 'object');
assert.deepEqual(iniContents,{"-":{"root":"something"},"section":{"one":"two","Foo":"Bar","this":"Your Mother!","blank":""},"Section Two":{"something else":"blah","remove":"whitespace"}})
assert.equal(iniContents['-']['root'],'something');
assert.equal(iniContents['section']['blank'],'');
assert.equal(iniContents['Section Two']['remove'],'whitespace');
});

4
test/simple/test-querystring.js

@ -114,6 +114,10 @@ var f = qs.stringify({
});
assert.equal(f, "a=b&q=x%3Dy%26y%3Dz");
assert.doesNotThrow(function () {
qs.parse(undefined);
});
// nested in colon
var f = qs.stringify({
a : "b",

2
wscript

@ -7,7 +7,7 @@ from os.path import join, dirname, abspath
from logging import fatal
cwd = os.getcwd()
VERSION="0.1.30"
VERSION="0.1.31"
APPNAME="node.js"
import js2c

Loading…
Cancel
Save