/*!
* master.js - master process for hsd
* Copyright (c) 2017-2018, Christopher Jeffrey (MIT License).
* https://github.com/handshake-org/hsd
*/
'use strict';
const assert = require('bsert');
const EventEmitter = require('events');
const {format} = require('util');
const Network = require('../protocol/network');
const jobs = require('./jobs');
const Parser = require('./parser');
const Framer = require('./framer');
const packets = require('./packets');
const Parent = require('./parent');
const {ownership} = require('../covenants/ownership');
/**
* Master
* Represents the master process.
* @alias module:workers.Master
* @extends EventEmitter
*/
class Master extends EventEmitter {
/**
* Create the master process.
* @constructor
*/
constructor() {
super();
this.parent = new Parent();
this.framer = new Framer();
this.parser = new Parser();
this.listening = false;
this.color = false;
this.init();
}
/**
* Initialize master. Bind events.
* @private
*/
init() {
this.parent.on('data', (data) => {
this.parser.feed(data);
});
this.parent.on('error', (err) => {
this.emit('error', err);
});
this.parent.on('exception', (err) => {
this.send(new packets.ErrorPacket(err));
setTimeout(() => this.destroy(), 1000);
});
this.parser.on('error', (err) => {
this.emit('error', err);
});
this.parser.on('packet', (packet) => {
this.emit('packet', packet);
});
}
/**
* Set environment.
* @param {Object} env
*/
setEnv(env) {
this.color = env.HSD_WORKER_ISTTY === '1';
this.set(env.HSD_WORKER_NETWORK);
ownership.ignore = env.HSD_WORKER_IGNORE === '1';
}
/**
* Set primary network.
* @param {NetworkType|Network} network
*/
set(network) {
return Network.set(network);
}
/**
* Send data to worker.
* @param {Buffer} data
* @returns {Boolean}
*/
write(data) {
return this.parent.write(data);
}
/**
* Frame and send a packet.
* @param {Packet} packet
* @returns {Boolean}
*/
send(packet) {
return this.write(this.framer.packet(packet));
}
/**
* Emit an event on the worker side.
* @param {String} event
* @param {...Object} arg
* @returns {Boolean}
*/
sendEvent(...items) {
return this.send(new packets.EventPacket(items));
}
/**
* Destroy the worker.
*/
destroy() {
return this.parent.destroy();
}
/**
* Write a message to stdout in the master process.
* @param {Object|String} obj
* @param {...String} args
*/
log() {
const text = format.apply(null, arguments);
this.send(new packets.LogPacket(text));
}
/**
* Listen for messages from master process (only if worker).
*/
listen() {
assert(!this.listening, 'Already listening.');
this.listening = true;
this.on('error', (err) => {
this.send(new packets.ErrorPacket(err));
});
this.on('packet', (packet) => {
try {
this.handlePacket(packet);
} catch (e) {
this.emit('error', e);
}
});
}
/**
* Handle packet.
* @private
* @param {Packet}
*/
handlePacket(packet) {
let result;
switch (packet.cmd) {
case packets.types.ENV:
this.setEnv(packet.env);
break;
case packets.types.EVENT:
this.emit('event', packet.items);
this.emit(...packet.items);
break;
case packets.types.ERROR:
this.emit('error', packet.error);
break;
default:
result = jobs.execute(packet);
result.id = packet.id;
this.send(result);
break;
}
}
}
/*
* Expose
*/
module.exports = Master;