Browse Source

Merge branch 'master' into v0.6-merge

Conflicts:
	src/node.cc
v0.9.1-release
isaacs 13 years ago
parent
commit
643f00d3f9
  1. 28
      LICENSE
  2. 24
      configure
  3. 181
      doc/api/child_process.markdown
  4. 4
      doc/api/net.markdown
  5. 56
      lib/buffer.js
  6. 305
      lib/child_process.js
  7. 6
      lib/dgram.js
  8. 1
      lib/fs.js
  9. 4
      lib/http.js
  10. 62
      lib/net.js
  11. 5
      lib/querystring.js
  12. 2
      lib/util.js
  13. 1
      node.gyp
  14. 8
      src/handle_wrap.cc
  15. 4
      src/handle_wrap.h
  16. 106
      src/ngx-queue.h
  17. 58
      src/node.cc
  18. 20
      src/req_wrap.h
  19. 58
      test/pummel/test-net-connect-econnrefused.js
  20. 198
      test/simple/test-child-process-fork-net.js
  21. 131
      test/simple/test-child-process-fork-net2.js
  22. 2
      test/simple/test-child-process-kill-throw.js
  23. 27
      test/simple/test-dgram-ref.js
  24. 58
      test/simple/test-http-client-pipe-end.js
  25. 7
      test/simple/test-net-isip.js
  26. 54
      test/simple/test-process-active-wraps.js
  27. 4
      test/simple/test-querystring.js
  28. 9
      test/simple/test-util.js

28
LICENSE

@ -550,3 +550,31 @@ maintained libraries. The externally maintained libraries used by Node are:
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
"""
- src/ngx-queue.h ngx-queue.h is taken from the nginx source tree. nginx's
license follows
"""
Copyright (C) 2002-2012 Igor Sysoev
Copyright (C) 2011,2012 Nginx, Inc.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
"""

24
configure

@ -224,18 +224,22 @@ def host_arch():
def target_arch():
return host_arch()
def gcc_version():
def cc_version():
try:
proc = subprocess.Popen([CC, '-v'], stderr=subprocess.PIPE)
except OSError:
return None
# TODO parse clang output
version = proc.communicate()[1].split('\n')[-2]
match = re.match('gcc version (\d+)\.(\d+)\.(\d+)', version)
if not match: return None
return ['LLVM' in version] + map(int, match.groups())
lines = proc.communicate()[1].split('\n')
version_line = None
for i, line in enumerate(lines):
if 'version' in line:
version_line = line
if not version_line:
return None
version = version_line.split("version")[1].strip().split()[0].split(".")
if not version:
return None
return ['LLVM' in version_line] + version
def configure_node(o):
# TODO add gdb
@ -250,10 +254,10 @@ def configure_node(o):
# see http://gcc.gnu.org/bugzilla/show_bug.cgi?id=45883
# see http://code.google.com/p/v8/issues/detail?id=884
o['variables']['strict_aliasing'] = b(
'clang' in CC or gcc_version() >= [False, 4, 6, 0])
'clang' in CC or cc_version() >= [False, 4, 6, 0])
# clang has always supported -fvisibility=hidden, right?
if 'clang' not in CC and gcc_version() < [False, 4, 0, 0]:
if 'clang' not in CC and cc_version() < [False, 4, 0, 0]:
o['variables']['visibility'] = ''
# By default, enable DTrace on SunOS systems. Don't allow it on other

181
doc/api/child_process.markdown

@ -52,6 +52,14 @@ in the child. After disconnecting it is no longer possible to send messages.
An alternative way to check if you can send messages is to see if the
`child.connected` property is `true`.
### Event: 'message'
* `message` {Object} a parsed JSON object or primitive value
* `sendHandle` {Handle object} a Socket or Server object
Messages send by `.send(message, [sendHandle])` are obtained using the
`message` event.
### child.stdin
* {Stream object}
@ -116,15 +124,120 @@ process may not actually kill it. `kill` really just sends a signal to a proces
See `kill(2)`
### child.send(message, [sendHandle])
* `message` {Object}
* `sendHandle` {Handle object}
Send a message (and, optionally, a handle object) to a child process.
When using `child_process.fork()` you can write to the child using
`child.send(message, [sendHandle])` and messages are received by
a `'message'` event on the child.
For example:
var cp = require('child_process');
var n = cp.fork(__dirname + '/sub.js');
n.on('message', function(m) {
console.log('PARENT got message:', m);
});
n.send({ hello: 'world' });
And then the child script, `'sub.js'` might look like this:
process.on('message', function(m) {
console.log('CHILD got message:', m);
});
process.send({ foo: 'bar' });
In the child the `process` object will have a `send()` method, and `process`
will emit objects each time it receives a message on its channel.
There is a special case when sending a `{cmd: 'NODE_foo'}` message. All messages
containing a `NODE_` prefix in its `cmd` property will not be emitted in
the `message` event, since they are internal messages used by node core.
Messages containing the prefix are emitted in the `internalMessage` event, you
should by all means avoid using this feature, it is subject to change without notice.
The `sendHandle` option to `child.send()` is for sending a TCP server or
socket object to another process. The child will receive the object as its
second argument to the `message` event.
**send server object**
Here is an example of sending a server:
var child = require('child_process').fork('child.js');
// Open up the server object and send the handle.
var server = require('net').createServer();
server.on('connection', function (socket) {
socket.end('handled by parent');
});
server.listen(1337, function() {
child.send('server', server);
});
And the child would the recive the server object as:
process.on('message', function(m, server) {
if (m === 'server') {
server.on('connection', function (socket) {
socket.end('handled by child');
});
}
});
Note that the server is now shared between the parent and child, this means
that some connections will be handled by the parent and some by the child.
**send socket object**
Here is an example of sending a socket. It will spawn two childs and handle
connections with the remote address `74.125.127.100` as VIP by sending the
socket to a "special" child process. Other sockets will go to a "normal" process.
var normal = require('child_process').fork('child.js', ['normal']);
var special = require('child_process').fork('child.js', ['special']);
// Open up the server and send sockets to child
var server = require('net').createServer();
server.on('connection', function (socket) {
// if this is a VIP
if (socket.remoteAddress === '74.125.127.100') {
special.send('socket', socket);
return;
}
// just the usual dudes
normal.send('socket', socket);
});
server.listen(1337);
The `child.js` could look like this:
See `child_process.fork()` for details.
process.on('message', function(m, socket) {
if (m === 'socket') {
socket.end('You where handled as a ' + process.argv[2] + ' person');
}
});
Note that once a single socket has been sent to a child the parent can no
longer keep track of when the socket is destroyed. To indicate this condition
the `.connections` property becomes `null`.
It is also recomended not to use `.maxConnections` in this condition.
### child.disconnect()
To close the IPC connection between parent and child use the
`child.disconnect()` method. This allows the child to exit gracefully since
there is no IPC channel keeping it alive. When calling this method the
`disconnect` event will be emitted in both parent and child, and the
`connected` flag will be set to `false`. Please note that you can also call
`process.disconnect()` in the child process.
## child_process.spawn(command, [args], [options])
@ -333,38 +446,8 @@ leaner than `child_process.exec`. It has the same options.
This is a special case of the `spawn()` functionality for spawning Node
processes. In addition to having all the methods in a normal ChildProcess
instance, the returned object has a communication channel built-in. The
channel is written to with `child.send(message, [sendHandle])` and messages
are received by a `'message'` event on the child.
For example:
var cp = require('child_process');
var n = cp.fork(__dirname + '/sub.js');
n.on('message', function(m) {
console.log('PARENT got message:', m);
});
n.send({ hello: 'world' });
And then the child script, `'sub.js'` might look like this:
process.on('message', function(m) {
console.log('CHILD got message:', m);
});
process.send({ foo: 'bar' });
In the child the `process` object will have a `send()` method, and `process`
will emit objects each time it receives a message on its channel.
There is a special case when sending a `{cmd: 'NODE_foo'}` message. All messages
containing a `NODE_` prefix in its `cmd` property will not be emitted in
the `message` event, since they are internal messages used by node core.
Messages containing the prefix are emitted in the `internalMessage` event, you
should by all means avoid using this feature, it may change without warranty.
instance, the returned object has a communication channel built-in. See
`child.send(message, [sendHandle])` for details.
By default the spawned Node process will have the stdout, stderr associated
with the parent's. To change this behavior set the `silent` property in the
@ -373,31 +456,3 @@ with the parent's. To change this behavior set the `silent` property in the
These child Nodes are still whole new instances of V8. Assume at least 30ms
startup and 10mb memory for each new Node. That is, you cannot create many
thousands of them.
The `sendHandle` option to `child.send()` is for sending a handle object to
another process. Child will receive the handle as as second argument to the
`message` event. Here is an example of sending a handle:
var server = require('net').createServer();
var child = require('child_process').fork(__dirname + '/child.js');
// Open up the server object and send the handle.
server.listen(1337, function() {
child.send({ server: true }, server._handle);
});
Here is an example of receiving the server handle and sharing it between
processes:
process.on('message', function(m, serverHandle) {
if (serverHandle) {
var server = require('net').createServer();
server.listen(serverHandle);
}
});
To close the IPC connection between parent and child use the
`child.disconnect()` method. This allows the child to exit gracefully since
there is no IPC channel keeping it alive. When calling this method the
`disconnect` event will be emitted in both parent and child, and the
`connected` flag will be set to `false`. Please note that you can also call
`process.disconnect()` in the child process.

4
doc/api/net.markdown

@ -198,10 +198,14 @@ Don't call `server.address()` until the `'listening'` event has been emitted.
Set this property to reject connections when the server's connection count gets
high.
It is not recommended to use this option once a socket has been sent to a child
with `child_process.fork()`.
### server.connections
The number of concurrent connections on the server.
This becomes `null` when sending a socket to a child with `child_process.fork()`.
`net.Server` is an `EventEmitter` with the following events:

56
lib/buffer.js

@ -33,6 +33,10 @@ function binaryWarn() {
exports.INSPECT_MAX_BYTES = 50;
// Make SlowBuffer inherit from Buffer.
// This is an exception to the rule that __proto__ is not allowed in core.
SlowBuffer.prototype.__proto__ = Buffer.prototype;
function toHex(n) {
if (n < 16) return '0' + n.toString(16);
@ -40,20 +44,6 @@ function toHex(n) {
}
SlowBuffer.prototype.inspect = function() {
var out = [],
len = this.length;
for (var i = 0; i < len; i++) {
out[i] = toHex(this[i]);
if (i == exports.INSPECT_MAX_BYTES) {
out[i + 1] = '...';
break;
}
}
return '<SlowBuffer ' + out.join(' ') + '>';
};
SlowBuffer.prototype.hexSlice = function(start, end) {
var len = this.length;
@ -302,24 +292,25 @@ function allocPool() {
// Static methods
Buffer.isBuffer = function isBuffer(b) {
return b instanceof Buffer || b instanceof SlowBuffer;
return b instanceof Buffer;
};
// Inspect
Buffer.prototype.inspect = function inspect() {
var out = [],
len = this.length;
len = this.length,
name = this.constructor.name;
for (var i = 0; i < len; i++) {
out[i] = toHex(this.parent[i + this.offset]);
out[i] = toHex(this[i]);
if (i == exports.INSPECT_MAX_BYTES) {
out[i + 1] = '...';
break;
}
}
return '<Buffer ' + out.join(' ') + '>';
return '<' + name + ' ' + out.join(' ') + '>';
};
@ -1143,32 +1134,3 @@ Buffer.prototype.writeDoubleLE = function(value, offset, noAssert) {
Buffer.prototype.writeDoubleBE = function(value, offset, noAssert) {
writeDouble(this, value, offset, true, noAssert);
};
SlowBuffer.prototype.readUInt8 = Buffer.prototype.readUInt8;
SlowBuffer.prototype.readUInt16LE = Buffer.prototype.readUInt16LE;
SlowBuffer.prototype.readUInt16BE = Buffer.prototype.readUInt16BE;
SlowBuffer.prototype.readUInt32LE = Buffer.prototype.readUInt32LE;
SlowBuffer.prototype.readUInt32BE = Buffer.prototype.readUInt32BE;
SlowBuffer.prototype.readInt8 = Buffer.prototype.readInt8;
SlowBuffer.prototype.readInt16LE = Buffer.prototype.readInt16LE;
SlowBuffer.prototype.readInt16BE = Buffer.prototype.readInt16BE;
SlowBuffer.prototype.readInt32LE = Buffer.prototype.readInt32LE;
SlowBuffer.prototype.readInt32BE = Buffer.prototype.readInt32BE;
SlowBuffer.prototype.readFloatLE = Buffer.prototype.readFloatLE;
SlowBuffer.prototype.readFloatBE = Buffer.prototype.readFloatBE;
SlowBuffer.prototype.readDoubleLE = Buffer.prototype.readDoubleLE;
SlowBuffer.prototype.readDoubleBE = Buffer.prototype.readDoubleBE;
SlowBuffer.prototype.writeUInt8 = Buffer.prototype.writeUInt8;
SlowBuffer.prototype.writeUInt16LE = Buffer.prototype.writeUInt16LE;
SlowBuffer.prototype.writeUInt16BE = Buffer.prototype.writeUInt16BE;
SlowBuffer.prototype.writeUInt32LE = Buffer.prototype.writeUInt32LE;
SlowBuffer.prototype.writeUInt32BE = Buffer.prototype.writeUInt32BE;
SlowBuffer.prototype.writeInt8 = Buffer.prototype.writeInt8;
SlowBuffer.prototype.writeInt16LE = Buffer.prototype.writeInt16LE;
SlowBuffer.prototype.writeInt16BE = Buffer.prototype.writeInt16BE;
SlowBuffer.prototype.writeInt32LE = Buffer.prototype.writeInt32LE;
SlowBuffer.prototype.writeInt32BE = Buffer.prototype.writeInt32BE;
SlowBuffer.prototype.writeFloatLE = Buffer.prototype.writeFloatLE;
SlowBuffer.prototype.writeFloatBE = Buffer.prototype.writeFloatBE;
SlowBuffer.prototype.writeDoubleLE = Buffer.prototype.writeDoubleLE;
SlowBuffer.prototype.writeDoubleBE = Buffer.prototype.writeDoubleBE;

305
lib/child_process.js

@ -54,15 +54,207 @@ function createSocket(pipe, readable) {
}
// this object contain function to convert TCP objects to native handle objects
// and back again.
var handleConversion = {
'net.Native': {
simultaneousAccepts: true,
send: function(message, handle) {
return handle;
},
got: function(message, handle, emit) {
emit(handle);
}
},
'net.Server': {
simultaneousAccepts: true,
send: function(message, server) {
return server._handle;
},
got: function(message, handle, emit) {
var self = this;
var server = new net.Server();
server.listen(handle, function() {
emit(server);
});
}
},
'net.Socket': {
send: function(message, socket) {
// pause socket so no data is lost, will be resumed later
socket.pause();
// if the socket wsa created by net.Server
if (socket.server) {
// the slave should keep track of the socket
message.key = socket.server._connectionKey;
var firstTime = !this._channel.sockets.send[message.key];
// add socket to connections list
var socketList = getSocketList('send', this, message.key);
socketList.add(socket);
// the server should no longer expose a .connection property
// and when asked to close it should query the socket status from slaves
if (firstTime) {
socket.server._setupSlave(socketList);
}
}
// remove handle from socket object, it will be closed when the socket
// has been send
var handle = socket._handle;
handle.onread = function() {};
socket._handle = null;
return handle;
},
got: function(message, handle, emit) {
var socket = new net.Socket({handle: handle});
socket.readable = socket.writable = true;
socket.pause();
// if the socket was created by net.Server we will track the socket
if (message.key) {
// add socket to connections list
var socketList = getSocketList('got', this, message.key);
socketList.add(socket);
}
emit(socket);
socket.resume();
}
}
};
// This object keep track of the socket there are sended
function SocketListSend(slave, key) {
var self = this;
this.key = key;
this.list = [];
this.slave = slave;
slave.once('disconnect', function() {
self.flush();
});
this.slave.on('internalMessage', function(msg) {
if (msg.cmd !== 'NODE_SOCKET_CLOSED' || msg.key !== self.key) return;
self.flush();
});
}
util.inherits(SocketListSend, EventEmitter);
SocketListSend.prototype.add = function(socket) {
this.list.push(socket);
};
SocketListSend.prototype.flush = function() {
var list = this.list;
this.list = [];
list.forEach(function(socket) {
socket.destroy();
});
};
SocketListSend.prototype.update = function() {
if (this.slave.connected === false) return;
this.slave.send({
cmd: 'NODE_SOCKET_FETCH',
key: this.key
});
};
// This object keep track of the socket there are received
function SocketListReceive(slave, key) {
var self = this;
this.key = key;
this.list = [];
this.slave = slave;
slave.on('internalMessage', function(msg) {
if (msg.cmd !== 'NODE_SOCKET_FETCH' || msg.key !== self.key) return;
if (self.list.length === 0) {
self.flush();
return;
}
self.on('itemRemoved', function removeMe() {
if (self.list.length !== 0) return;
self.removeListener('itemRemoved', removeMe);
self.flush();
});
});
}
util.inherits(SocketListReceive, EventEmitter);
SocketListReceive.prototype.flush = function() {
this.list = [];
if (this.slave.connected) {
this.slave.send({
cmd: 'NODE_SOCKET_CLOSED',
key: this.key
});
}
};
SocketListReceive.prototype.add = function(socket) {
var self = this;
this.list.push(socket);
socket.on('close', function() {
self.list.splice(self.list.indexOf(socket), 1);
self.emit('itemRemoved');
});
};
function getSocketList(type, slave, key) {
var sockets = slave._channel.sockets[type];
var socketList = sockets[key];
if (!socketList) {
var Construct = type === 'send' ? SocketListSend : SocketListReceive;
socketList = sockets[key] = new Construct(slave, key);
}
return socketList;
}
function handleMessage(target, message, handle) {
//Filter out internal messages
//if cmd property begin with "_NODE"
if (message !== null &&
typeof message === 'object' &&
typeof message.cmd === 'string' &&
message.cmd.indexOf('NODE_') === 0) {
target.emit('internalMessage', message, handle);
}
//Non-internal message
else {
target.emit('message', message, handle);
}
}
function setupChannel(target, channel) {
target._channel = channel;
var jsonBuffer = '';
channel.buffering = false;
channel.onread = function(pool, offset, length, recvHandle) {
// Update simultaneous accepts on Windows
net._setSimultaneousAccepts(recvHandle);
if (pool) {
jsonBuffer += pool.toString('ascii', offset, offset + length);
@ -73,18 +265,7 @@ function setupChannel(target, channel) {
var json = jsonBuffer.slice(start, i);
var message = JSON.parse(json);
//Filter out internal messages
//if cmd property begin with "_NODE"
if (message !== null &&
typeof message === 'object' &&
typeof message.cmd === 'string' &&
message.cmd.indexOf('NODE_') === 0) {
target.emit('internalMessage', message, recvHandle);
}
//Non-internal message
else {
target.emit('message', message, recvHandle);
}
handleMessage(target, message, recvHandle);
start = i + 1;
}
@ -97,7 +278,27 @@ function setupChannel(target, channel) {
}
};
target.send = function(message, sendHandle) {
// object where socket lists will live
channel.sockets = { got: {}, send: {} };
// handlers will go through this
target.on('internalMessage', function(message, handle) {
if (message.cmd !== 'NODE_HANDLE') return;
var obj = handleConversion[message.type];
// Update simultaneous accepts on Windows
if (obj.simultaneousAccepts) {
net._setSimultaneousAccepts(handle);
}
// Convert handle object
obj.got.call(this, message, handle, function(handle) {
handleMessage(target, message.msg, handle);
});
});
target.send = function(message, handle) {
if (typeof message === 'undefined') {
throw new TypeError('message cannot be undefined');
}
@ -112,12 +313,43 @@ function setupChannel(target, channel) {
return false;
}
var string = JSON.stringify(message) + '\n';
// package messages with a handle object
if (handle) {
// this message will be handled by an internalMessage event handler
message = {
cmd: 'NODE_HANDLE',
type: 'net.',
msg: message
};
switch (handle.constructor.name) {
case 'Socket':
message.type += 'Socket'; break;
case 'Server':
message.type += 'Server'; break;
case 'Pipe':
case 'TCP':
message.type += 'Native'; break;
}
// Update simultaneous accepts on Windows
net._setSimultaneousAccepts(sendHandle);
var obj = handleConversion[message.type];
// convert TCP object to native handle object
handle = handleConversion[message.type].send.apply(target, arguments);
var writeReq = channel.writeUtf8String(string, sendHandle);
// Update simultaneous accepts on Windows
if (obj.simultaneousAccepts) {
net._setSimultaneousAccepts(handle);
}
}
var string = JSON.stringify(message) + '\n';
var writeReq = channel.writeUtf8String(string, handle);
// Close the Socket handle after sending it
if (message && message.type === 'net.Socket') {
handle.close();
}
if (!writeReq) {
var er = errnoException(errno, 'write', 'cannot write to IPC channel.');
@ -172,10 +404,10 @@ exports.fork = function(modulePath /*, args, options*/) {
var options, args, execArgv;
if (Array.isArray(arguments[1])) {
args = arguments[1];
options = arguments[2] || {};
options = util._extend({}, arguments[2]);
} else {
args = [];
options = arguments[1] || {};
options = util._extend({}, arguments[1]);
}
// Prepare arguments for fork:
@ -264,15 +496,12 @@ exports.execFile = function(file /* args, options, callback */) {
if (Array.isArray(arguments[1])) {
args = arguments[1];
if (typeof arguments[2] === 'object') optionArg = arguments[2];
options = util._extend(options, arguments[2]);
} else {
args = [];
if (typeof arguments[1] === 'object') optionArg = arguments[1];
options = util._extend(options, arguments[1]);
}
// Merge optionArg into options
util._extend(options, optionArg);
var child = spawn(file, args, {
cwd: options.cwd,
env: options.env,
@ -398,8 +627,10 @@ function ChildProcess() {
this.exitCode = null;
this.killed = false;
this._internal = new Process();
this._internal.onexit = function(exitCode, signalCode) {
this._handle = new Process();
this._handle.owner = this;
this._handle.onexit = function(exitCode, signalCode) {
//
// follow 0.4.x behaviour:
//
@ -416,8 +647,8 @@ function ChildProcess() {
self.stdin.destroy();
}
self._internal.close();
self._internal = null;
self._handle.close();
self._handle = null;
self.emit('exit', self.exitCode, self.signalCode);
@ -452,7 +683,7 @@ ChildProcess.prototype.spawn = function(options) {
setStreamOption('stdoutStream', 1, options);
setStreamOption('stderrStream', 2, options);
var r = this._internal.spawn(options);
var r = this._handle.spawn(options);
if (r) {
if (options.stdinStream) {
@ -467,12 +698,12 @@ ChildProcess.prototype.spawn = function(options) {
options.stderrStream.close();
}
this._internal.close();
this._internal = null;
this._handle.close();
this._handle = null;
throw errnoException(errno, 'spawn');
}
this.pid = this._internal.pid;
this.pid = this._handle.pid;
if (options.stdinStream) {
this.stdin = createSocket(options.stdinStream, false);
@ -525,9 +756,9 @@ ChildProcess.prototype.kill = function(sig) {
throw new Error('Unknown signal: ' + sig);
}
if (this._internal) {
if (this._handle) {
this.killed = true;
var r = this._internal.kill(signal);
var r = this._handle.kill(signal);
if (r === -1) {
this.emit('error', errnoException(errno, 'kill'));
return;

6
lib/dgram.js

@ -91,7 +91,7 @@ function Socket(type, listener) {
events.EventEmitter.call(this);
var handle = newHandle(type);
handle.socket = this;
handle.owner = this;
this._handle = handle;
this._receiving = false;
@ -200,7 +200,7 @@ Socket.prototype.send = function(buffer,
function afterSend(status, handle, req, buffer) {
var self = handle.socket;
var self = handle.owner;
// CHECKME socket's been closed by user, don't call callback?
if (handle !== self._handle)
@ -344,7 +344,7 @@ Socket.prototype._stopReceiving = function() {
function onMessage(handle, slab, start, len, rinfo) {
var self = handle.socket;
var self = handle.owner;
if (!slab) return self.emit('error', errnoException(errno, 'recvmsg'));
rinfo.size = len; // compatibility
self.emit('message', slab.slice(start, start + len), rinfo);

1
lib/fs.js

@ -737,6 +737,7 @@ function FSWatcher() {
var self = this;
var FSEvent = process.binding('fs_event_wrap').FSEvent;
this._handle = new FSEvent();
this._handle.owner = this;
this._handle.onchange = function(status, event, filename) {
if (status) {

4
lib/http.js

@ -475,6 +475,10 @@ OutgoingMessage.prototype._send = function(data, encoding) {
OutgoingMessage.prototype._writeRaw = function(data, encoding) {
if (data.length === 0) {
return true;
}
if (this.connection &&
this.connection._httpMessage === this &&
this.connection.writable) {

62
lib/net.js

@ -120,7 +120,7 @@ function initSocketHandle(self) {
// Handle creation may be deferred to bind() or connect() time.
if (self._handle) {
self._handle.socket = self;
self._handle.owner = self;
self._handle.onread = onread;
}
}
@ -291,7 +291,7 @@ Socket.prototype.end = function(data, encoding) {
function afterShutdown(status, handle, req) {
var self = handle.socket;
var self = handle.owner;
assert.ok(self._flags & FLAG_SHUTDOWN);
assert.ok(!self.writable);
@ -352,7 +352,7 @@ Socket.prototype._destroy = function(exception, cb) {
timers.unenroll(this);
if (this.server) {
this.server.connections--;
this.server._connections--;
this.server._emitCloseIfDrained();
}
@ -380,7 +380,7 @@ Socket.prototype.destroy = function(exception) {
function onread(buffer, offset, length) {
var handle = this;
var self = handle.socket;
var self = handle.owner;
assert.equal(handle, self._handle);
timers.active(self);
@ -583,7 +583,7 @@ Socket.prototype.__defineGetter__('bytesWritten', function() {
function afterWrite(status, handle, req) {
var self = handle.socket;
var self = handle.owner;
// callback may come after call to destroy.
if (self.destroyed) {
@ -722,7 +722,7 @@ Socket.prototype.connect = function(options, cb) {
function afterConnect(status, handle, req, readable, writable) {
var self = handle.socket;
var self = handle.owner;
// callback may come after call to destroy
if (self.destroyed) {
@ -800,7 +800,23 @@ function Server(/* [ options, ] listener */) {
}
}
this.connections = 0;
this._connections = 0;
// when server is using slaves .connections is not reliable
// so null will be return if thats the case
Object.defineProperty(this, 'connections', {
get: function() {
if (self._usingSlaves) {
return null;
}
return self._connections;
},
set: function(val) {
return (self._connections = val);
},
configurable: true, enumerable: true
});
this.allowHalfOpen = options.allowHalfOpen || false;
this._handle = null;
@ -865,7 +881,7 @@ Server.prototype._listen2 = function(address, port, addressType, backlog) {
}
self._handle.onconnection = onconnection;
self._handle.socket = self;
self._handle.owner = self;
// Use a backlog of 512 entries. We pass 511 to the listen() call because
// the kernel does: backlogsize = roundup_pow_of_two(backlogsize + 1);
@ -881,6 +897,9 @@ Server.prototype._listen2 = function(address, port, addressType, backlog) {
return;
}
// generate connection key, this should be unique to the connection
this._connectionKey = addressType + ':' + address + ':' + port;
process.nextTick(function() {
self.emit('listening');
});
@ -961,7 +980,7 @@ Server.prototype.address = function() {
function onconnection(clientHandle) {
var handle = this;
var self = handle.socket;
var self = handle.owner;
debug('onconnection');
@ -970,7 +989,7 @@ function onconnection(clientHandle) {
return;
}
if (self.maxConnections && self.connections >= self.maxConnections) {
if (self.maxConnections && self._connections >= self.maxConnections) {
clientHandle.close();
return;
}
@ -983,7 +1002,7 @@ function onconnection(clientHandle) {
socket.resume();
self.connections++;
self._connections++;
socket.server = self;
DTRACE_NET_SERVER_CONNECTION(socket);
@ -1005,13 +1024,21 @@ Server.prototype.close = function(cb) {
this._handle = null;
this._emitCloseIfDrained();
// fetch new socket lists
if (this._usingSlaves) {
this._slaves.forEach(function(socketList) {
if (socketList.list.length === 0) return;
socketList.update();
});
}
return this;
};
Server.prototype._emitCloseIfDrained = function() {
var self = this;
if (self._handle || self.connections) return;
if (self._handle || self._connections) return;
process.nextTick(function() {
self.emit('close');
@ -1023,6 +1050,15 @@ Server.prototype.listenFD = function(fd, type) {
throw new Error('This API is no longer supported. See child_process.fork');
};
// when sending a socket using fork IPC this function is executed
Server.prototype._setupSlave = function(socketList) {
if (!this._usingSlaves) {
this._usingSlaves = true;
this._slaves = [];
}
this._slaves.push(socketList);
};
// TODO: isIP should be moved to the DNS code. Putting it here now because
// this is what the legacy system did.
@ -1030,7 +1066,7 @@ Server.prototype.listenFD = function(fd, type) {
// and it does not detect more than one double : in a string.
exports.isIP = function(input) {
if (!input) {
return 4;
return 0;
} else if (/^(\d?\d?\d)\.(\d?\d?\d)\.(\d?\d?\d)\.(\d?\d?\d)$/.test(input)) {
var parts = input.split('.');
for (var i = 0; i < parts.length; i++) {

5
lib/querystring.js

@ -189,6 +189,11 @@ QueryString.parse = QueryString.decode = function(qs, sep, eq, options) {
kstr = x.substring(0, idx),
vstr = x.substring(idx + 1), k, v;
if (kstr === '' && vstr !== '') {
kstr = vstr;
vstr = '';
}
try {
k = decodeURIComponent(kstr);
v = decodeURIComponent(vstr);

2
lib/util.js

@ -509,7 +509,7 @@ exports.inherits = function(ctor, superCtor) {
exports._extend = function(origin, add) {
// Don't do anything if add isn't an object
if (!add) return origin;
if (!add || typeof add !== 'object') return origin;
var keys = Object.keys(add);
var i = keys.length;

1
node.gyp

@ -107,6 +107,7 @@
'src/node_script.h',
'src/node_string.h',
'src/node_version.h',
'src/ngx-queue.h',
'src/pipe_wrap.h',
'src/req_wrap.h',
'src/slab_allocator.h',

8
src/handle_wrap.cc

@ -20,10 +20,12 @@
// USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "node.h"
#include "ngx-queue.h"
#include "handle_wrap.h"
namespace node {
using v8::Array;
using v8::Object;
using v8::Handle;
using v8::Local;
@ -52,6 +54,10 @@ using v8::Integer;
}
// defined in node.cc
extern ngx_queue_t handle_wrap_queue;
void HandleWrap::Initialize(Handle<Object> target) {
/* Doesn't do anything at the moment. */
}
@ -125,6 +131,7 @@ HandleWrap::HandleWrap(Handle<Object> object, uv_handle_t* h) {
assert(object->InternalFieldCount() > 0);
object_ = v8::Persistent<v8::Object>::New(object);
object_->SetPointerInInternalField(0, this);
ngx_queue_insert_tail(&handle_wrap_queue, &handle_wrap_queue_);
}
@ -136,6 +143,7 @@ void HandleWrap::SetHandle(uv_handle_t* h) {
HandleWrap::~HandleWrap() {
assert(object_.IsEmpty());
ngx_queue_remove(&handle_wrap_queue_);
}

4
src/handle_wrap.h

@ -22,6 +22,8 @@
#ifndef HANDLE_WRAP_H_
#define HANDLE_WRAP_H_
#include "ngx-queue.h"
namespace node {
// Rules:
@ -61,7 +63,9 @@ class HandleWrap {
v8::Persistent<v8::Object> object_;
private:
friend v8::Handle<v8::Value> GetActiveHandles(const v8::Arguments&);
static void OnClose(uv_handle_t* handle);
ngx_queue_t handle_wrap_queue_;
// Using double underscore due to handle_ member in tcp_wrap. Probably
// tcp_wrap should rename it's member to 'handle'.
uv_handle_t* handle__;

106
src/ngx-queue.h

@ -0,0 +1,106 @@
/*
* Copyright (C) Igor Sysoev
*/
#ifndef NGX_QUEUE_H_INCLUDED_
#define NGX_QUEUE_H_INCLUDED_
typedef struct ngx_queue_s ngx_queue_t;
struct ngx_queue_s {
ngx_queue_t *prev;
ngx_queue_t *next;
};
#define ngx_queue_init(q) \
(q)->prev = q; \
(q)->next = q
#define ngx_queue_empty(h) \
(h == (h)->prev)
#define ngx_queue_insert_head(h, x) \
(x)->next = (h)->next; \
(x)->next->prev = x; \
(x)->prev = h; \
(h)->next = x
#define ngx_queue_insert_after ngx_queue_insert_head
#define ngx_queue_insert_tail(h, x) \
(x)->prev = (h)->prev; \
(x)->prev->next = x; \
(x)->next = h; \
(h)->prev = x
#define ngx_queue_head(h) \
(h)->next
#define ngx_queue_last(h) \
(h)->prev
#define ngx_queue_sentinel(h) \
(h)
#define ngx_queue_next(q) \
(q)->next
#define ngx_queue_prev(q) \
(q)->prev
#if (NGX_DEBUG)
#define ngx_queue_remove(x) \
(x)->next->prev = (x)->prev; \
(x)->prev->next = (x)->next; \
(x)->prev = NULL; \
(x)->next = NULL
#else
#define ngx_queue_remove(x) \
(x)->next->prev = (x)->prev; \
(x)->prev->next = (x)->next
#endif
#define ngx_queue_split(h, q, n) \
(n)->prev = (h)->prev; \
(n)->prev->next = n; \
(n)->next = q; \
(h)->prev = (q)->prev; \
(h)->prev->next = h; \
(q)->prev = n;
#define ngx_queue_add(h, n) \
(h)->prev->next = (n)->next; \
(n)->next->prev = (h)->prev; \
(h)->prev = (n)->prev; \
(h)->prev->next = h;
#define ngx_queue_data(q, type, link) \
(type *) ((unsigned char *) q - offsetof(type, link))
#define ngx_queue_foreach(q, h) \
for ((q) = ngx_queue_head(h); (q) != (h); (q) = ngx_queue_next(q))
#endif /* NGX_QUEUE_H_INCLUDED_ */

58
src/node.cc

@ -20,6 +20,8 @@
// USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "node.h"
#include "req_wrap.h"
#include "handle_wrap.h"
#include "uv.h"
@ -90,6 +92,13 @@ extern char **environ;
namespace node {
ngx_queue_t handle_wrap_queue = { &handle_wrap_queue, &handle_wrap_queue };
ngx_queue_t req_wrap_queue = { &req_wrap_queue, &req_wrap_queue };
// declared in req_wrap.h
Persistent<String> process_symbol;
Persistent<String> domain_symbol;
static Persistent<Object> process;
static Persistent<String> errno_symbol;
@ -105,7 +114,6 @@ static Persistent<String> listeners_symbol;
static Persistent<String> uncaught_exception_symbol;
static Persistent<String> emit_symbol;
static Persistent<String> domain_symbol;
static Persistent<String> enter_symbol;
static Persistent<String> exit_symbol;
static Persistent<String> disposed_symbol;
@ -1019,8 +1027,7 @@ MakeCallback(const Handle<Object> object,
TryCatch try_catch;
if (domain_symbol.IsEmpty()) {
domain_symbol = NODE_PSYMBOL("domain");
if (enter_symbol.IsEmpty()) {
enter_symbol = NODE_PSYMBOL("enter");
exit_symbol = NODE_PSYMBOL("exit");
disposed_symbol = NODE_PSYMBOL("_disposed");
@ -1330,6 +1337,46 @@ Local<Value> ExecuteString(Handle<String> source, Handle<Value> filename) {
}
static Handle<Value> GetActiveRequests(const Arguments& args) {
HandleScope scope;
Local<Array> ary = Array::New();
ngx_queue_t* q = NULL;
int i = 0;
ngx_queue_foreach(q, &req_wrap_queue) {
ReqWrap<uv_req_t>* w = container_of(q, ReqWrap<uv_req_t>, req_wrap_queue_);
if (w->object_.IsEmpty()) continue;
ary->Set(i++, w->object_);
}
return scope.Close(ary);
}
// Non-static, friend of HandleWrap. Could have been a HandleWrap method but
// implemented here for consistency with GetActiveRequests().
Handle<Value> GetActiveHandles(const Arguments& args) {
HandleScope scope;
Local<Array> ary = Array::New();
ngx_queue_t* q = NULL;
int i = 0;
Local<String> owner_sym = String::New("owner");
ngx_queue_foreach(q, &handle_wrap_queue) {
HandleWrap* w = container_of(q, HandleWrap, handle_wrap_queue_);
if (w->object_.IsEmpty() || w->unref) continue;
Local<Value> obj = w->object_->Get(owner_sym);
if (obj->IsUndefined()) obj = *w->object_;
ary->Set(i++, obj);
}
return scope.Close(ary);
}
static Handle<Value> Abort(const Arguments& args) {
abort();
return Undefined();
@ -2239,6 +2286,8 @@ Handle<Object> SetupProcessObject(int argc, char *argv[]) {
// define various internal methods
NODE_SET_METHOD(process, "_getActiveRequests", GetActiveRequests);
NODE_SET_METHOD(process, "_getActiveHandles", GetActiveHandles);
NODE_SET_METHOD(process, "_needTickCallback", NeedTickCallback);
NODE_SET_METHOD(process, "reallyExit", Exit);
NODE_SET_METHOD(process, "abort", Abort);
@ -2869,6 +2918,9 @@ int Start(int argc, char *argv[]) {
Persistent<Context> context = Context::New();
Context::Scope context_scope(context);
process_symbol = NODE_PSYMBOL("process");
domain_symbol = NODE_PSYMBOL("domain");
// Use original argv, as we're just copying values out of it.
Handle<Object> process_l = SetupProcessObject(argc, argv);
v8_typed_array::AttachBindings(context->Global());

20
src/req_wrap.h

@ -22,10 +22,14 @@
#ifndef REQ_WRAP_H_
#define REQ_WRAP_H_
#include "ngx-queue.h"
namespace node {
static v8::Persistent<v8::String> process_symbol;
static v8::Persistent<v8::String> domain_symbol;
// defined in node.cc
extern v8::Persistent<v8::String> process_symbol;
extern v8::Persistent<v8::String> domain_symbol;
extern ngx_queue_t req_wrap_queue;
template <typename T>
class ReqWrap {
@ -34,12 +38,6 @@ class ReqWrap {
v8::HandleScope scope;
object_ = v8::Persistent<v8::Object>::New(v8::Object::New());
// TODO: grab a handle to the current process.domain
if (process_symbol.IsEmpty()) {
process_symbol = NODE_PSYMBOL("process");
domain_symbol = NODE_PSYMBOL("domain");
}
v8::Local<v8::Value> domain = v8::Context::GetCurrent()
->Global()
->Get(process_symbol)
@ -50,10 +48,13 @@ class ReqWrap {
// fprintf(stderr, "setting domain on ReqWrap\n");
object_->Set(domain_symbol, domain);
}
ngx_queue_insert_tail(&req_wrap_queue, &req_wrap_queue_);
}
~ReqWrap() {
ngx_queue_remove(&req_wrap_queue_);
// Assert that someone has called Dispatched()
assert(req_.data == this);
assert(!object_.IsEmpty());
@ -67,8 +68,9 @@ class ReqWrap {
}
v8::Persistent<v8::Object> object_;
T req_;
ngx_queue_t req_wrap_queue_;
void* data_;
T req_; // *must* be last, GetActiveRequests() in node.cc depends on it
};

58
test/pummel/test-net-connect-econnrefused.js

@ -0,0 +1,58 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
// verify that connect reqs are properly cleaned up
var common = require('../common');
var assert = require('assert');
var net = require('net');
var ROUNDS = 1024;
var rounds = 0;
var reqs = 0;
pummel();
function pummel() {
net.createConnection(common.PORT).on('error', function(err) {
assert.equal(err.code, 'ECONNREFUSED');
if (++rounds < ROUNDS) return pummel();
check();
});
reqs++;
}
function check() {
process.nextTick(function() {
process.nextTick(function() {
assert.equal(process._getActiveRequests().length, 0);
assert.equal(process._getActiveHandles().length, 0);
check_called = true;
});
});
}
var check_called = false;
process.on('exit', function() {
assert.equal(rounds, ROUNDS);
assert.equal(reqs, ROUNDS);
assert(check_called);
});

198
test/simple/test-child-process-fork-net.js

@ -0,0 +1,198 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var assert = require('assert');
var common = require('../common');
var fork = require('child_process').fork;
var net = require('net');
// progress tracker
function ProgressTracker(missing, callback) {
this.missing = missing;
this.callback = callback;
}
ProgressTracker.prototype.done = function() {
this.missing -= 1;
this.check();
};
ProgressTracker.prototype.check = function() {
if (this.missing === 0) this.callback();
};
if (process.argv[2] === 'child') {
var serverScope;
process.on('message', function onServer(msg, server) {
if (msg.what !== 'server') return;
process.removeListener('message', onServer);
serverScope = server;
server.on('connection', function(socket) {
console.log('CHILD: got connection');
process.send({what: 'connection'});
socket.destroy();
});
// start making connection from parent
console.log('CHILD: server listening');
process.send({what: 'listening'});
});
process.on('message', function onClose(msg) {
if (msg.what !== 'close') return;
process.removeListener('message', onClose);
serverScope.on('close', function() {
process.send({what: 'close'});
});
serverScope.close();
});
process.on('message', function onSocket(msg, socket) {
if (msg.what !== 'socket') return;
process.removeListener('message', onSocket);
socket.end('echo');
console.log('CHILD: got socket');
});
process.send({what: 'ready'});
} else {
var child = fork(process.argv[1], ['child']);
child.on('exit', function() {
console.log('CHILD: died');
});
// send net.Server to child and test by connecting
var testServer = function(callback) {
// destroy server execute callback when done
var progress = new ProgressTracker(2, function() {
server.on('close', function() {
console.log('PARENT: server closed');
child.send({what: 'close'});
});
server.close();
});
// we expect 10 connections and close events
var connections = new ProgressTracker(10, progress.done.bind(progress));
var closed = new ProgressTracker(10, progress.done.bind(progress));
// create server and send it to child
var server = net.createServer();
server.on('connection', function(socket) {
console.log('PARENT: got connection');
socket.destroy();
connections.done();
});
server.on('listening', function() {
console.log('PARENT: server listening');
child.send({what: 'server'}, server);
});
server.listen(common.PORT);
// handle client messages
var messageHandlers = function(msg) {
if (msg.what === 'listening') {
// make connections
var socket;
for (var i = 0; i < 10; i++) {
socket = net.connect(common.PORT, function() {
console.log('CLIENT: connected');
});
socket.on('close', function() {
closed.done();
console.log('CLIENT: closed');
});
}
} else if (msg.what === 'connection') {
// child got connection
connections.done();
} else if (msg.what === 'close') {
child.removeListener('message', messageHandlers);
callback();
}
};
child.on('message', messageHandlers);
};
// send net.Socket to child
var testSocket = function(callback) {
// create a new server and connect to it,
// but the socket will be handled by the child
var server = net.createServer();
server.on('connection', function(socket) {
socket.on('close', function() {
console.log('CLIENT: socket closed');
});
child.send({what: 'socket'}, socket);
});
server.on('close', function() {
console.log('PARENT: server closed');
callback();
});
server.listen(common.PORT, function() {
var connect = net.connect(common.PORT);
var store = '';
connect.on('data', function(chunk) {
store += chunk;
console.log('CLIENT: got data');
});
connect.on('close', function() {
console.log('CLIENT: closed');
assert.equal(store, 'echo');
server.close();
});
});
};
// create server and send it to child
var serverSucess = false;
var socketSucess = false;
child.on('message', function onReady(msg) {
if (msg.what !== 'ready') return;
child.removeListener('message', onReady);
testServer(function() {
serverSucess = true;
testSocket(function() {
socketSucess = true;
child.kill();
});
});
});
process.on('exit', function() {
assert.ok(serverSucess);
assert.ok(socketSucess);
});
}

131
test/simple/test-child-process-fork-net2.js

@ -0,0 +1,131 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var assert = require('assert');
var common = require('../common');
var fork = require('child_process').fork;
var net = require('net');
if (process.argv[2] === 'child') {
var endMe = null;
process.on('message', function(m, socket) {
if (!socket) return;
// will call .end('end') or .write('write');
socket[m](m);
// store the unfinished socket
if (m === 'write') {
endMe = socket;
}
});
process.on('message', function(m) {
if (m !== 'close') return;
endMe.end('end');
endMe = null;
});
process.on('disconnect', function() {
endMe.end('end');
endMe = null;
});
} else {
var child1 = fork(process.argv[1], ['child']);
var child2 = fork(process.argv[1], ['child']);
var child3 = fork(process.argv[1], ['child']);
var server = net.createServer();
var connected = 0;
server.on('connection', function(socket) {
switch (connected) {
case 0:
child1.send('end', socket); break;
case 1:
child1.send('write', socket); break;
case 2:
child2.send('end', socket); break;
case 3:
child2.send('write', socket); break;
case 4:
child3.send('end', socket); break;
case 5:
child3.send('write', socket); break;
}
connected += 1;
if (connected === 6) {
closeServer();
}
});
var disconnected = 0;
server.on('listening', function() {
var j = 6, client;
while (j--) {
client = net.connect(common.PORT, '127.0.0.1');
client.on('close', function() {
disconnected += 1;
});
}
});
var closeEmitted = false;
server.on('close', function() {
closeEmitted = true;
child1.kill();
child2.kill();
child3.kill();
});
server.listen(common.PORT, '127.0.0.1');
var timeElasped = 0;
var closeServer = function() {
var startTime = Date.now();
server.on('close', function() {
timeElasped = Date.now() - startTime;
});
server.close();
setTimeout(function() {
child1.send('close');
child2.send('close');
child3.disconnect();
}, 200);
};
process.on('exit', function() {
assert.equal(disconnected, 6);
assert.equal(connected, 6);
assert.ok(closeEmitted);
assert.ok(timeElasped >= 190 && timeElasped <= 1000,
'timeElasped was not between 190 and 1000 ms');
});
}

2
test/simple/test-child-process-kill-throw.js

@ -30,7 +30,7 @@ if (process.argv[2] === 'child') {
var error = {};
child.on('exit', function() {
child._internal = {
child._handle = {
kill: function() {
global.errno = 42;
return -1;

27
test/simple/test-dgram-ref.js

@ -0,0 +1,27 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common');
var dgram = require('dgram');
// should not hang, see #1282
dgram.createSocket('udp4');
dgram.createSocket('udp6');

58
test/simple/test-http-client-pipe-end.js

@ -0,0 +1,58 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
// see https://github.com/joyent/node/issues/3257
var common = require('../common');
var assert = require('assert');
var http = require('http');
var server = http.createServer(function(req, res) {
req.once('end', function() {
res.writeHead(200);
res.end();
server.close();
});
});
server.listen(common.PIPE, function() {
var req = http.request({
socketPath: common.PIPE,
headers: {'Content-Length':'1'},
method: 'POST',
path: '/'
});
req.write('.');
sched(function() { req.end() }, 5);
});
// schedule a callback after `ticks` event loop ticks
function sched(cb, ticks) {
function fn() {
if (--ticks)
process.nextTick(fn);
else
cb();
}
process.nextTick(fn);
}

7
test/simple/test-net-isip.js

@ -36,12 +36,13 @@ assert.equal(net.isIP('::1'), 6);
assert.equal(net.isIP('::'), 6);
assert.equal(net.isIP('0000:0000:0000:0000:0000:0000:12345:0000'), 0);
assert.equal(net.isIP('0'), 0);
assert.equal(net.isIP(), 0);
assert.equal(net.isIP(""), 0);
assert.equal(net.isIPv4('127.0.0.1'), true);
assert.equal(net.isIPv4('example.com'), false);
assert.equal(net.isIPv4('2001:252:0:1::2008:6'), false);
assert.equal(net.isIPv6('127.0.0.1'), false);
assert.equal(net.isIPv4('example.com'), false);
assert.equal(net.isIPv6('2001:252:0:1::2008:6'), true);
assert.equal(net.isIPv6('example.com'), false);
assert.equal(net.isIPv6('2001:252:0:1::2008:6'), true);

54
test/simple/test-process-active-wraps.js

@ -0,0 +1,54 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common');
var assert = require('assert');
var spawn = require('child_process').spawn;
var net = require('net');
function expect(activeHandles, activeRequests) {
assert.equal(process._getActiveHandles().length, activeHandles);
assert.equal(process._getActiveRequests().length, activeRequests);
}
(function() {
expect(0, 0);
var server = net.createServer().listen(common.PORT);
expect(1, 0);
server.close();
expect(1, 0); // server handle doesn't shut down until next tick
})();
(function() {
expect(1, 0);
var conn = net.createConnection(common.PORT);
conn.on('error', function() { /* ignore */ });
expect(2, 1);
conn.destroy();
expect(2, 1); // client handle doesn't shut down until next tick
})();
process.nextTick(function() {
process.nextTick(function() {
// the handles should be gone but the connect req could still be alive
assert.equal(process._getActiveHandles().length, 0);
});
});

4
test/simple/test-querystring.js

@ -55,7 +55,9 @@ var qsTestCases = [
{ hasOwnProperty: 'x',
toString: 'foo',
valueOf: 'bar',
__defineGetter__: 'baz' }]
__defineGetter__: 'baz' }],
// See: https://github.com/joyent/node/issues/3058
['foo&bar=baz', 'foo=&bar=baz', { foo: '', bar: 'baz' }]
];
// [ wonkyQS, canonicalQS, obj ]

9
test/simple/test-util.js

@ -69,3 +69,12 @@ assert.equal(false, util.isError({}));
assert.equal(false, util.isError({ name: 'Error', message: '' }));
assert.equal(false, util.isError([]));
assert.equal(false, util.isError(Object.create(Error.prototype)));
// _extend
assert.deepEqual(util._extend({a:1}), {a:1});
assert.deepEqual(util._extend({a:1}, []), {a:1});
assert.deepEqual(util._extend({a:1}, null), {a:1});
assert.deepEqual(util._extend({a:1}, true), {a:1});
assert.deepEqual(util._extend({a:1}, false), {a:1});
assert.deepEqual(util._extend({a:1}, {b:2}), {a:1, b:2});
assert.deepEqual(util._extend({a:1, b:2}, {b:3}), {a:1, b:3});

Loading…
Cancel
Save