summaryrefslogtreecommitdiff
path: root/test/simple/test-sendfd.js
diff options
context:
space:
mode:
Diffstat (limited to 'test/simple/test-sendfd.js')
-rw-r--r--test/simple/test-sendfd.js126
1 files changed, 126 insertions, 0 deletions
diff --git a/test/simple/test-sendfd.js b/test/simple/test-sendfd.js
new file mode 100644
index 0000000000..9c5d7313bd
--- /dev/null
+++ b/test/simple/test-sendfd.js
@@ -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