Browse Source

Merge branch 'master' into net2

v0.7.4-release
Ryan Dahl 15 years ago
parent
commit
96f42745ff
  1. 36
      ChangeLog
  2. 184
      deps/coupling/coupling.c
  3. 28
      doc/api.txt
  4. 4
      doc/index.html
  5. 2
      lib/multipart.js
  6. 7
      src/node.cc
  7. 79
      src/node.js
  8. 0
      test/mjsunit/disabled/test-dns.js
  9. 0
      test/mjsunit/disabled/test-fs-sendfile.js
  10. 12
      test/mjsunit/fixtures/echo.js
  11. 1
      test/mjsunit/test-module-loading.js
  12. 15
      test/mjsunit/test-readdir.js
  13. 35
      test/mjsunit/test-stdio.js
  14. 1
      tools/test.py
  15. 2
      wscript

36
ChangeLog

@ -1,4 +1,38 @@
2010.01.20, Version 0.1.26
2010.02.03, Version 0.1.27
* Implemented __dirname (Felix Geisendörfer)
* Downcase process.ARGV, process.ENV, GLOBAL
(now process.argv, process.env, global)
* Bug Fix: Late promise promise callbacks firing
(Felix Geisendörfer, Jonas Pfenniger)
* Make assert.AssertionError instance of Error
* Removed inline require call for querystring
(self@cloudhead.net)
* Add support for MX, TXT, and SRV records in DNS module.
(Blaine Cook)
* Bugfix: HTTP client automatically reconnecting
* Adding OS X .dmg build scripts. (Standa Opichal)
* Bugfix: ObjectWrap memory leak
* Bugfix: Multipart handle Content-Type headers with charset
(Felix Geisendörfer)
* Upgrade http-parser to fix header overflow attack.
* Upgrade V8 to 2.1.0
* Various other bug fixes, performance improvements.
2010.01.20, Version 0.1.26, da00413196e432247346d9e587f8c78ce5ceb087
* Bugfix, HTTP eof causing crash (Ben Williamson)

184
deps/coupling/coupling.c

@ -143,98 +143,176 @@ ring_buffer_push (ring_buffer *ring, int fd)
return r;
}
/* PULL PUMP
*
* This is used to read data from a blocking file descriptor and pump it into
* a non-blocking pipe (or other non-blocking fd). The algorithm is this:
*
* while (true) {
* read(STDIN_FILENO) // blocking
*
* while (!ring.empty) {
* write(pipe) // non-blocking
* select(pipe, writable)
* }
* }
*
*/
static void
pump (int is_pull, int pullfd, int pushfd)
pull_pump (int pullfd, int pushfd)
{
int r;
ring_buffer ring;
fd_set readfds, writefds, exceptfds;
ring_buffer_init(&ring);
int maxfd;
while (pushfd >= 0 && (pullfd >= 0 || !ring_buffer_empty_p(&ring))) {
fd_set writefds, exceptfds;
FD_ZERO(&exceptfds);
FD_ZERO(&readfds);
FD_ZERO(&writefds);
maxfd = -1;
if (is_pull) {
if (!ring_buffer_empty_p(&ring)) {
maxfd = pushfd;
FD_SET(pushfd, &exceptfds);
FD_SET(pushfd, &writefds);
ring_buffer_init(&ring);
while (pullfd >= 0) {
/* Blocking read from STDIN_FILENO */
r = ring_buffer_pull(&ring, pullfd);
if (r == 0) {
/* eof */
close(pullfd);
pullfd = -1;
} else if (r < 0 && errno != EINTR && errno != EAGAIN) {
/* error */
perror("pull_pump read()");
close(pullfd);
pullfd = -1;
}
/* Push all of the data in the ring buffer out. */
while (!ring_buffer_empty_p(&ring)) {
/* non-blocking write() to the pipe */
r = ring_buffer_push(&ring, pushfd);
if (r < 0 && errno != EAGAIN && errno != EINTR) {
if (errno == EPIPE) {
/* This happens if someone closes the other end of the pipe. This
* is a normal forced close of STDIN. Hopefully there wasn't data
* in the ring buffer. Just close both ends and exit.
*/
close(pushfd);
close(pullfd);
pushfd = pullfd = -1;
} else {
if (pullfd >= 0) {
if (!ring_buffer_filled_p(&ring)) {
maxfd = pullfd;
FD_SET(pullfd, &exceptfds);
FD_SET(pullfd, &readfds);
}
perror("pull_pump write()");
close(pushfd);
close(pullfd);
}
return;
}
if (maxfd >= 0) {
r = select(maxfd+1, &readfds, &writefds, &exceptfds, NULL);
/* Select for writablity on the pipe end.
* Very rarely will this stick.
*/
r = select(pushfd+1, NULL, &writefds, &exceptfds, NULL);
if (r < 0 || (pullfd >= 0 && FD_ISSET(pushfd, &exceptfds))) {
if (r < 0 || FD_ISSET(pushfd, &exceptfds)) {
close(pushfd);
close(pullfd);
pushfd = pullfd = -1;
return;
}
}
if (pullfd >= 0 && FD_ISSET(pullfd, &exceptfds)) {
close(pullfd);
pullfd = -1;
}
assert(pullfd < 0);
assert(ring_buffer_empty_p(&ring));
close(pushfd);
}
/* PUSH PUMP
*
* This is used to push data out to a blocking file descriptor. It pulls
* data from a non-blocking pipe (pullfd) and pushes to STDOUT_FILENO
* (pushfd).
* When the pipe is closed, then the rest of the data is pushed out and then
* STDOUT_FILENO is closed.
*
* The algorithm looks roughly like this:
*
* while (true) {
* r = read(pipe) // nonblocking
*
* while (!ring.empty) {
* write(STDOUT_FILENO) // blocking
* }
*
* select(pipe, readable);
* }
*/
static void
push_pump (int pullfd, int pushfd)
{
int r;
ring_buffer ring;
fd_set readfds, exceptfds;
FD_ZERO(&exceptfds);
FD_ZERO(&readfds);
FD_SET(pullfd, &exceptfds);
FD_SET(pullfd, &readfds);
ring_buffer_init(&ring);
/* The pipe is open or there is data left to be pushed out
* NOTE: if pushfd (STDOUT_FILENO) ever errors out, then we just exit the
* loop.
*/
while (pullfd >= 0 || !ring_buffer_empty_p(&ring)) {
if (pullfd >= 0 && (is_pull || FD_ISSET(pullfd, &readfds))) {
/* Pull from the non-blocking pipe */
r = ring_buffer_pull(&ring, pullfd);
if (r == 0) {
/* eof */
close(pullfd);
pullfd = -1;
} else if (r < 0) {
if (errno != EINTR && errno != EAGAIN) goto error;
}
} else if (r < 0 && errno != EINTR && errno != EAGAIN) {
perror("push_pump read()");
close(pullfd);
pullfd = -1;
return;
}
if (!is_pull || FD_ISSET(pushfd, &writefds)) {
/* Push everything out to STDOUT */
while (!ring_buffer_empty_p(&ring)) {
/* Blocking write() to pushfd (STDOUT_FILENO) */
r = ring_buffer_push(&ring, pushfd);
if (r < 0) {
switch (errno) {
case EINTR:
case EAGAIN:
continue;
case EPIPE:
/* TODO catch SIGPIPE? */
/* If there was a problem, just exit the entire function */
if (r < 0 && errno != EINTR) {
close(pushfd);
close(pullfd);
pushfd = pullfd = -1;
return;
default:
goto error;
}
}
}
}
if (pullfd >= 0) {
/* select for readability on the pullfd */
r = select(pullfd+1, &readfds, NULL, &exceptfds, NULL);
if (r < 0 || FD_ISSET(pullfd, &exceptfds)) {
close(pushfd);
close(pullfd);
pushfd = pullfd = -1;
return;
error:
}
}
}
/* If we got here then we got eof on pullfd and pushed all the data out.
* so now just close pushfd */
assert(pullfd < 0);
assert(ring_buffer_empty_p(&ring));
close(pushfd);
close(pullfd);
perror("(coupling) pump");
}
static inline int
@ -262,7 +340,11 @@ pump_thread (void *data)
{
struct coupling *c = (struct coupling*)data;
pump(c->is_pull, c->pullfd, c->pushfd);
if (c->is_pull) {
pull_pump(c->pullfd, c->pushfd);
} else {
push_pump(c->pullfd, c->pushfd);
}
return NULL;
}

28
doc/api.txt

@ -1,7 +1,7 @@
NODE(1)
=======
Ryan Dahl <ry@tinyclouds.org>
Version, 0.1.26, 2010.01.20
Version, 0.1.27, 2010.02.03
== NAME
@ -48,7 +48,7 @@ execution.
=== Global Objects
+GLOBAL+ ::
+global+ ::
The global namespace object.
+process+ ::
@ -100,10 +100,10 @@ more information.
signal names such as SIGINT, SIGUSR1, etc.
|=========================================================
+process.ARGV+ ::
+process.argv+ ::
An array containing the command line arguments.
+process.ENV+ ::
+process.env+ ::
An object containing the user environment. See environ(7).
+process.pid+ ::
@ -565,7 +565,7 @@ Node provides a tridirectional +popen(3)+ facility through the class
+"error"+ callbacks will no longer be made.
|=========================================================
+process.createChildProcess(command, args=[], env=ENV)+::
+process.createChildProcess(command, args=[], env=process.env)+::
Launches a new process with the given +command+, command line arguments, and
environmental variables. For example:
+
@ -1459,14 +1459,14 @@ resolution.addCallback(function (addresses, ttl, cname) {
reversing.addCallback( function (domains, ttl, cname) {
sys.puts("reverse for " + a + ": " + JSON.stringify(domains));
});
reversing.addErrback( function (code, msg) {
sys.puts("reverse for " + a + " failed: " + msg);
reversing.addErrback( function (e) {
puts("reverse for " + a + " failed: " + e.message);
});
}
});
resolution.addErrback(function (code, msg) {
sys.puts("error: " + msg);
resolution.addErrback(function (e) {
puts("error: " + e.message);
});
-------------------------------------------------------------------------
@ -1482,8 +1482,9 @@ This function returns a promise.
canonical name for the query.
The type of each item in +addresses+ is determined by the record type, and
described in the documentation for the corresponding lookup methods below.
- on error: returns +code, msg+. +code+ is one of the error codes listed
below and +msg+ is a string describing the error in English.
- on error: Returns an instanceof Error object, where the "errno" field is one
of the error codes listed below and the "message" field is a string
describing the error in English.
+dns.resolve4(domain)+::
@ -1521,8 +1522,9 @@ Reverse resolves an ip address to an array of domain names.
- on success: returns +domains, ttl, cname+. +ttl+ (time-to-live) is an integer
specifying the number of seconds this result is valid for. +cname+ is the
canonical name for the query. +domains+ is an array of domains.
- on error: returns +code, msg+. +code+ is one of the error codes listed
below and +msg+ is a string describing the error in English.
- on error: Returns an instanceof Error object, where the "errno" field is one
of the error codes listed below and the "message" field is a string
describing the error in English.
Each DNS query can return an error code.

4
doc/index.html

@ -97,9 +97,9 @@ server.listen(7000, "localhost");</pre>
<a href="http://github.com/ry/node/tree/master">git repo</a>
</p>
<p>
2010.01.20
2010.02.03
<a
href="http://s3.amazonaws.com/four.livejournal/20100120/node-v0.1.26.tar.gz">node-v0.1.26.tar.gz</a>
href="http://s3.amazonaws.com/four.livejournal/20100203/node-v0.1.27.tar.gz">node-v0.1.27.tar.gz</a>
</p>
<h2 id="build">Build</h2>

2
lib/multipart.js

@ -183,7 +183,7 @@ Part.prototype.write = function(chunk) {
var header = this.buffer.substr(0, offset).split(/: ?/);
this.headers[header[0].toLowerCase()] = header[1];
this.buffer = this.buffer.substr(offset+2);
} else if (offset === false) {
} else if (offset === -1) {
return;
}
}

7
src/node.cc

@ -918,7 +918,7 @@ static void Load(int argc, char *argv[]) {
#define str(s) #s
process->Set(String::NewSymbol("platform"), String::New(xstr(PLATFORM)));
// process.ARGV
// process.argv
int i, j;
Local<Array> arguments = Array::New(argc - dash_dash_index + 1);
arguments->Set(Integer::New(0), String::New(argv[0]));
@ -928,8 +928,9 @@ static void Load(int argc, char *argv[]) {
}
// assign it
process->Set(String::NewSymbol("ARGV"), arguments);
process->Set(String::NewSymbol("argv"), arguments);
// create process.ENV
// create process.env
Local<Object> env = Object::New();
for (i = 0; environ[i]; i++) {
// skip entries without a '=' character
@ -945,6 +946,8 @@ static void Load(int argc, char *argv[]) {
}
// assign process.ENV
process->Set(String::NewSymbol("ENV"), env);
process->Set(String::NewSymbol("env"), env);
process->Set(String::NewSymbol("pid"), Integer::New(getpid()));
// define various internal methods

79
src/node.js

@ -41,11 +41,20 @@ node.dns.createConnection = removed("node.dns.createConnection() has moved. Use
// Module
var internalModuleCache = {};
function Module (id, parent) {
this.id = id;
this.exports = {};
this.parent = parent;
this.moduleCache = {};
if (parent) {
process.mixin(this.moduleCache, parent.moduleCache);
this.moduleCache[parent.id] = parent;
}
this.filename = null;
this.loaded = false;
this.loadPromise = null;
@ -53,23 +62,11 @@ function Module (id, parent) {
this.children = [];
};
var moduleCache = {};
function createModule (id, parent) {
if (id in moduleCache) {
debug("found " + JSON.stringify(id) + " in cache");
return moduleCache[id];
}
debug("didn't found " + JSON.stringify(id) + " in cache. creating new module");
var m = new Module(id, parent);
moduleCache[id] = m;
return m;
};
function createInternalModule (id, constructor) {
var m = createModule(id);
var m = new Module(id);
constructor(m.exports);
m.loaded = true;
internalModuleCache[id] = m;
return m;
};
@ -86,7 +83,7 @@ process.inherits = function (ctor, superCtor) {
process.createChildProcess = function (file, args, env) {
var child = new process.ChildProcess();
args = args || [];
env = env || process.ENV;
env = env || process.env;
var envPairs = [];
for (var key in env) {
if (env.hasOwnProperty(key)) {
@ -493,7 +490,7 @@ GLOBAL.clearInterval = GLOBAL.clearTimeout;
// Modules
var debugLevel = 0;
if ("NODE_DEBUG" in process.ENV) debugLevel = 1;
if ("NODE_DEBUG" in process.env) debugLevel = 1;
function debug (x) {
if (debugLevel > 0) {
@ -744,12 +741,12 @@ var path = pathModule.exports;
process.paths = [ path.join(process.installPrefix, "lib/node/libraries")
];
if (process.ENV["HOME"]) {
process.paths.unshift(path.join(process.ENV["HOME"], ".node_libraries"));
if (process.env["HOME"]) {
process.paths.unshift(path.join(process.env["HOME"], ".node_libraries"));
}
if (process.ENV["NODE_PATH"]) {
process.paths = process.ENV["NODE_PATH"].split(":").concat(process.paths);
if (process.env["NODE_PATH"]) {
process.paths = process.env["NODE_PATH"].split(":").concat(process.paths);
}
@ -803,11 +800,7 @@ function findModulePath (id, dirs, callback) {
searchLocations();
}
function loadModule (request, parent) {
// This is the promise which is actually returned from require.async()
var loadPromise = new events.Promise();
// debug("loadModule REQUEST " + (request) + " parent: " + JSON.stringify(parent));
function resolveModulePath(request, parent) {
var id, paths;
if (request.charAt(0) == "." && (request.charAt(1) == "/" || request.charAt(1) == ".")) {
@ -823,12 +816,24 @@ function loadModule (request, parent) {
paths = process.paths;
}
if (id in moduleCache) {
return [id, paths];
}
function loadModule (request, parent) {
var
// The promise returned from require.async()
loadPromise = new events.Promise(),
resolvedModule = resolveModulePath(request, parent),
id = resolvedModule[0],
paths = resolvedModule[1];
// debug("loadModule REQUEST " + (request) + " parent: " + JSON.stringify(parent));
var cachedModule = internalModuleCache[id] || parent.moduleCache[id];
if (cachedModule) {
debug("found " + JSON.stringify(id) + " in cache");
// In cache
var module = moduleCache[id];
process.nextTick(function() {
loadPromise.emitSuccess(module.exports);
loadPromise.emitSuccess(cachedModule.exports);
});
} else {
debug("looking for " + JSON.stringify(id) + " in " + JSON.stringify(paths));
@ -837,7 +842,7 @@ function loadModule (request, parent) {
if (!filename) {
loadPromise.emitError(new Error("Cannot find module '" + request + "'"));
} else {
var module = createModule(id, parent);
var module = new Module(id, parent);
module.load(filename, loadPromise);
}
});
@ -968,19 +973,19 @@ process.exit = function (code) {
var cwd = process.cwd();
// Make process.ARGV[0] and process.ARGV[1] into full paths.
if (process.ARGV[0].indexOf('/') > 0) {
process.ARGV[0] = path.join(cwd, process.ARGV[0]);
// Make process.argv[0] and process.argv[1] into full paths.
if (process.argv[0].indexOf('/') > 0) {
process.argv[0] = path.join(cwd, process.argv[0]);
}
if (process.ARGV[1].charAt(0) != "/" && !(/^http:\/\//).exec(process.ARGV[1])) {
process.ARGV[1] = path.join(cwd, process.ARGV[1]);
if (process.argv[1].charAt(0) != "/" && !(/^http:\/\//).exec(process.argv[1])) {
process.argv[1] = path.join(cwd, process.argv[1]);
}
// Load the main module--the command line argument.
process.mainModule = createModule(".");
process.mainModule = new Module(".");
var loadPromise = new events.Promise();
process.mainModule.load(process.ARGV[1], loadPromise);
process.mainModule.load(process.argv[1], loadPromise);
// All our arguments are loaded. We've evaluated all of the scripts. We
// might even have created TCP servers. Now we enter the main eventloop. If

0
test/mjsunit/test-dns.js → test/mjsunit/disabled/test-dns.js

0
test/mjsunit/test-fs-sendfile.js → test/mjsunit/disabled/test-fs-sendfile.js

12
test/mjsunit/fixtures/echo.js

@ -0,0 +1,12 @@
process.mixin(require("../common"));
process.stdio.open();
print("hello world\r\n");
process.stdio.addListener("data", function (data) {
print(data);
});
process.stdio.addListener("close", function () {
process.stdio.close();
});

1
test/mjsunit/test-module-loading.js

@ -41,7 +41,6 @@ assert.notEqual(one.hello, two.hello);
debug("test cycles containing a .. path");
var root = require("./fixtures/cycles/root"),
foo = require("./fixtures/cycles/folder/foo");
assert.equal(root.foo, foo);
assert.equal(root.sayHello(), root.hello);
var errorThrown = false;

15
test/mjsunit/test-readdir.js

@ -7,9 +7,18 @@ puts("readdir " + fixturesDir);
promise.addCallback(function (files) {
p(files);
assert.deepEqual(["a.js", "b","cycles", "multipart.js",
"nested-index","test_ca.pem",
"test_cert.pem", "test_key.pem", "throws_error.js", "x.txt"], files.sort());
assert.deepEqual(['a.js'
, 'b'
, 'cycles'
, 'echo.js'
, 'multipart.js'
, 'nested-index'
, 'test_ca.pem'
, 'test_cert.pem'
, 'test_key.pem'
, 'throws_error.js'
, 'x.txt'
], files.sort());
});
promise.addErrback(function () {

35
test/mjsunit/test-stdio.js

@ -0,0 +1,35 @@
process.mixin(require("./common"));
var sub = path.join(fixturesDir, 'echo.js');
var gotHelloWorld = false;
var gotEcho = false;
var child = process.createChildProcess(process.argv[0], [sub]);
child.addListener("error", function (data){
puts("parent stderr: " + data);
});
child.addListener("output", function (data){
if (data) {
puts('child said: ' + JSON.stringify(data));
if (!gotHelloWorld) {
assert.equal("hello world\r\n", data);
gotHelloWorld = true;
child.write('echo me\r\n');
} else {
assert.equal("echo me\r\n", data);
gotEcho = true;
child.close();
}
} else {
puts('child eof');
}
});
process.addListener('exit', function () {
assert.ok(gotHelloWorld);
assert.ok(gotEcho);
});

1
tools/test.py

@ -101,7 +101,6 @@ class ProgressIndicator(object):
# ...and then reraise the exception to bail out
raise
self.Done()
print "\r\nPlatform: {0} {1}".format(platform.system(), platform.release())
return not self.failed
def RunSingle(self):

2
wscript

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

Loading…
Cancel
Save