mirror of https://github.com/lukechilds/node.git
Browse Source
a) create a layer of indirection in net.Stream to allow swapping in different read/write implementations and b) emit an 'fd' event when file descriptors are received over a UNIX pipe, as finally as a tangential benefit c) remove a bunch of conditionals from the primary codepaths for ease-of-reading.v0.7.4-release
Peter Griess
15 years ago
committed by
Ryan Dahl
4 changed files with 527 additions and 139 deletions
@ -0,0 +1,57 @@ |
|||
// See test/simple/test-sendfd.js for a complete description of what this
|
|||
// script is doing and how it fits into the test as a whole.
|
|||
|
|||
var net = require('net'); |
|||
var sys = require('sys'); |
|||
|
|||
var receivedData = []; |
|||
var receivedFDs = []; |
|||
var numSentMessages = 0; |
|||
|
|||
function processData(s) { |
|||
if (receivedData.length == 0 || receivedFDs.length == 0) { |
|||
return; |
|||
} |
|||
|
|||
var fd = receivedFDs.shift(); |
|||
var d = receivedData.shift(); |
|||
|
|||
// Augment our received object before sending it back across the pipe.
|
|||
d.pid = process.pid; |
|||
|
|||
// Create a stream around the FD that we received and send a serialized
|
|||
// version of our modified object back. Clean up when we're done.
|
|||
var pipeStream = new net.Stream(fd); |
|||
|
|||
var drainFunc = function() { |
|||
pipeStream.destroy(); |
|||
|
|||
if (++numSentMessages == 2) { |
|||
s.destroy(); |
|||
} |
|||
}; |
|||
|
|||
pipeStream.addListener('drain', drainFunc); |
|||
pipeStream.resume(); |
|||
|
|||
if (pipeStream.write(JSON.stringify(d) + '\n')) { |
|||
drainFunc(); |
|||
} |
|||
}; |
|||
|
|||
// Create a UNIX socket to the path defined by argv[2] and read a file
|
|||
// descriptor and misc data from it.
|
|||
var s = new net.Stream(); |
|||
s.addListener('fd', function(fd) { |
|||
receivedFDs.unshift(fd); |
|||
processData(s); |
|||
}); |
|||
s.addListener('data', function(data) { |
|||
data.toString('utf8').trim().split('\n').forEach(function(d) { |
|||
receivedData.unshift(JSON.parse(d)); |
|||
}); |
|||
processData(s); |
|||
}); |
|||
s.connect(process.argv[2]); |
|||
|
|||
// vim:ts=2 sw=2 et
|
@ -0,0 +1,126 @@ |
|||
// Test sending and receiving a file descriptor.
|
|||
//
|
|||
// This test is pretty complex. It ends up spawning test/fixtures/recvfd.js
|
|||
// as a child to test desired behavior. What happens is
|
|||
//
|
|||
// 1. Create an in-memory pipe via pipe(2). These two file descriptors
|
|||
// are not visible to any other process, and so make a good test-case
|
|||
// for sharing.
|
|||
// 2. Create a a UNIX socket at SOCK_PATH. When a client connects to this
|
|||
// path, they are sent the write end of the pipe from above.
|
|||
// 3. The client is sent n JSON representations of the DATA variable, each
|
|||
// with a different ordinal. We send these delimited by '\n' strings
|
|||
// so that the receiving end can avoid any coalescing that hapepns
|
|||
// due to the stream nature of the socket (e.g. '{}{}' is not a valid
|
|||
// JSON string).
|
|||
// 4. The child process receives file descriptors and JSON blobs and,
|
|||
// whenever it has at least one of each, writes a modified JSON blob
|
|||
// to the FD. The blob is modified to include the child's process ID.
|
|||
// 5. Once the child process has sent n responses, it closes the write end
|
|||
// of the pipe, which signals to the parent that there is no more data
|
|||
// coming.
|
|||
// 6. The parent listens to the read end of the pipe, accumulating JSON
|
|||
// blobs (again, delimited by '\n') and verifying that a) the 'pid'
|
|||
// attribute belongs to the child and b) the 'ord' field has not been
|
|||
// seen in a response yet. This is intended to ensure that all blobs
|
|||
// sent out have been relayed back to us.
|
|||
|
|||
require('../common'); |
|||
|
|||
var buffer = require('buffer'); |
|||
var child_process = require('child_process'); |
|||
var fs = require('fs'); |
|||
var net = require('net'); |
|||
var netBinding = process.binding('net'); |
|||
var path = require('path'); |
|||
var sys = require('sys'); |
|||
|
|||
var DATA = { |
|||
'ppid' : process.pid, |
|||
'ord' : 0 |
|||
}; |
|||
|
|||
var SOCK_PATH = path.join( |
|||
__dirname, |
|||
'..', |
|||
path.basename(__filename, '.js') + '.sock' |
|||
); |
|||
|
|||
var logChild = function(d) { |
|||
if (typeof d == 'object') { |
|||
d = d.toString(); |
|||
} |
|||
|
|||
d.split('\n').forEach(function(l) { |
|||
if (l.length > 0) { |
|||
sys.debug('CHILD: ' + l); |
|||
} |
|||
}); |
|||
}; |
|||
|
|||
// Create a pipe
|
|||
//
|
|||
// We establish a listener on the read end of the pipe so that we can
|
|||
// validate any data sent back by the child. We send the write end of the
|
|||
// pipe to the child and close it off in our process.
|
|||
var pipeFDs = netBinding.pipe(); |
|||
assert.equal(pipeFDs.length, 2); |
|||
|
|||
var seenOrdinals = []; |
|||
|
|||
var pipeReadStream = new net.Stream(); |
|||
pipeReadStream.addListener('data', function(data) { |
|||
data.toString('utf8').trim().split('\n').forEach(function(d) { |
|||
var rd = JSON.parse(d); |
|||
|
|||
assert.equal(rd.pid, cpp); |
|||
assert.equal(seenOrdinals.indexOf(rd.ord), -1); |
|||
|
|||
seenOrdinals.unshift(rd.ord); |
|||
}); |
|||
}); |
|||
pipeReadStream.open(pipeFDs[0]); |
|||
pipeReadStream.resume(); |
|||
|
|||
// Create a UNIX socket at SOCK_PATH and send DATA and the write end
|
|||
// of the pipe to whoever connects.
|
|||
//
|
|||
// We send two messages here, both with the same pipe FD: one string, and
|
|||
// one buffer. We want to make sure that both datatypes are handled
|
|||
// correctly.
|
|||
var srv = net.createServer(function(s) { |
|||
var str = JSON.stringify(DATA) + '\n'; |
|||
|
|||
DATA.ord = DATA.ord + 1; |
|||
var buf = new buffer.Buffer(str.length); |
|||
buf.write(JSON.stringify(DATA) + '\n', 'utf8'); |
|||
|
|||
s.write(str, 'utf8', pipeFDs[1]); |
|||
if (s.write(buf, undefined, pipeFDs[1])) { |
|||
netBinding.close(pipeFDs[1]); |
|||
} else { |
|||
s.addListener('drain', function() { |
|||
netBinding.close(pipeFDs[1]); |
|||
}); |
|||
} |
|||
}); |
|||
srv.listen(SOCK_PATH); |
|||
|
|||
// Spawn a child running test/fixtures/recvfd.js
|
|||
var cp = child_process.spawn(process.argv[0], |
|||
[path.join(fixturesDir, 'recvfd.js'), SOCK_PATH]); |
|||
|
|||
cp.stdout.addListener('data', logChild); |
|||
cp.stderr.addListener('data', logChild); |
|||
|
|||
// When the child exits, clean up and validate its exit status
|
|||
var cpp = cp.pid; |
|||
cp.addListener('exit', function(code, signal) { |
|||
srv.close(); |
|||
// fs.unlinkSync(SOCK_PATH);
|
|||
|
|||
assert.equal(code, 0); |
|||
assert.equal(seenOrdinals.length, 2); |
|||
}); |
|||
|
|||
// vim:ts=2 sw=2 et
|
Loading…
Reference in new issue