macgyver 0.0.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/README.md +45 -0
- data/Rakefile +1 -0
- data/assets/MacGap.app/Contents/Frameworks/Growl.framework/Growl +0 -0
- data/assets/MacGap.app/Contents/Frameworks/Growl.framework/Versions/A/Growl +0 -0
- data/assets/MacGap.app/Contents/Frameworks/Growl.framework/Versions/A/Headers/Growl.h +5 -0
- data/assets/MacGap.app/Contents/Frameworks/Growl.framework/Versions/A/Headers/GrowlApplicationBridge.h +551 -0
- data/assets/MacGap.app/Contents/Frameworks/Growl.framework/Versions/A/Headers/GrowlDefines.h +341 -0
- data/assets/MacGap.app/Contents/Frameworks/Growl.framework/Versions/A/Resources/Info.plist +40 -0
- data/assets/MacGap.app/Contents/Frameworks/Growl.framework/Versions/A/_CodeSignature/CodeResources +34 -0
- data/assets/MacGap.app/Contents/Info.plist +48 -0
- data/assets/MacGap.app/Contents/MacOS/MacGap +0 -0
- data/assets/MacGap.app/Contents/PkgInfo +1 -0
- data/assets/MacGap.app/Contents/Resources/_debugger.js +1718 -0
- data/assets/MacGap.app/Contents/Resources/_http_agent.js +310 -0
- data/assets/MacGap.app/Contents/Resources/_http_client.js +533 -0
- data/assets/MacGap.app/Contents/Resources/_http_common.js +222 -0
- data/assets/MacGap.app/Contents/Resources/_http_incoming.js +194 -0
- data/assets/MacGap.app/Contents/Resources/_http_outgoing.js +597 -0
- data/assets/MacGap.app/Contents/Resources/_http_server.js +510 -0
- data/assets/MacGap.app/Contents/Resources/_linklist.js +76 -0
- data/assets/MacGap.app/Contents/Resources/_stream_duplex.js +69 -0
- data/assets/MacGap.app/Contents/Resources/_stream_passthrough.js +41 -0
- data/assets/MacGap.app/Contents/Resources/_stream_readable.js +900 -0
- data/assets/MacGap.app/Contents/Resources/_stream_transform.js +204 -0
- data/assets/MacGap.app/Contents/Resources/_stream_writable.js +456 -0
- data/assets/MacGap.app/Contents/Resources/_tls_legacy.js +887 -0
- data/assets/MacGap.app/Contents/Resources/_tls_wrap.js +831 -0
- data/assets/MacGap.app/Contents/Resources/application.icns +0 -0
- data/assets/MacGap.app/Contents/Resources/assert.js +326 -0
- data/assets/MacGap.app/Contents/Resources/buffer.js +724 -0
- data/assets/MacGap.app/Contents/Resources/child_process.js +1107 -0
- data/assets/MacGap.app/Contents/Resources/cluster.js +613 -0
- data/assets/MacGap.app/Contents/Resources/console.js +108 -0
- data/assets/MacGap.app/Contents/Resources/constants.js +22 -0
- data/assets/MacGap.app/Contents/Resources/crypto.js +691 -0
- data/assets/MacGap.app/Contents/Resources/dgram.js +459 -0
- data/assets/MacGap.app/Contents/Resources/dns.js +274 -0
- data/assets/MacGap.app/Contents/Resources/domain.js +292 -0
- data/assets/MacGap.app/Contents/Resources/en.lproj/Credits.rtf +29 -0
- data/assets/MacGap.app/Contents/Resources/en.lproj/InfoPlist.strings +0 -0
- data/assets/MacGap.app/Contents/Resources/en.lproj/MainMenu.nib +0 -0
- data/assets/MacGap.app/Contents/Resources/en.lproj/Window.nib +0 -0
- data/assets/MacGap.app/Contents/Resources/events.js +312 -0
- data/assets/MacGap.app/Contents/Resources/freelist.js +43 -0
- data/assets/MacGap.app/Contents/Resources/fs.js +1732 -0
- data/assets/MacGap.app/Contents/Resources/http.js +119 -0
- data/assets/MacGap.app/Contents/Resources/https.js +134 -0
- data/assets/MacGap.app/Contents/Resources/module.js +529 -0
- data/assets/MacGap.app/Contents/Resources/net.js +1378 -0
- data/assets/MacGap.app/Contents/Resources/nodelike.js +195 -0
- data/assets/MacGap.app/Contents/Resources/os.js +64 -0
- data/assets/MacGap.app/Contents/Resources/path.js +517 -0
- data/assets/MacGap.app/Contents/Resources/public/index.html +38 -0
- data/assets/MacGap.app/Contents/Resources/punycode.js +507 -0
- data/assets/MacGap.app/Contents/Resources/querystring.js +206 -0
- data/assets/MacGap.app/Contents/Resources/readline.js +1311 -0
- data/assets/MacGap.app/Contents/Resources/repl.js +945 -0
- data/assets/MacGap.app/Contents/Resources/smalloc.js +90 -0
- data/assets/MacGap.app/Contents/Resources/stream.js +127 -0
- data/assets/MacGap.app/Contents/Resources/string_decoder.js +189 -0
- data/assets/MacGap.app/Contents/Resources/sys.js +24 -0
- data/assets/MacGap.app/Contents/Resources/timers.js +568 -0
- data/assets/MacGap.app/Contents/Resources/tls.js +220 -0
- data/assets/MacGap.app/Contents/Resources/tty.js +129 -0
- data/assets/MacGap.app/Contents/Resources/url.js +693 -0
- data/assets/MacGap.app/Contents/Resources/util.js +688 -0
- data/assets/MacGap.app/Contents/Resources/vm.js +73 -0
- data/assets/MacGap.app/Contents/Resources/zlib.js +524 -0
- data/assets/index.html +38 -0
- data/bin/macgyver +104 -0
- data/macgyver.gemspec +19 -0
- data/test/public/index.html +27 -0
- metadata +121 -0
@@ -0,0 +1,613 @@
|
|
1
|
+
// Copyright Joyent, Inc. and other Node contributors.
|
2
|
+
//
|
3
|
+
// Permission is hereby granted, free of charge, to any person obtaining a
|
4
|
+
// copy of this software and associated documentation files (the
|
5
|
+
// "Software"), to deal in the Software without restriction, including
|
6
|
+
// without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
8
|
+
// persons to whom the Software is furnished to do so, subject to the
|
9
|
+
// following conditions:
|
10
|
+
//
|
11
|
+
// The above copyright notice and this permission notice shall be included
|
12
|
+
// in all copies or substantial portions of the Software.
|
13
|
+
//
|
14
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
15
|
+
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
17
|
+
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
18
|
+
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
19
|
+
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
20
|
+
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
+
|
22
|
+
var EventEmitter = require('events').EventEmitter;
|
23
|
+
var assert = require('assert');
|
24
|
+
var dgram = require('dgram');
|
25
|
+
var fork = require('child_process').fork;
|
26
|
+
var net = require('net');
|
27
|
+
var util = require('util');
|
28
|
+
var SCHED_NONE = 1;
|
29
|
+
var SCHED_RR = 2;
|
30
|
+
|
31
|
+
var cluster = new EventEmitter;
|
32
|
+
module.exports = cluster;
|
33
|
+
cluster.Worker = Worker;
|
34
|
+
cluster.isWorker = ('NODE_UNIQUE_ID' in process.env);
|
35
|
+
cluster.isMaster = (cluster.isWorker === false);
|
36
|
+
|
37
|
+
|
38
|
+
function Worker() {
|
39
|
+
if (!(this instanceof Worker)) return new Worker;
|
40
|
+
EventEmitter.call(this);
|
41
|
+
this.suicide = undefined;
|
42
|
+
this.state = 'none';
|
43
|
+
this.id = 0;
|
44
|
+
}
|
45
|
+
util.inherits(Worker, EventEmitter);
|
46
|
+
|
47
|
+
Worker.prototype.kill = function() {
|
48
|
+
this.destroy.apply(this, arguments);
|
49
|
+
};
|
50
|
+
|
51
|
+
Worker.prototype.send = function() {
|
52
|
+
this.process.send.apply(this.process, arguments);
|
53
|
+
};
|
54
|
+
|
55
|
+
// Master/worker specific methods are defined in the *Init() functions.
|
56
|
+
|
57
|
+
function SharedHandle(key, address, port, addressType, backlog, fd) {
|
58
|
+
this.key = key;
|
59
|
+
this.workers = [];
|
60
|
+
this.handle = null;
|
61
|
+
this.errno = 0;
|
62
|
+
|
63
|
+
// FIXME(bnoordhuis) Polymorphic return type for lack of a better solution.
|
64
|
+
var rval;
|
65
|
+
if (addressType === 'udp4' || addressType === 'udp6')
|
66
|
+
rval = dgram._createSocketHandle(address, port, addressType, fd);
|
67
|
+
else
|
68
|
+
rval = net._createServerHandle(address, port, addressType, fd);
|
69
|
+
|
70
|
+
if (util.isNumber(rval))
|
71
|
+
this.errno = rval;
|
72
|
+
else
|
73
|
+
this.handle = rval;
|
74
|
+
}
|
75
|
+
|
76
|
+
SharedHandle.prototype.add = function(worker, send) {
|
77
|
+
assert(this.workers.indexOf(worker) === -1);
|
78
|
+
this.workers.push(worker);
|
79
|
+
send(this.errno, null, this.handle);
|
80
|
+
};
|
81
|
+
|
82
|
+
SharedHandle.prototype.remove = function(worker) {
|
83
|
+
var index = this.workers.indexOf(worker);
|
84
|
+
assert(index !== -1);
|
85
|
+
this.workers.splice(index, 1);
|
86
|
+
if (this.workers.length !== 0) return false;
|
87
|
+
this.handle.close();
|
88
|
+
this.handle = null;
|
89
|
+
return true;
|
90
|
+
};
|
91
|
+
|
92
|
+
|
93
|
+
// Start a round-robin server. Master accepts connections and distributes
|
94
|
+
// them over the workers.
|
95
|
+
function RoundRobinHandle(key, address, port, addressType, backlog, fd) {
|
96
|
+
this.key = key;
|
97
|
+
this.all = {};
|
98
|
+
this.free = [];
|
99
|
+
this.handles = [];
|
100
|
+
this.handle = null;
|
101
|
+
this.server = net.createServer(assert.fail);
|
102
|
+
|
103
|
+
if (fd >= 0)
|
104
|
+
this.server.listen({ fd: fd });
|
105
|
+
else if (port >= 0)
|
106
|
+
this.server.listen(port, address);
|
107
|
+
else
|
108
|
+
this.server.listen(address); // UNIX socket path.
|
109
|
+
|
110
|
+
var self = this;
|
111
|
+
this.server.once('listening', function() {
|
112
|
+
self.handle = self.server._handle;
|
113
|
+
self.handle.onconnection = self.distribute.bind(self);
|
114
|
+
self.server._handle = null;
|
115
|
+
self.server = null;
|
116
|
+
});
|
117
|
+
}
|
118
|
+
|
119
|
+
RoundRobinHandle.prototype.add = function(worker, send) {
|
120
|
+
assert(worker.id in this.all === false);
|
121
|
+
this.all[worker.id] = worker;
|
122
|
+
|
123
|
+
var self = this;
|
124
|
+
function done() {
|
125
|
+
if (self.handle.getsockname) {
|
126
|
+
var out = {};
|
127
|
+
var err = self.handle.getsockname(out);
|
128
|
+
// TODO(bnoordhuis) Check err.
|
129
|
+
send(null, { sockname: out }, null);
|
130
|
+
}
|
131
|
+
else {
|
132
|
+
send(null, null, null); // UNIX socket.
|
133
|
+
}
|
134
|
+
self.handoff(worker); // In case there are connections pending.
|
135
|
+
}
|
136
|
+
|
137
|
+
if (util.isNull(this.server)) return done();
|
138
|
+
// Still busy binding.
|
139
|
+
this.server.once('listening', done);
|
140
|
+
this.server.once('error', function(err) {
|
141
|
+
// Hack: translate 'EADDRINUSE' error string back to numeric error code.
|
142
|
+
// It works but ideally we'd have some backchannel between the net and
|
143
|
+
// cluster modules for stuff like this.
|
144
|
+
var errno = process.binding('uv')['UV_' + err.errno];
|
145
|
+
send(errno, null);
|
146
|
+
});
|
147
|
+
};
|
148
|
+
|
149
|
+
RoundRobinHandle.prototype.remove = function(worker) {
|
150
|
+
if (worker.id in this.all === false) return false;
|
151
|
+
delete this.all[worker.id];
|
152
|
+
var index = this.free.indexOf(worker);
|
153
|
+
if (index !== -1) this.free.splice(index, 1);
|
154
|
+
if (Object.getOwnPropertyNames(this.all).length !== 0) return false;
|
155
|
+
for (var handle; handle = this.handles.shift(); handle.close());
|
156
|
+
this.handle.close();
|
157
|
+
this.handle = null;
|
158
|
+
return true;
|
159
|
+
};
|
160
|
+
|
161
|
+
RoundRobinHandle.prototype.distribute = function(err, handle) {
|
162
|
+
this.handles.push(handle);
|
163
|
+
var worker = this.free.shift();
|
164
|
+
if (worker) this.handoff(worker);
|
165
|
+
};
|
166
|
+
|
167
|
+
RoundRobinHandle.prototype.handoff = function(worker) {
|
168
|
+
if (worker.id in this.all === false) {
|
169
|
+
return; // Worker is closing (or has closed) the server.
|
170
|
+
}
|
171
|
+
var handle = this.handles.shift();
|
172
|
+
if (util.isUndefined(handle)) {
|
173
|
+
this.free.push(worker); // Add to ready queue again.
|
174
|
+
return;
|
175
|
+
}
|
176
|
+
var message = { act: 'newconn', key: this.key };
|
177
|
+
var self = this;
|
178
|
+
sendHelper(worker.process, message, handle, function(reply) {
|
179
|
+
if (reply.accepted)
|
180
|
+
handle.close();
|
181
|
+
else
|
182
|
+
self.distribute(0, handle); // Worker is shutting down. Send to another.
|
183
|
+
self.handoff(worker);
|
184
|
+
});
|
185
|
+
};
|
186
|
+
|
187
|
+
|
188
|
+
if (cluster.isMaster)
|
189
|
+
masterInit();
|
190
|
+
else
|
191
|
+
workerInit();
|
192
|
+
|
193
|
+
|
194
|
+
function createWorkerExecArgv(masterExecArgv, worker) {
|
195
|
+
var args = masterExecArgv.slice();
|
196
|
+
var debugPort = process.debugPort + worker.id;
|
197
|
+
var hasDebugArg = false;
|
198
|
+
|
199
|
+
for (var i = 0; i < args.length; i++) {
|
200
|
+
var match = args[i].match(/^(--debug|--debug-brk)(=\d+)?$/);
|
201
|
+
if (!match) continue;
|
202
|
+
args[i] = match[1] + '=' + debugPort;
|
203
|
+
hasDebugArg = true;
|
204
|
+
}
|
205
|
+
|
206
|
+
if (!hasDebugArg)
|
207
|
+
args = ['--debug-port=' + debugPort].concat(args);
|
208
|
+
|
209
|
+
return args;
|
210
|
+
}
|
211
|
+
|
212
|
+
|
213
|
+
function masterInit() {
|
214
|
+
cluster.workers = {};
|
215
|
+
|
216
|
+
var intercom = new EventEmitter;
|
217
|
+
var settings = {
|
218
|
+
args: process.argv.slice(2),
|
219
|
+
exec: process.argv[1],
|
220
|
+
execArgv: process.execArgv,
|
221
|
+
silent: false
|
222
|
+
};
|
223
|
+
cluster.settings = settings;
|
224
|
+
|
225
|
+
// XXX(bnoordhuis) Fold cluster.schedulingPolicy into cluster.settings?
|
226
|
+
var schedulingPolicy = {
|
227
|
+
'none': SCHED_NONE,
|
228
|
+
'rr': SCHED_RR
|
229
|
+
}[process.env.NODE_CLUSTER_SCHED_POLICY];
|
230
|
+
|
231
|
+
if (util.isUndefined(schedulingPolicy)) {
|
232
|
+
// FIXME Round-robin doesn't perform well on Windows right now due to the
|
233
|
+
// way IOCP is wired up. Bert is going to fix that, eventually.
|
234
|
+
schedulingPolicy = (process.platform === 'win32') ? SCHED_NONE : SCHED_RR;
|
235
|
+
}
|
236
|
+
|
237
|
+
cluster.schedulingPolicy = schedulingPolicy;
|
238
|
+
cluster.SCHED_NONE = SCHED_NONE; // Leave it to the operating system.
|
239
|
+
cluster.SCHED_RR = SCHED_RR; // Master distributes connections.
|
240
|
+
|
241
|
+
// Keyed on address:port:etc. When a worker dies, we walk over the handles
|
242
|
+
// and remove() the worker from each one. remove() may do a linear scan
|
243
|
+
// itself so we might end up with an O(n*m) operation. Ergo, FIXME.
|
244
|
+
var handles = {};
|
245
|
+
|
246
|
+
var initialized = false;
|
247
|
+
cluster.setupMaster = function(options) {
|
248
|
+
if (initialized === true) return;
|
249
|
+
initialized = true;
|
250
|
+
settings = util._extend(settings, options || {});
|
251
|
+
// Tell V8 to write profile data for each process to a separate file.
|
252
|
+
// Without --logfile=v8-%p.log, everything ends up in a single, unusable
|
253
|
+
// file. (Unusable because what V8 logs are memory addresses and each
|
254
|
+
// process has its own memory mappings.)
|
255
|
+
if (settings.execArgv.some(function(s) { return /^--prof/.test(s); }) &&
|
256
|
+
!settings.execArgv.some(function(s) { return /^--logfile=/.test(s); }))
|
257
|
+
{
|
258
|
+
settings.execArgv = settings.execArgv.concat(['--logfile=v8-%p.log']);
|
259
|
+
}
|
260
|
+
schedulingPolicy = cluster.schedulingPolicy; // Freeze policy.
|
261
|
+
assert(schedulingPolicy === SCHED_NONE || schedulingPolicy === SCHED_RR,
|
262
|
+
'Bad cluster.schedulingPolicy: ' + schedulingPolicy);
|
263
|
+
cluster.settings = settings;
|
264
|
+
|
265
|
+
process.on('internalMessage', function(message) {
|
266
|
+
if (message.cmd !== 'NODE_DEBUG_ENABLED') return;
|
267
|
+
var key;
|
268
|
+
for (key in cluster.workers)
|
269
|
+
process._debugProcess(cluster.workers[key].process.pid);
|
270
|
+
});
|
271
|
+
|
272
|
+
process.nextTick(function() {
|
273
|
+
cluster.emit('setup');
|
274
|
+
});
|
275
|
+
};
|
276
|
+
|
277
|
+
var ids = 0;
|
278
|
+
cluster.fork = function(env) {
|
279
|
+
cluster.setupMaster();
|
280
|
+
var worker = new Worker;
|
281
|
+
worker.id = ++ids;
|
282
|
+
var workerEnv = util._extend({}, process.env);
|
283
|
+
workerEnv = util._extend(workerEnv, env);
|
284
|
+
workerEnv.NODE_UNIQUE_ID = '' + worker.id;
|
285
|
+
worker.process = fork(settings.exec, settings.args, {
|
286
|
+
env: workerEnv,
|
287
|
+
silent: settings.silent,
|
288
|
+
execArgv: createWorkerExecArgv(settings.execArgv, worker)
|
289
|
+
});
|
290
|
+
worker.process.once('exit', function(exitCode, signalCode) {
|
291
|
+
worker.suicide = !!worker.suicide;
|
292
|
+
worker.state = 'dead';
|
293
|
+
worker.emit('exit', exitCode, signalCode);
|
294
|
+
cluster.emit('exit', worker, exitCode, signalCode);
|
295
|
+
delete cluster.workers[worker.id];
|
296
|
+
});
|
297
|
+
worker.process.once('disconnect', function() {
|
298
|
+
worker.suicide = !!worker.suicide;
|
299
|
+
worker.state = 'disconnected';
|
300
|
+
worker.emit('disconnect');
|
301
|
+
cluster.emit('disconnect', worker);
|
302
|
+
delete cluster.workers[worker.id];
|
303
|
+
});
|
304
|
+
worker.process.on('error', worker.emit.bind(worker, 'error'));
|
305
|
+
worker.process.on('message', worker.emit.bind(worker, 'message'));
|
306
|
+
worker.process.on('internalMessage', internal(worker, onmessage));
|
307
|
+
process.nextTick(function() {
|
308
|
+
cluster.emit('fork', worker);
|
309
|
+
});
|
310
|
+
cluster.workers[worker.id] = worker;
|
311
|
+
return worker;
|
312
|
+
};
|
313
|
+
|
314
|
+
cluster.disconnect = function(cb) {
|
315
|
+
var workers = Object.keys(cluster.workers);
|
316
|
+
if (workers.length === 0) {
|
317
|
+
process.nextTick(intercom.emit.bind(intercom, 'disconnect'));
|
318
|
+
} else {
|
319
|
+
for (var key in workers) {
|
320
|
+
key = workers[key];
|
321
|
+
cluster.workers[key].disconnect();
|
322
|
+
}
|
323
|
+
}
|
324
|
+
if (cb) intercom.once('disconnect', cb);
|
325
|
+
};
|
326
|
+
|
327
|
+
cluster.on('disconnect', function(worker) {
|
328
|
+
delete cluster.workers[worker.id];
|
329
|
+
for (var key in handles) {
|
330
|
+
var handle = handles[key];
|
331
|
+
if (handle.remove(worker)) delete handles[key];
|
332
|
+
}
|
333
|
+
if (Object.keys(cluster.workers).length === 0) {
|
334
|
+
assert(Object.keys(handles).length === 0, 'Resource leak detected.');
|
335
|
+
intercom.emit('disconnect');
|
336
|
+
}
|
337
|
+
});
|
338
|
+
|
339
|
+
Worker.prototype.disconnect = function() {
|
340
|
+
this.suicide = true;
|
341
|
+
send(this, { act: 'disconnect' });
|
342
|
+
};
|
343
|
+
|
344
|
+
Worker.prototype.destroy = function(signo) {
|
345
|
+
signo = signo || 'SIGTERM';
|
346
|
+
var proc = this.process;
|
347
|
+
if (proc.connected) {
|
348
|
+
this.once('disconnect', proc.kill.bind(proc, signo));
|
349
|
+
this.disconnect();
|
350
|
+
return;
|
351
|
+
}
|
352
|
+
proc.kill(signo);
|
353
|
+
};
|
354
|
+
|
355
|
+
function onmessage(message, handle) {
|
356
|
+
var worker = this;
|
357
|
+
if (message.act === 'online')
|
358
|
+
online(worker);
|
359
|
+
else if (message.act === 'queryServer')
|
360
|
+
queryServer(worker, message);
|
361
|
+
else if (message.act === 'listening')
|
362
|
+
listening(worker, message);
|
363
|
+
else if (message.act === 'suicide')
|
364
|
+
worker.suicide = true;
|
365
|
+
else if (message.act === 'close')
|
366
|
+
close(worker, message);
|
367
|
+
}
|
368
|
+
|
369
|
+
function online(worker) {
|
370
|
+
worker.state = 'online';
|
371
|
+
worker.emit('online');
|
372
|
+
cluster.emit('online', worker);
|
373
|
+
}
|
374
|
+
|
375
|
+
function queryServer(worker, message) {
|
376
|
+
var args = [message.address,
|
377
|
+
message.port,
|
378
|
+
message.addressType,
|
379
|
+
message.fd];
|
380
|
+
var key = args.join(':');
|
381
|
+
var handle = handles[key];
|
382
|
+
if (util.isUndefined(handle)) {
|
383
|
+
var constructor = RoundRobinHandle;
|
384
|
+
// UDP is exempt from round-robin connection balancing for what should
|
385
|
+
// be obvious reasons: it's connectionless. There is nothing to send to
|
386
|
+
// the workers except raw datagrams and that's pointless.
|
387
|
+
if (schedulingPolicy !== SCHED_RR ||
|
388
|
+
message.addressType === 'udp4' ||
|
389
|
+
message.addressType === 'udp6') {
|
390
|
+
constructor = SharedHandle;
|
391
|
+
}
|
392
|
+
handles[key] = handle = new constructor(key,
|
393
|
+
message.address,
|
394
|
+
message.port,
|
395
|
+
message.addressType,
|
396
|
+
message.backlog,
|
397
|
+
message.fd);
|
398
|
+
}
|
399
|
+
if (!handle.data) handle.data = message.data;
|
400
|
+
|
401
|
+
// Set custom server data
|
402
|
+
handle.add(worker, function(errno, reply, handle) {
|
403
|
+
reply = util._extend({
|
404
|
+
errno: errno,
|
405
|
+
key: key,
|
406
|
+
ack: message.seq,
|
407
|
+
data: handles[key].data
|
408
|
+
}, reply);
|
409
|
+
if (errno) delete handles[key]; // Gives other workers a chance to retry.
|
410
|
+
send(worker, reply, handle);
|
411
|
+
});
|
412
|
+
}
|
413
|
+
|
414
|
+
function listening(worker, message) {
|
415
|
+
var info = {
|
416
|
+
addressType: message.addressType,
|
417
|
+
address: message.address,
|
418
|
+
port: message.port,
|
419
|
+
fd: message.fd
|
420
|
+
};
|
421
|
+
worker.state = 'listening';
|
422
|
+
worker.emit('listening', info);
|
423
|
+
cluster.emit('listening', worker, info);
|
424
|
+
}
|
425
|
+
|
426
|
+
// Round-robin only. Server in worker is closing, remove from list.
|
427
|
+
function close(worker, message) {
|
428
|
+
var key = message.key;
|
429
|
+
var handle = handles[key];
|
430
|
+
if (handle.remove(worker)) delete handles[key];
|
431
|
+
}
|
432
|
+
|
433
|
+
function send(worker, message, handle, cb) {
|
434
|
+
sendHelper(worker.process, message, handle, cb);
|
435
|
+
}
|
436
|
+
}
|
437
|
+
|
438
|
+
|
439
|
+
function workerInit() {
|
440
|
+
var handles = {};
|
441
|
+
|
442
|
+
// Called from src/node.js
|
443
|
+
cluster._setupWorker = function() {
|
444
|
+
var worker = new Worker;
|
445
|
+
cluster.worker = worker;
|
446
|
+
worker.id = +process.env.NODE_UNIQUE_ID | 0;
|
447
|
+
worker.state = 'online';
|
448
|
+
worker.process = process;
|
449
|
+
process.once('disconnect', function() {
|
450
|
+
if (!worker.suicide) {
|
451
|
+
// Unexpected disconnect, master exited, or some such nastiness, so
|
452
|
+
// worker exits immediately.
|
453
|
+
process.exit(0);
|
454
|
+
}
|
455
|
+
});
|
456
|
+
process.on('internalMessage', internal(worker, onmessage));
|
457
|
+
send({ act: 'online' });
|
458
|
+
function onmessage(message, handle) {
|
459
|
+
if (message.act === 'newconn')
|
460
|
+
onconnection(message, handle);
|
461
|
+
else if (message.act === 'disconnect')
|
462
|
+
worker.disconnect();
|
463
|
+
}
|
464
|
+
};
|
465
|
+
|
466
|
+
// obj is a net#Server or a dgram#Socket object.
|
467
|
+
cluster._getServer = function(obj, address, port, addressType, fd, cb) {
|
468
|
+
var message = {
|
469
|
+
addressType: addressType,
|
470
|
+
address: address,
|
471
|
+
port: port,
|
472
|
+
act: 'queryServer',
|
473
|
+
fd: fd,
|
474
|
+
data: null
|
475
|
+
};
|
476
|
+
// Set custom data on handle (i.e. tls tickets key)
|
477
|
+
if (obj._getServerData) message.data = obj._getServerData();
|
478
|
+
send(message, function(reply, handle) {
|
479
|
+
if (obj._setServerData) obj._setServerData(reply.data);
|
480
|
+
|
481
|
+
if (handle)
|
482
|
+
shared(reply, handle, cb); // Shared listen socket.
|
483
|
+
else
|
484
|
+
rr(reply, cb); // Round-robin.
|
485
|
+
});
|
486
|
+
obj.once('listening', function() {
|
487
|
+
cluster.worker.state = 'listening';
|
488
|
+
var address = obj.address();
|
489
|
+
message.act = 'listening';
|
490
|
+
message.port = address && address.port || port;
|
491
|
+
send(message);
|
492
|
+
});
|
493
|
+
};
|
494
|
+
|
495
|
+
// Shared listen socket.
|
496
|
+
function shared(message, handle, cb) {
|
497
|
+
var key = message.key;
|
498
|
+
// Monkey-patch the close() method so we can keep track of when it's
|
499
|
+
// closed. Avoids resource leaks when the handle is short-lived.
|
500
|
+
var close = handle.close;
|
501
|
+
handle.close = function() {
|
502
|
+
delete handles[key];
|
503
|
+
return close.apply(this, arguments);
|
504
|
+
};
|
505
|
+
assert(util.isUndefined(handles[key]));
|
506
|
+
handles[key] = handle;
|
507
|
+
cb(message.errno, handle);
|
508
|
+
}
|
509
|
+
|
510
|
+
// Round-robin. Master distributes handles across workers.
|
511
|
+
function rr(message, cb) {
|
512
|
+
if (message.errno)
|
513
|
+
return cb(message.errno, null);
|
514
|
+
|
515
|
+
var key = message.key;
|
516
|
+
function listen(backlog) {
|
517
|
+
// TODO(bnoordhuis) Send a message to the master that tells it to
|
518
|
+
// update the backlog size. The actual backlog should probably be
|
519
|
+
// the largest requested size by any worker.
|
520
|
+
return 0;
|
521
|
+
}
|
522
|
+
|
523
|
+
function close() {
|
524
|
+
// lib/net.js treats server._handle.close() as effectively synchronous.
|
525
|
+
// That means there is a time window between the call to close() and
|
526
|
+
// the ack by the master process in which we can still receive handles.
|
527
|
+
// onconnection() below handles that by sending those handles back to
|
528
|
+
// the master.
|
529
|
+
if (util.isUndefined(key)) return;
|
530
|
+
send({ act: 'close', key: key });
|
531
|
+
delete handles[key];
|
532
|
+
key = undefined;
|
533
|
+
}
|
534
|
+
|
535
|
+
function getsockname(out) {
|
536
|
+
if (key) util._extend(out, message.sockname);
|
537
|
+
return 0;
|
538
|
+
}
|
539
|
+
|
540
|
+
// Faux handle. Mimics a TCPWrap with just enough fidelity to get away
|
541
|
+
// with it. Fools net.Server into thinking that it's backed by a real
|
542
|
+
// handle.
|
543
|
+
var handle = {
|
544
|
+
close: close,
|
545
|
+
listen: listen
|
546
|
+
};
|
547
|
+
if (message.sockname) {
|
548
|
+
handle.getsockname = getsockname; // TCP handles only.
|
549
|
+
}
|
550
|
+
assert(util.isUndefined(handles[key]));
|
551
|
+
handles[key] = handle;
|
552
|
+
cb(0, handle);
|
553
|
+
}
|
554
|
+
|
555
|
+
// Round-robin connection.
|
556
|
+
function onconnection(message, handle) {
|
557
|
+
var key = message.key;
|
558
|
+
var server = handles[key];
|
559
|
+
var accepted = !util.isUndefined(server);
|
560
|
+
send({ ack: message.seq, accepted: accepted });
|
561
|
+
if (accepted) server.onconnection(0, handle);
|
562
|
+
}
|
563
|
+
|
564
|
+
Worker.prototype.disconnect = function() {
|
565
|
+
this.suicide = true;
|
566
|
+
for (var key in handles) {
|
567
|
+
var handle = handles[key];
|
568
|
+
delete handles[key];
|
569
|
+
handle.close();
|
570
|
+
}
|
571
|
+
process.disconnect();
|
572
|
+
};
|
573
|
+
|
574
|
+
Worker.prototype.destroy = function() {
|
575
|
+
this.suicide = true;
|
576
|
+
if (!process.connected) process.exit(0);
|
577
|
+
var exit = process.exit.bind(null, 0);
|
578
|
+
send({ act: 'suicide' }, exit);
|
579
|
+
process.once('disconnect', exit);
|
580
|
+
process.disconnect();
|
581
|
+
};
|
582
|
+
|
583
|
+
function send(message, cb) {
|
584
|
+
sendHelper(process, message, null, cb);
|
585
|
+
}
|
586
|
+
}
|
587
|
+
|
588
|
+
|
589
|
+
var seq = 0;
|
590
|
+
var callbacks = {};
|
591
|
+
function sendHelper(proc, message, handle, cb) {
|
592
|
+
// Mark message as internal. See INTERNAL_PREFIX in lib/child_process.js
|
593
|
+
message = util._extend({ cmd: 'NODE_CLUSTER' }, message);
|
594
|
+
if (cb) callbacks[seq] = cb;
|
595
|
+
message.seq = seq;
|
596
|
+
seq += 1;
|
597
|
+
proc.send(message, handle);
|
598
|
+
}
|
599
|
+
|
600
|
+
|
601
|
+
// Returns an internalMessage listener that hands off normal messages
|
602
|
+
// to the callback but intercepts and redirects ACK messages.
|
603
|
+
function internal(worker, cb) {
|
604
|
+
return function(message, handle) {
|
605
|
+
if (message.cmd !== 'NODE_CLUSTER') return;
|
606
|
+
var fn = cb;
|
607
|
+
if (!util.isUndefined(message.ack)) {
|
608
|
+
fn = callbacks[message.ack];
|
609
|
+
delete callbacks[message.ack];
|
610
|
+
}
|
611
|
+
fn.apply(worker, arguments);
|
612
|
+
};
|
613
|
+
}
|