child_process: new stdio API for .spawn() method
This commit is contained in:
parent
30a0e58d63
commit
af98fc9d5f
@ -25,17 +25,38 @@ var Process = process.binding('process_wrap').Process;
|
|||||||
var util = require('util');
|
var util = require('util');
|
||||||
var constants; // if (!constants) constants = process.binding('constants');
|
var constants; // if (!constants) constants = process.binding('constants');
|
||||||
|
|
||||||
var Pipe;
|
var handleWraps = {};
|
||||||
|
|
||||||
|
function handleWrapGetter(name, callback) {
|
||||||
|
var cons;
|
||||||
|
|
||||||
|
Object.defineProperty(handleWraps, name, {
|
||||||
|
get: function() {
|
||||||
|
if (cons !== undefined) return cons;
|
||||||
|
return cons = callback();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleWrapGetter('Pipe', function() {
|
||||||
|
return process.binding('pipe_wrap').Pipe;
|
||||||
|
});
|
||||||
|
|
||||||
|
handleWrapGetter('TTY', function() {
|
||||||
|
return process.binding('tty_wrap').TTY;
|
||||||
|
});
|
||||||
|
|
||||||
|
handleWrapGetter('TCP', function() {
|
||||||
|
return process.binding('tcp_wrap').TCP;
|
||||||
|
});
|
||||||
|
|
||||||
|
handleWrapGetter('UDP', function() {
|
||||||
|
return process.binding('udp_wrap').UDP;
|
||||||
|
});
|
||||||
|
|
||||||
// constructors for lazy loading
|
// constructors for lazy loading
|
||||||
function createPipe(ipc) {
|
function createPipe(ipc) {
|
||||||
// Lazy load
|
return new handleWraps.Pipe(ipc);
|
||||||
if (!Pipe) {
|
|
||||||
Pipe = process.binding('pipe_wrap').Pipe;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Pipe(ipc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function createSocket(pipe, readable) {
|
function createSocket(pipe, readable) {
|
||||||
@ -414,39 +435,18 @@ exports.fork = function(modulePath /*, args, options*/) {
|
|||||||
execArgv = options.execArgv || process.execArgv;
|
execArgv = options.execArgv || process.execArgv;
|
||||||
args = execArgv.concat([modulePath], args);
|
args = execArgv.concat([modulePath], args);
|
||||||
|
|
||||||
// Don't allow stdinStream and customFds since a stdin channel will be used
|
|
||||||
if (options.stdinStream) {
|
|
||||||
throw new Error('stdinStream not allowed for fork()');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.customFds) {
|
|
||||||
throw new Error('customFds not allowed for fork()');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Leave stdin open for the IPC channel. stdout and stderr should be the
|
// Leave stdin open for the IPC channel. stdout and stderr should be the
|
||||||
// same as the parent's if silent isn't set.
|
// same as the parent's if silent isn't set.
|
||||||
options.customFds = (options.silent ? [-1, -1, -1] : [-1, 1, 2]);
|
options.stdio = options.silent ? ['ipc', 'pipe', 'pipe'] : ['ipc', 1, 2];
|
||||||
|
|
||||||
// Just need to set this - child process won't actually use the fd.
|
return spawn(process.execPath, args, options);
|
||||||
// For backwards compat - this can be changed to 'NODE_CHANNEL' before v0.6.
|
|
||||||
options.env = util._extend({}, options.env || process.env);
|
|
||||||
options.env.NODE_CHANNEL_FD = 42;
|
|
||||||
|
|
||||||
// stdin is the IPC channel.
|
|
||||||
options.stdinStream = createPipe(true);
|
|
||||||
|
|
||||||
var child = spawn(process.execPath, args, options);
|
|
||||||
|
|
||||||
setupChannel(child, options.stdinStream);
|
|
||||||
|
|
||||||
return child;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
exports._forkChild = function() {
|
exports._forkChild = function(fd) {
|
||||||
// set process.send()
|
// set process.send()
|
||||||
var p = createPipe(true);
|
var p = createPipe(true);
|
||||||
p.open(0);
|
p.open(fd);
|
||||||
setupChannel(process, p);
|
setupChannel(process, p);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -591,6 +591,11 @@ var spawn = exports.spawn = function(file, args, options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var child = new ChildProcess();
|
var child = new ChildProcess();
|
||||||
|
if (options && options.customFds && !options.stdio) {
|
||||||
|
options.stdio = options.customFds.map(function(fd) {
|
||||||
|
return fd === -1 ? 'pipe' : fd;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
child.spawn({
|
child.spawn({
|
||||||
file: file,
|
file: file,
|
||||||
@ -598,8 +603,7 @@ var spawn = exports.spawn = function(file, args, options) {
|
|||||||
cwd: options ? options.cwd : null,
|
cwd: options ? options.cwd : null,
|
||||||
windowsVerbatimArguments: !!(options && options.windowsVerbatimArguments),
|
windowsVerbatimArguments: !!(options && options.windowsVerbatimArguments),
|
||||||
envPairs: envPairs,
|
envPairs: envPairs,
|
||||||
customFds: options ? options.customFds : null,
|
stdio: options ? options.stdio : null,
|
||||||
stdinStream: options ? options.stdinStream : null,
|
|
||||||
uid: options ? options.uid : null,
|
uid: options ? options.uid : null,
|
||||||
gid: options ? options.gid : null
|
gid: options ? options.gid : null
|
||||||
});
|
});
|
||||||
@ -658,45 +662,110 @@ function ChildProcess() {
|
|||||||
util.inherits(ChildProcess, EventEmitter);
|
util.inherits(ChildProcess, EventEmitter);
|
||||||
|
|
||||||
|
|
||||||
function setStreamOption(name, index, options) {
|
function getHandleWrapType(stream) {
|
||||||
// Skip if we already have options.stdinStream
|
if (stream instanceof handleWraps.Pipe) return 'pipe';
|
||||||
if (options[name]) return;
|
if (stream instanceof handleWraps.TTY) return 'tty';
|
||||||
|
if (stream instanceof handleWraps.TCP) return 'tcp';
|
||||||
|
if (stream instanceof handleWraps.UDP) return 'udp';
|
||||||
|
|
||||||
if (options.customFds &&
|
return false;
|
||||||
typeof options.customFds[index] == 'number' &&
|
|
||||||
options.customFds[index] !== -1) {
|
|
||||||
if (options.customFds[index] === index) {
|
|
||||||
options[name] = null;
|
|
||||||
} else {
|
|
||||||
throw new Error('customFds not yet supported');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
options[name] = createPipe();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ChildProcess.prototype.spawn = function(options) {
|
ChildProcess.prototype.spawn = function(options) {
|
||||||
var self = this;
|
var self = this,
|
||||||
|
ipc,
|
||||||
|
ipcFd,
|
||||||
|
// If no `stdio` option was given - use default
|
||||||
|
stdio = options.stdio || 'pipe';
|
||||||
|
|
||||||
setStreamOption('stdinStream', 0, options);
|
// Replace shortcut with an array
|
||||||
setStreamOption('stdoutStream', 1, options);
|
if (typeof stdio === 'string') {
|
||||||
setStreamOption('stderrStream', 2, options);
|
switch (stdio) {
|
||||||
|
case 'ignore': stdio = ['ignore', 'ignore', 'ignore']; break;
|
||||||
|
case 'pipe': stdio = ['pipe', 'pipe', 'pipe']; break;
|
||||||
|
case 'inherit': stdio = [0, 1, 2]; break;
|
||||||
|
default: throw new TypeError('Incorrect value of stdio option: ' + stdio);
|
||||||
|
}
|
||||||
|
} else if (!Array.isArray(stdio)) {
|
||||||
|
throw new TypeError('Incorrect value of stdio option: ' + stdio);
|
||||||
|
}
|
||||||
|
|
||||||
|
// At least 3 stdio will be created
|
||||||
|
if (stdio.length < 3) {
|
||||||
|
stdio = stdio.concat(new Array(3 - stdio.length));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Translate stdio into C++-readable form
|
||||||
|
// (i.e. PipeWraps or fds)
|
||||||
|
stdio = stdio.reduce(function(acc, stdio, i) {
|
||||||
|
function cleanup() {
|
||||||
|
acc.filter(function(stdio) {
|
||||||
|
return stdio.type === 'pipe' || stdio.type === 'ipc';
|
||||||
|
}).forEach(function(stdio) {
|
||||||
|
stdio.handle.close();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defaults
|
||||||
|
if (stdio === undefined || stdio === null) {
|
||||||
|
stdio = i < 3 ? 'pipe' : 'ignore';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stdio === 'ignore') {
|
||||||
|
acc.push({type: 'ignore'});
|
||||||
|
} else if (stdio === 'pipe' || typeof stdio === 'number' && stdio < 0) {
|
||||||
|
acc.push({type: 'pipe', handle: createPipe()});
|
||||||
|
} else if (stdio === 'ipc') {
|
||||||
|
if (ipc !== undefined) {
|
||||||
|
// Cleanup previously created pipes
|
||||||
|
cleanup();
|
||||||
|
throw Error('Child process can have only one IPC pipe');
|
||||||
|
}
|
||||||
|
|
||||||
|
ipc = createPipe(true);
|
||||||
|
ipcFd = i;
|
||||||
|
|
||||||
|
acc.push({ type: 'pipe', handle: ipc });
|
||||||
|
} else if (typeof stdio === 'number' || typeof stdio.fd === 'number') {
|
||||||
|
acc.push({ type: 'fd', fd: stdio.fd || stdio });
|
||||||
|
} else if (getHandleWrapType(stdio) || getHandleWrapType(stdio.handle) ||
|
||||||
|
getHandleWrapType(stdio._handle)) {
|
||||||
|
var handle = getHandleWrapType(stdio) ?
|
||||||
|
stdio :
|
||||||
|
getHandleWrapType(stdio.handle) ? stdio.handle : stdio._handle;
|
||||||
|
|
||||||
|
acc.push({
|
||||||
|
type: 'wrap',
|
||||||
|
wrapType: getHandleWrapType(handle),
|
||||||
|
handle: handle
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Cleanup
|
||||||
|
cleanup();
|
||||||
|
throw new TypeError('Incorrect value for stdio stream: ' + stdio);
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
options.stdio = stdio;
|
||||||
|
|
||||||
|
if (ipc !== undefined) {
|
||||||
|
// Let child process know about opened IPC channel
|
||||||
|
options.envPairs = options.envPairs || [];
|
||||||
|
options.envPairs.push('NODE_CHANNEL_FD=' + ipcFd);
|
||||||
|
}
|
||||||
|
|
||||||
var r = this._handle.spawn(options);
|
var r = this._handle.spawn(options);
|
||||||
|
|
||||||
if (r) {
|
if (r) {
|
||||||
if (options.stdinStream) {
|
// Close all opened fds on error
|
||||||
options.stdinStream.close();
|
stdio.forEach(function(stdio) {
|
||||||
}
|
if (stdio.type === 'pipe') {
|
||||||
|
stdio.handle.close();
|
||||||
if (options.stdoutStream) {
|
}
|
||||||
options.stdoutStream.close();
|
});
|
||||||
}
|
|
||||||
|
|
||||||
if (options.stderrStream) {
|
|
||||||
options.stderrStream.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
this._handle.close();
|
this._handle.close();
|
||||||
this._handle = null;
|
this._handle = null;
|
||||||
@ -705,25 +774,36 @@ ChildProcess.prototype.spawn = function(options) {
|
|||||||
|
|
||||||
this.pid = this._handle.pid;
|
this.pid = this._handle.pid;
|
||||||
|
|
||||||
if (options.stdinStream) {
|
stdio.forEach(function(stdio, i) {
|
||||||
this.stdin = createSocket(options.stdinStream, false);
|
if (stdio.type === 'ignore') return;
|
||||||
}
|
|
||||||
|
|
||||||
if (options.stdoutStream) {
|
if (stdio.handle) {
|
||||||
this.stdout = createSocket(options.stdoutStream, true);
|
// when i === 0 - we're dealing with stdin
|
||||||
this._closesNeeded++;
|
// (which is the only one writable pipe)
|
||||||
this.stdout.on('close', function() {
|
stdio.socket = createSocket(stdio.handle, i > 0);
|
||||||
maybeClose(self);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.stderrStream) {
|
if (i > 0) {
|
||||||
this.stderr = createSocket(options.stderrStream, true);
|
self._closesNeeded++;
|
||||||
this._closesNeeded++;
|
stdio.socket.on('close', function() {
|
||||||
this.stderr.on('close', function() {
|
maybeClose(self);
|
||||||
maybeClose(self);
|
});
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.stdin = stdio.length >= 1 && stdio[0].socket !== undefined ?
|
||||||
|
stdio[0].socket : null;
|
||||||
|
this.stdout = stdio.length >= 2 && stdio[1].socket !== undefined ?
|
||||||
|
stdio[1].socket : null;
|
||||||
|
this.stderr = stdio.length >= 3 && stdio[2].socket !== undefined ?
|
||||||
|
stdio[2].socket : null;
|
||||||
|
|
||||||
|
this.stdio = stdio.map(function(stdio) {
|
||||||
|
return stdio.socket === undefined ? null : stdio.socket;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add .send() method and start listening for IPC data
|
||||||
|
if (ipc !== undefined) setupChannel(this, ipc);
|
||||||
|
|
||||||
return r;
|
return r;
|
||||||
};
|
};
|
||||||
|
3
node.gyp
3
node.gyp
@ -109,6 +109,9 @@
|
|||||||
'src/node_version.h',
|
'src/node_version.h',
|
||||||
'src/ngx-queue.h',
|
'src/ngx-queue.h',
|
||||||
'src/pipe_wrap.h',
|
'src/pipe_wrap.h',
|
||||||
|
'src/tty_wrap.h',
|
||||||
|
'src/tcp_wrap.h',
|
||||||
|
'src/udp_wrap.h',
|
||||||
'src/req_wrap.h',
|
'src/req_wrap.h',
|
||||||
'src/slab_allocator.h',
|
'src/slab_allocator.h',
|
||||||
'src/stream_wrap.h',
|
'src/stream_wrap.h',
|
||||||
|
@ -482,7 +482,8 @@
|
|||||||
// If we were spawned with env NODE_CHANNEL_FD then load that up and
|
// If we were spawned with env NODE_CHANNEL_FD then load that up and
|
||||||
// start parsing data from that stream.
|
// start parsing data from that stream.
|
||||||
if (process.env.NODE_CHANNEL_FD) {
|
if (process.env.NODE_CHANNEL_FD) {
|
||||||
assert(parseInt(process.env.NODE_CHANNEL_FD) >= 0);
|
var fd = parseInt(process.env.NODE_CHANNEL_FD, 10);
|
||||||
|
assert(fd >= 0);
|
||||||
|
|
||||||
// Make sure it's not accidentally inherited by child processes.
|
// Make sure it's not accidentally inherited by child processes.
|
||||||
delete process.env.NODE_CHANNEL_FD;
|
delete process.env.NODE_CHANNEL_FD;
|
||||||
@ -494,7 +495,7 @@
|
|||||||
// FIXME is this really necessary?
|
// FIXME is this really necessary?
|
||||||
process.binding('tcp_wrap');
|
process.binding('tcp_wrap');
|
||||||
|
|
||||||
cp._forkChild();
|
cp._forkChild(fd);
|
||||||
assert(process.send);
|
assert(process.send);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,10 @@
|
|||||||
#include "node.h"
|
#include "node.h"
|
||||||
#include "handle_wrap.h"
|
#include "handle_wrap.h"
|
||||||
#include "pipe_wrap.h"
|
#include "pipe_wrap.h"
|
||||||
|
#include "tty_wrap.h"
|
||||||
|
#include "tcp_wrap.h"
|
||||||
|
#include "udp_wrap.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
@ -36,6 +40,7 @@ using v8::HandleScope;
|
|||||||
using v8::FunctionTemplate;
|
using v8::FunctionTemplate;
|
||||||
using v8::String;
|
using v8::String;
|
||||||
using v8::Array;
|
using v8::Array;
|
||||||
|
using v8::Number;
|
||||||
using v8::Function;
|
using v8::Function;
|
||||||
using v8::TryCatch;
|
using v8::TryCatch;
|
||||||
using v8::Context;
|
using v8::Context;
|
||||||
@ -82,6 +87,56 @@ class ProcessWrap : public HandleWrap {
|
|||||||
ProcessWrap(Handle<Object> object) : HandleWrap(object, NULL) { }
|
ProcessWrap(Handle<Object> object) : HandleWrap(object, NULL) { }
|
||||||
~ProcessWrap() { }
|
~ProcessWrap() { }
|
||||||
|
|
||||||
|
static void ParseStdioOptions(Local<Object> js_options,
|
||||||
|
uv_process_options_t* options) {
|
||||||
|
Local<Array> stdios = js_options
|
||||||
|
->Get(String::NewSymbol("stdio")).As<Array>();
|
||||||
|
int len = stdios->Length();
|
||||||
|
options->stdio = new uv_stdio_container_t[len];
|
||||||
|
options->stdio_count = len;
|
||||||
|
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
Local<Object> stdio = stdios
|
||||||
|
->Get(Number::New(static_cast<double>(i))).As<Object>();
|
||||||
|
Local<Value> type = stdio->Get(String::NewSymbol("type"));
|
||||||
|
|
||||||
|
if (type->Equals(String::NewSymbol("ignore"))) {
|
||||||
|
options->stdio[i].flags = UV_IGNORE;
|
||||||
|
} else if (type->Equals(String::NewSymbol("pipe"))) {
|
||||||
|
options->stdio[i].flags = UV_CREATE_PIPE;
|
||||||
|
options->stdio[i].data.stream = reinterpret_cast<uv_stream_t*>(
|
||||||
|
PipeWrap::Unwrap(stdio
|
||||||
|
->Get(String::NewSymbol("handle")).As<Object>())->UVHandle());
|
||||||
|
} else if (type->Equals(String::NewSymbol("wrap"))) {
|
||||||
|
uv_stream_t* stream = NULL;
|
||||||
|
Local<Value> wrapType = stdio->Get(String::NewSymbol("wrapType"));
|
||||||
|
if (wrapType->Equals(String::NewSymbol("pipe"))) {
|
||||||
|
stream = reinterpret_cast<uv_stream_t*>(PipeWrap::Unwrap(stdio
|
||||||
|
->Get(String::NewSymbol("handle")).As<Object>())->UVHandle());
|
||||||
|
} else if (wrapType->Equals(String::NewSymbol("tty"))) {
|
||||||
|
stream = reinterpret_cast<uv_stream_t*>(TTYWrap::Unwrap(stdio
|
||||||
|
->Get(String::NewSymbol("handle")).As<Object>())->UVHandle());
|
||||||
|
} else if (wrapType->Equals(String::NewSymbol("tcp"))) {
|
||||||
|
stream = reinterpret_cast<uv_stream_t*>(TCPWrap::Unwrap(stdio
|
||||||
|
->Get(String::NewSymbol("handle")).As<Object>())->UVHandle());
|
||||||
|
} else if (wrapType->Equals(String::NewSymbol("udp"))) {
|
||||||
|
stream = reinterpret_cast<uv_stream_t*>(UDPWrap::Unwrap(stdio
|
||||||
|
->Get(String::NewSymbol("handle")).As<Object>())->UVHandle());
|
||||||
|
}
|
||||||
|
assert(stream != NULL);
|
||||||
|
|
||||||
|
options->stdio[i].flags = UV_INHERIT_STREAM;
|
||||||
|
options->stdio[i].data.stream = stream;
|
||||||
|
} else {
|
||||||
|
int fd = static_cast<int>(
|
||||||
|
stdio->Get(String::NewSymbol("fd"))->IntegerValue());
|
||||||
|
|
||||||
|
options->stdio[i].flags = UV_INHERIT_FD;
|
||||||
|
options->stdio[i].data.fd = fd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static Handle<Value> Spawn(const Arguments& args) {
|
static Handle<Value> Spawn(const Arguments& args) {
|
||||||
HandleScope scope;
|
HandleScope scope;
|
||||||
|
|
||||||
@ -169,47 +224,8 @@ class ProcessWrap : public HandleWrap {
|
|||||||
options.env[envc] = NULL;
|
options.env[envc] = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
uv_stdio_container_t stdio[3];
|
// options.stdio
|
||||||
memset(stdio, 0, sizeof(stdio));
|
ParseStdioOptions(js_options, &options);
|
||||||
|
|
||||||
options.stdio = stdio;
|
|
||||||
options.stdio_count = 3;
|
|
||||||
options.stdio[0].flags = UV_IGNORE;
|
|
||||||
options.stdio[1].flags = UV_IGNORE;
|
|
||||||
options.stdio[2].flags = UV_IGNORE;
|
|
||||||
|
|
||||||
// options.stdin_stream
|
|
||||||
Local<Value> stdin_stream_v = js_options->Get(
|
|
||||||
String::NewSymbol("stdinStream"));
|
|
||||||
if (!stdin_stream_v.IsEmpty() && stdin_stream_v->IsObject()) {
|
|
||||||
PipeWrap* stdin_wrap = PipeWrap::Unwrap(stdin_stream_v->ToObject());
|
|
||||||
options.stdio[0].flags = static_cast<uv_stdio_flags>(
|
|
||||||
UV_CREATE_PIPE | UV_WRITABLE_PIPE);
|
|
||||||
options.stdio[0].data.stream = reinterpret_cast<uv_stream_t*>(
|
|
||||||
stdin_wrap->UVHandle());
|
|
||||||
}
|
|
||||||
|
|
||||||
// options.stdout_stream
|
|
||||||
Local<Value> stdout_stream_v = js_options->Get(
|
|
||||||
String::NewSymbol("stdoutStream"));
|
|
||||||
if (!stdout_stream_v.IsEmpty() && stdout_stream_v->IsObject()) {
|
|
||||||
PipeWrap* stdout_wrap = PipeWrap::Unwrap(stdout_stream_v->ToObject());
|
|
||||||
options.stdio[1].flags = static_cast<uv_stdio_flags>(
|
|
||||||
UV_CREATE_PIPE | UV_READABLE_PIPE);
|
|
||||||
options.stdio[1].data.stream = reinterpret_cast<uv_stream_t*>(
|
|
||||||
stdout_wrap->UVHandle());
|
|
||||||
}
|
|
||||||
|
|
||||||
// options.stderr_stream
|
|
||||||
Local<Value> stderr_stream_v = js_options->Get(
|
|
||||||
String::NewSymbol("stderrStream"));
|
|
||||||
if (!stderr_stream_v.IsEmpty() && stderr_stream_v->IsObject()) {
|
|
||||||
PipeWrap* stderr_wrap = PipeWrap::Unwrap(stderr_stream_v->ToObject());
|
|
||||||
options.stdio[2].flags = static_cast<uv_stdio_flags>(
|
|
||||||
UV_CREATE_PIPE | UV_READABLE_PIPE);
|
|
||||||
options.stdio[2].data.stream = reinterpret_cast<uv_stream_t*>(
|
|
||||||
stderr_wrap->UVHandle());
|
|
||||||
}
|
|
||||||
|
|
||||||
// options.windows_verbatim_arguments
|
// options.windows_verbatim_arguments
|
||||||
if (js_options->Get(String::NewSymbol("windowsVerbatimArguments"))->
|
if (js_options->Get(String::NewSymbol("windowsVerbatimArguments"))->
|
||||||
@ -238,6 +254,8 @@ class ProcessWrap : public HandleWrap {
|
|||||||
delete [] options.env;
|
delete [] options.env;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
delete[] options.stdio;
|
||||||
|
|
||||||
return scope.Close(Integer::New(r));
|
return scope.Close(Integer::New(r));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,6 +128,18 @@ void TCPWrap::Initialize(Handle<Object> target) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TCPWrap* TCPWrap::Unwrap(Local<Object> obj) {
|
||||||
|
assert(!obj.IsEmpty());
|
||||||
|
assert(obj->InternalFieldCount() > 0);
|
||||||
|
return static_cast<TCPWrap*>(obj->GetPointerFromInternalField(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uv_tcp_t* TCPWrap::UVHandle() {
|
||||||
|
return &handle_;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Handle<Value> TCPWrap::New(const Arguments& args) {
|
Handle<Value> TCPWrap::New(const Arguments& args) {
|
||||||
// This constructor should not be exposed to public javascript.
|
// This constructor should not be exposed to public javascript.
|
||||||
// Therefore we assert that we are not trying to call this as a
|
// Therefore we assert that we are not trying to call this as a
|
||||||
|
@ -31,6 +31,8 @@ class TCPWrap : public StreamWrap {
|
|||||||
static TCPWrap* Unwrap(v8::Local<v8::Object> obj);
|
static TCPWrap* Unwrap(v8::Local<v8::Object> obj);
|
||||||
static void Initialize(v8::Handle<v8::Object> target);
|
static void Initialize(v8::Handle<v8::Object> target);
|
||||||
|
|
||||||
|
uv_tcp_t* UVHandle();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TCPWrap(v8::Handle<v8::Object> object);
|
TCPWrap(v8::Handle<v8::Object> object);
|
||||||
~TCPWrap();
|
~TCPWrap();
|
||||||
|
184
src/tty_wrap.cc
184
src/tty_wrap.cc
@ -24,6 +24,7 @@
|
|||||||
#include "req_wrap.h"
|
#include "req_wrap.h"
|
||||||
#include "handle_wrap.h"
|
#include "handle_wrap.h"
|
||||||
#include "stream_wrap.h"
|
#include "stream_wrap.h"
|
||||||
|
#include "tty_wrap.h"
|
||||||
|
|
||||||
namespace node {
|
namespace node {
|
||||||
|
|
||||||
@ -42,128 +43,141 @@ using v8::Arguments;
|
|||||||
using v8::Integer;
|
using v8::Integer;
|
||||||
using v8::Undefined;
|
using v8::Undefined;
|
||||||
|
|
||||||
class TTYWrap : StreamWrap {
|
|
||||||
public:
|
|
||||||
static void Initialize(Handle<Object> target) {
|
|
||||||
StreamWrap::Initialize(target);
|
|
||||||
|
|
||||||
HandleScope scope;
|
void TTYWrap::Initialize(Handle<Object> target) {
|
||||||
|
StreamWrap::Initialize(target);
|
||||||
|
|
||||||
Local<FunctionTemplate> t = FunctionTemplate::New(New);
|
HandleScope scope;
|
||||||
t->SetClassName(String::NewSymbol("TTY"));
|
|
||||||
|
|
||||||
t->InstanceTemplate()->SetInternalFieldCount(1);
|
Local<FunctionTemplate> t = FunctionTemplate::New(New);
|
||||||
|
t->SetClassName(String::NewSymbol("TTY"));
|
||||||
|
|
||||||
NODE_SET_PROTOTYPE_METHOD(t, "close", HandleWrap::Close);
|
t->InstanceTemplate()->SetInternalFieldCount(1);
|
||||||
NODE_SET_PROTOTYPE_METHOD(t, "unref", HandleWrap::Unref);
|
|
||||||
|
|
||||||
NODE_SET_PROTOTYPE_METHOD(t, "readStart", StreamWrap::ReadStart);
|
NODE_SET_PROTOTYPE_METHOD(t, "close", HandleWrap::Close);
|
||||||
NODE_SET_PROTOTYPE_METHOD(t, "readStop", StreamWrap::ReadStop);
|
NODE_SET_PROTOTYPE_METHOD(t, "unref", HandleWrap::Unref);
|
||||||
|
|
||||||
NODE_SET_PROTOTYPE_METHOD(t, "writeBuffer", StreamWrap::WriteBuffer);
|
NODE_SET_PROTOTYPE_METHOD(t, "readStart", StreamWrap::ReadStart);
|
||||||
NODE_SET_PROTOTYPE_METHOD(t, "writeAsciiString", StreamWrap::WriteAsciiString);
|
NODE_SET_PROTOTYPE_METHOD(t, "readStop", StreamWrap::ReadStop);
|
||||||
NODE_SET_PROTOTYPE_METHOD(t, "writeUtf8String", StreamWrap::WriteUtf8String);
|
|
||||||
NODE_SET_PROTOTYPE_METHOD(t, "writeUcs2String", StreamWrap::WriteUcs2String);
|
|
||||||
|
|
||||||
NODE_SET_PROTOTYPE_METHOD(t, "getWindowSize", TTYWrap::GetWindowSize);
|
NODE_SET_PROTOTYPE_METHOD(t, "writeBuffer", StreamWrap::WriteBuffer);
|
||||||
NODE_SET_PROTOTYPE_METHOD(t, "setRawMode", SetRawMode);
|
NODE_SET_PROTOTYPE_METHOD(t, "writeAsciiString", StreamWrap::WriteAsciiString);
|
||||||
|
NODE_SET_PROTOTYPE_METHOD(t, "writeUtf8String", StreamWrap::WriteUtf8String);
|
||||||
|
NODE_SET_PROTOTYPE_METHOD(t, "writeUcs2String", StreamWrap::WriteUcs2String);
|
||||||
|
|
||||||
NODE_SET_METHOD(target, "isTTY", IsTTY);
|
NODE_SET_PROTOTYPE_METHOD(t, "getWindowSize", TTYWrap::GetWindowSize);
|
||||||
NODE_SET_METHOD(target, "guessHandleType", GuessHandleType);
|
NODE_SET_PROTOTYPE_METHOD(t, "setRawMode", SetRawMode);
|
||||||
|
|
||||||
target->Set(String::NewSymbol("TTY"), t->GetFunction());
|
NODE_SET_METHOD(target, "isTTY", IsTTY);
|
||||||
}
|
NODE_SET_METHOD(target, "guessHandleType", GuessHandleType);
|
||||||
|
|
||||||
private:
|
target->Set(String::NewSymbol("TTY"), t->GetFunction());
|
||||||
static Handle<Value> GuessHandleType(const Arguments& args) {
|
}
|
||||||
HandleScope scope;
|
|
||||||
int fd = args[0]->Int32Value();
|
|
||||||
assert(fd >= 0);
|
|
||||||
|
|
||||||
uv_handle_type t = uv_guess_handle(fd);
|
|
||||||
|
|
||||||
switch (t) {
|
TTYWrap* TTYWrap::Unwrap(Local<Object> obj) {
|
||||||
case UV_TTY:
|
assert(!obj.IsEmpty());
|
||||||
return scope.Close(String::New("TTY"));
|
assert(obj->InternalFieldCount() > 0);
|
||||||
|
return static_cast<TTYWrap*>(obj->GetPointerFromInternalField(0));
|
||||||
|
}
|
||||||
|
|
||||||
case UV_NAMED_PIPE:
|
|
||||||
return scope.Close(String::New("PIPE"));
|
|
||||||
|
|
||||||
case UV_FILE:
|
uv_tty_t* TTYWrap::UVHandle() {
|
||||||
return scope.Close(String::New("FILE"));
|
return &handle_;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
|
||||||
assert(0);
|
|
||||||
return v8::Undefined();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static Handle<Value> IsTTY(const Arguments& args) {
|
Handle<Value> TTYWrap::GuessHandleType(const Arguments& args) {
|
||||||
HandleScope scope;
|
HandleScope scope;
|
||||||
int fd = args[0]->Int32Value();
|
int fd = args[0]->Int32Value();
|
||||||
assert(fd >= 0);
|
assert(fd >= 0);
|
||||||
return uv_guess_handle(fd) == UV_TTY ? v8::True() : v8::False();
|
|
||||||
}
|
|
||||||
|
|
||||||
static Handle<Value> GetWindowSize(const Arguments& args) {
|
uv_handle_type t = uv_guess_handle(fd);
|
||||||
HandleScope scope;
|
|
||||||
|
|
||||||
UNWRAP(TTYWrap)
|
switch (t) {
|
||||||
|
case UV_TTY:
|
||||||
|
return scope.Close(String::New("TTY"));
|
||||||
|
|
||||||
int width, height;
|
case UV_NAMED_PIPE:
|
||||||
int r = uv_tty_get_winsize(&wrap->handle_, &width, &height);
|
return scope.Close(String::New("PIPE"));
|
||||||
|
|
||||||
if (r) {
|
case UV_FILE:
|
||||||
SetErrno(uv_last_error(uv_default_loop()));
|
return scope.Close(String::New("FILE"));
|
||||||
|
|
||||||
|
default:
|
||||||
|
assert(0);
|
||||||
return v8::Undefined();
|
return v8::Undefined();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Local<v8::Array> a = v8::Array::New(2);
|
|
||||||
a->Set(0, Integer::New(width));
|
|
||||||
a->Set(1, Integer::New(height));
|
|
||||||
|
|
||||||
return scope.Close(a);
|
Handle<Value> TTYWrap::IsTTY(const Arguments& args) {
|
||||||
|
HandleScope scope;
|
||||||
|
int fd = args[0]->Int32Value();
|
||||||
|
assert(fd >= 0);
|
||||||
|
return uv_guess_handle(fd) == UV_TTY ? v8::True() : v8::False();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Handle<Value> TTYWrap::GetWindowSize(const Arguments& args) {
|
||||||
|
HandleScope scope;
|
||||||
|
|
||||||
|
UNWRAP(TTYWrap)
|
||||||
|
|
||||||
|
int width, height;
|
||||||
|
int r = uv_tty_get_winsize(&wrap->handle_, &width, &height);
|
||||||
|
|
||||||
|
if (r) {
|
||||||
|
SetErrno(uv_last_error(uv_default_loop()));
|
||||||
|
return v8::Undefined();
|
||||||
}
|
}
|
||||||
|
|
||||||
static Handle<Value> SetRawMode(const Arguments& args) {
|
Local<v8::Array> a = v8::Array::New(2);
|
||||||
HandleScope scope;
|
a->Set(0, Integer::New(width));
|
||||||
|
a->Set(1, Integer::New(height));
|
||||||
|
|
||||||
UNWRAP(TTYWrap)
|
return scope.Close(a);
|
||||||
|
}
|
||||||
|
|
||||||
int r = uv_tty_set_mode(&wrap->handle_, args[0]->IsTrue());
|
|
||||||
|
|
||||||
if (r) {
|
Handle<Value> TTYWrap::SetRawMode(const Arguments& args) {
|
||||||
SetErrno(uv_last_error(uv_default_loop()));
|
HandleScope scope;
|
||||||
}
|
|
||||||
|
|
||||||
return scope.Close(Integer::New(r));
|
UNWRAP(TTYWrap)
|
||||||
|
|
||||||
|
int r = uv_tty_set_mode(&wrap->handle_, args[0]->IsTrue());
|
||||||
|
|
||||||
|
if (r) {
|
||||||
|
SetErrno(uv_last_error(uv_default_loop()));
|
||||||
}
|
}
|
||||||
|
|
||||||
static Handle<Value> New(const Arguments& args) {
|
return scope.Close(Integer::New(r));
|
||||||
HandleScope scope;
|
}
|
||||||
|
|
||||||
// This constructor should not be exposed to public javascript.
|
|
||||||
// Therefore we assert that we are not trying to call this as a
|
|
||||||
// normal function.
|
|
||||||
assert(args.IsConstructCall());
|
|
||||||
|
|
||||||
int fd = args[0]->Int32Value();
|
Handle<Value> TTYWrap::New(const Arguments& args) {
|
||||||
assert(fd >= 0);
|
HandleScope scope;
|
||||||
|
|
||||||
TTYWrap* wrap = new TTYWrap(args.This(), fd, args[1]->IsTrue());
|
// This constructor should not be exposed to public javascript.
|
||||||
assert(wrap);
|
// Therefore we assert that we are not trying to call this as a
|
||||||
wrap->UpdateWriteQueueSize();
|
// normal function.
|
||||||
|
assert(args.IsConstructCall());
|
||||||
|
|
||||||
return scope.Close(args.This());
|
int fd = args[0]->Int32Value();
|
||||||
}
|
assert(fd >= 0);
|
||||||
|
|
||||||
TTYWrap(Handle<Object> object, int fd, bool readable)
|
TTYWrap* wrap = new TTYWrap(args.This(), fd, args[1]->IsTrue());
|
||||||
: StreamWrap(object, (uv_stream_t*)&handle_) {
|
assert(wrap);
|
||||||
uv_tty_init(uv_default_loop(), &handle_, fd, readable);
|
wrap->UpdateWriteQueueSize();
|
||||||
}
|
|
||||||
|
|
||||||
uv_tty_t handle_;
|
return scope.Close(args.This());
|
||||||
};
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TTYWrap::TTYWrap(Handle<Object> object, int fd, bool readable)
|
||||||
|
: StreamWrap(object, (uv_stream_t*)&handle_) {
|
||||||
|
uv_tty_init(uv_default_loop(), &handle_, fd, readable);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace node
|
} // namespace node
|
||||||
|
|
||||||
|
58
src/tty_wrap.h
Normal file
58
src/tty_wrap.h
Normal file
@ -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.
|
||||||
|
|
||||||
|
#ifndef TTY_WRAP_H_
|
||||||
|
#define TTY_WRAP_H_
|
||||||
|
|
||||||
|
#include "handle_wrap.h"
|
||||||
|
#include "stream_wrap.h"
|
||||||
|
|
||||||
|
namespace node {
|
||||||
|
|
||||||
|
using v8::Object;
|
||||||
|
using v8::Handle;
|
||||||
|
using v8::Local;
|
||||||
|
using v8::Value;
|
||||||
|
using v8::Arguments;
|
||||||
|
|
||||||
|
|
||||||
|
class TTYWrap : StreamWrap {
|
||||||
|
public:
|
||||||
|
static void Initialize(Handle<Object> target);
|
||||||
|
static TTYWrap* Unwrap(Local<Object> obj);
|
||||||
|
|
||||||
|
uv_tty_t* UVHandle();
|
||||||
|
|
||||||
|
private:
|
||||||
|
TTYWrap(Handle<Object> object, int fd, bool readable);
|
||||||
|
|
||||||
|
static Handle<Value> GuessHandleType(const Arguments& args);
|
||||||
|
static Handle<Value> IsTTY(const Arguments& args);
|
||||||
|
static Handle<Value> GetWindowSize(const Arguments& args);
|
||||||
|
static Handle<Value> SetRawMode(const Arguments& args);
|
||||||
|
static Handle<Value> New(const Arguments& args);
|
||||||
|
|
||||||
|
uv_tty_t handle_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace node
|
||||||
|
|
||||||
|
#endif // TTY_WRAP_H_
|
@ -24,6 +24,7 @@
|
|||||||
#include "slab_allocator.h"
|
#include "slab_allocator.h"
|
||||||
#include "req_wrap.h"
|
#include "req_wrap.h"
|
||||||
#include "handle_wrap.h"
|
#include "handle_wrap.h"
|
||||||
|
#include "udp_wrap.h"
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
@ -60,45 +61,6 @@ static Persistent<String> onmessage_sym;
|
|||||||
static SlabAllocator slab_allocator(SLAB_SIZE);
|
static SlabAllocator slab_allocator(SLAB_SIZE);
|
||||||
|
|
||||||
|
|
||||||
class UDPWrap: public HandleWrap {
|
|
||||||
public:
|
|
||||||
static void Initialize(Handle<Object> target);
|
|
||||||
static Handle<Value> New(const Arguments& args);
|
|
||||||
static Handle<Value> Bind(const Arguments& args);
|
|
||||||
static Handle<Value> Send(const Arguments& args);
|
|
||||||
static Handle<Value> Bind6(const Arguments& args);
|
|
||||||
static Handle<Value> Send6(const Arguments& args);
|
|
||||||
static Handle<Value> RecvStart(const Arguments& args);
|
|
||||||
static Handle<Value> RecvStop(const Arguments& args);
|
|
||||||
static Handle<Value> GetSockName(const Arguments& args);
|
|
||||||
static Handle<Value> AddMembership(const Arguments& args);
|
|
||||||
static Handle<Value> DropMembership(const Arguments& args);
|
|
||||||
static Handle<Value> SetMulticastTTL(const Arguments& args);
|
|
||||||
static Handle<Value> SetMulticastLoopback(const Arguments& args);
|
|
||||||
static Handle<Value> SetBroadcast(const Arguments& args);
|
|
||||||
static Handle<Value> SetTTL(const Arguments& args);
|
|
||||||
|
|
||||||
private:
|
|
||||||
UDPWrap(Handle<Object> object);
|
|
||||||
virtual ~UDPWrap();
|
|
||||||
|
|
||||||
static Handle<Value> DoBind(const Arguments& args, int family);
|
|
||||||
static Handle<Value> DoSend(const Arguments& args, int family);
|
|
||||||
static Handle<Value> SetMembership(const Arguments& args,
|
|
||||||
uv_membership membership);
|
|
||||||
|
|
||||||
static uv_buf_t OnAlloc(uv_handle_t* handle, size_t suggested_size);
|
|
||||||
static void OnSend(uv_udp_send_t* req, int status);
|
|
||||||
static void OnRecv(uv_udp_t* handle,
|
|
||||||
ssize_t nread,
|
|
||||||
uv_buf_t buf,
|
|
||||||
struct sockaddr* addr,
|
|
||||||
unsigned flags);
|
|
||||||
|
|
||||||
uv_udp_t handle_;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
UDPWrap::UDPWrap(Handle<Object> object): HandleWrap(object,
|
UDPWrap::UDPWrap(Handle<Object> object): HandleWrap(object,
|
||||||
(uv_handle_t*)&handle_) {
|
(uv_handle_t*)&handle_) {
|
||||||
int r = uv_udp_init(uv_default_loop(), &handle_);
|
int r = uv_udp_init(uv_default_loop(), &handle_);
|
||||||
@ -426,6 +388,18 @@ void UDPWrap::OnRecv(uv_udp_t* handle,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
UDPWrap* UDPWrap::Unwrap(Local<Object> obj) {
|
||||||
|
assert(!obj.IsEmpty());
|
||||||
|
assert(obj->InternalFieldCount() > 0);
|
||||||
|
return static_cast<UDPWrap*>(obj->GetPointerFromInternalField(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uv_udp_t* UDPWrap::UVHandle() {
|
||||||
|
return &handle_;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace node
|
} // namespace node
|
||||||
|
|
||||||
NODE_MODULE(node_udp_wrap, node::UDPWrap::Initialize)
|
NODE_MODULE(node_udp_wrap, node::UDPWrap::Initialize)
|
||||||
|
60
src/udp_wrap.h
Normal file
60
src/udp_wrap.h
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
#ifndef UDP_WRAP_H_
|
||||||
|
#define UDP_WRAP_H_
|
||||||
|
|
||||||
|
#include "node.h"
|
||||||
|
#include "req_wrap.h"
|
||||||
|
#include "handle_wrap.h"
|
||||||
|
|
||||||
|
namespace node {
|
||||||
|
|
||||||
|
using v8::Object;
|
||||||
|
using v8::Handle;
|
||||||
|
using v8::Local;
|
||||||
|
using v8::Value;
|
||||||
|
using v8::String;
|
||||||
|
using v8::Arguments;
|
||||||
|
|
||||||
|
class UDPWrap: public HandleWrap {
|
||||||
|
public:
|
||||||
|
static void Initialize(Handle<Object> target);
|
||||||
|
static Handle<Value> New(const Arguments& args);
|
||||||
|
static Handle<Value> Bind(const Arguments& args);
|
||||||
|
static Handle<Value> Send(const Arguments& args);
|
||||||
|
static Handle<Value> Bind6(const Arguments& args);
|
||||||
|
static Handle<Value> Send6(const Arguments& args);
|
||||||
|
static Handle<Value> RecvStart(const Arguments& args);
|
||||||
|
static Handle<Value> RecvStop(const Arguments& args);
|
||||||
|
static Handle<Value> GetSockName(const Arguments& args);
|
||||||
|
static Handle<Value> AddMembership(const Arguments& args);
|
||||||
|
static Handle<Value> DropMembership(const Arguments& args);
|
||||||
|
static Handle<Value> SetMulticastTTL(const Arguments& args);
|
||||||
|
static Handle<Value> SetMulticastLoopback(const Arguments& args);
|
||||||
|
static Handle<Value> SetBroadcast(const Arguments& args);
|
||||||
|
static Handle<Value> SetTTL(const Arguments& args);
|
||||||
|
static UDPWrap* Unwrap(Local<Object> obj);
|
||||||
|
|
||||||
|
uv_udp_t* UVHandle();
|
||||||
|
|
||||||
|
private:
|
||||||
|
UDPWrap(Handle<Object> object);
|
||||||
|
virtual ~UDPWrap();
|
||||||
|
|
||||||
|
static Handle<Value> DoBind(const Arguments& args, int family);
|
||||||
|
static Handle<Value> DoSend(const Arguments& args, int family);
|
||||||
|
static Handle<Value> SetMembership(const Arguments& args,
|
||||||
|
uv_membership membership);
|
||||||
|
|
||||||
|
static uv_buf_t OnAlloc(uv_handle_t* handle, size_t suggested_size);
|
||||||
|
static void OnSend(uv_udp_send_t* req, int status);
|
||||||
|
static void OnRecv(uv_udp_t* handle,
|
||||||
|
ssize_t nread,
|
||||||
|
uv_buf_t buf,
|
||||||
|
struct sockaddr* addr,
|
||||||
|
unsigned flags);
|
||||||
|
|
||||||
|
uv_udp_t handle_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace node
|
||||||
|
|
||||||
|
#endif // UDP_WRAP_H_
|
@ -58,7 +58,11 @@ pipe.onread = function(b, off, len) {
|
|||||||
p.spawn({
|
p.spawn({
|
||||||
file: process.execPath,
|
file: process.execPath,
|
||||||
args: [process.execPath, '-v'],
|
args: [process.execPath, '-v'],
|
||||||
stdoutStream: pipe
|
stdio: [
|
||||||
|
{ type: 'ignore' },
|
||||||
|
{ type: 'pipe', handle: pipe },
|
||||||
|
{ type: 'ignore' }
|
||||||
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user