1
// Test sending and receiving a file descriptor.
3
// This test is pretty complex. It ends up spawning test/fixtures/recvfd.js
4
// as a child to test desired behavior. What happens is
6
// 1. Create an in-memory pipe via pipe(2). These two file descriptors
7
// are not visible to any other process, and so make a good test-case
9
// 2. Create a a UNIX socket at SOCK_PATH. When a client connects to this
10
// path, they are sent the write end of the pipe from above.
11
// 3. The client is sent n JSON representations of the DATA variable, each
12
// with a different ordinal. We send these delimited by '\n' strings
13
// so that the receiving end can avoid any coalescing that hapepns
14
// due to the stream nature of the socket (e.g. '{}{}' is not a valid
16
// 4. The child process receives file descriptors and JSON blobs and,
17
// whenever it has at least one of each, writes a modified JSON blob
18
// to the FD. The blob is modified to include the child's process ID.
19
// 5. Once the child process has sent n responses, it closes the write end
20
// of the pipe, which signals to the parent that there is no more data
22
// 6. The parent listens to the read end of the pipe, accumulating JSON
23
// blobs (again, delimited by '\n') and verifying that a) the 'pid'
24
// attribute belongs to the child and b) the 'ord' field has not been
25
// seen in a response yet. This is intended to ensure that all blobs
26
// sent out have been relayed back to us.
28
common = require("../common");
29
assert = common.assert
31
var buffer = require('buffer');
32
var child_process = require('child_process');
33
var fs = require('fs');
34
var net = require('net');
35
var netBinding = process.binding('net');
36
var path = require('path');
37
var sys = require('sys');
44
var SOCK_PATH = path.join(
47
path.basename(__filename, '.js') + '.sock'
50
var logChild = function(d) {
51
if (typeof d == 'object') {
55
d.split('\n').forEach(function(l) {
57
common.debug('CHILD: ' + l);
64
// We establish a listener on the read end of the pipe so that we can
65
// validate any data sent back by the child. We send the write end of the
66
// pipe to the child and close it off in our process.
67
var pipeFDs = netBinding.pipe();
68
assert.equal(pipeFDs.length, 2);
70
var seenOrdinals = [];
72
var pipeReadStream = new net.Stream();
73
pipeReadStream.addListener('data', function(data) {
74
data.toString('utf8').trim().split('\n').forEach(function(d) {
75
var rd = JSON.parse(d);
77
assert.equal(rd.pid, cpp);
78
assert.equal(seenOrdinals.indexOf(rd.ord), -1);
80
seenOrdinals.unshift(rd.ord);
83
pipeReadStream.open(pipeFDs[0]);
84
pipeReadStream.resume();
86
// Create a UNIX socket at SOCK_PATH and send DATA and the write end
87
// of the pipe to whoever connects.
89
// We send two messages here, both with the same pipe FD: one string, and
90
// one buffer. We want to make sure that both datatypes are handled
92
var srv = net.createServer(function(s) {
93
var str = JSON.stringify(DATA) + '\n';
95
DATA.ord = DATA.ord + 1;
96
var buf = new buffer.Buffer(str.length);
97
buf.write(JSON.stringify(DATA) + '\n', 'utf8');
99
s.write(str, 'utf8', pipeFDs[1]);
100
if (s.write(buf, undefined, pipeFDs[1])) {
101
netBinding.close(pipeFDs[1]);
103
s.addListener('drain', function() {
104
netBinding.close(pipeFDs[1]);
108
srv.listen(SOCK_PATH);
110
// Spawn a child running test/fixtures/recvfd.js
111
var cp = child_process.spawn(process.argv[0],
112
[path.join(common.fixturesDir, 'recvfd.js'), SOCK_PATH]);
114
cp.stdout.addListener('data', logChild);
115
cp.stderr.addListener('data', logChild);
117
// When the child exits, clean up and validate its exit status
119
cp.addListener('exit', function(code, signal) {
121
// fs.unlinkSync(SOCK_PATH);
123
assert.equal(code, 0);
124
assert.equal(seenOrdinals.length, 2);