1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
|
'use strict';
// 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 happens
// 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.
const common = require('../common');
const assert = require('assert');
const buffer = require('buffer');
const child_process = require('child_process');
const fs = require('fs');
const net = require('net');
var netBinding = process.binding('net');
const path = require('path');
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) {
console.error('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.strictEqual(pipeFDs.length, 2);
var seenOrdinals = [];
var pipeReadStream = new net.Stream();
pipeReadStream.on('data', function(data) {
data.toString('utf8').trim().split('\n').forEach(function(d) {
var rd = JSON.parse(d);
assert.strictEqual(rd.pid, cpp);
assert.strictEqual(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 = buffer.Buffer.allocUnsafe(str.length);
buf.write(JSON.stringify(DATA) + '\n', 'utf8');
s.write(str, 'utf8', pipeFDs[1]);
if (s.write(buf, pipeFDs[1])) {
netBinding.close(pipeFDs[1]);
} else {
s.on('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(common.fixturesDir, 'recvfd.js'),
SOCK_PATH]);
cp.stdout.on('data', logChild);
cp.stderr.on('data', logChild);
// When the child exits, clean up and validate its exit status
var cpp = cp.pid;
cp.on('exit', function(code, signal) {
srv.close();
// fs.unlinkSync(SOCK_PATH);
assert.strictEqual(code, 0);
assert.strictEqual(seenOrdinals.length, 2);
});
// vim:ts=2 sw=2 et
|