UNPKG

4.47 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3const Debug = require("debug");
4const dgram = require("dgram");
5const events = require("events");
6const netmask_1 = require("netmask");
7const os = require("os");
8const debug = Debug('ionic:discover:publisher');
9const PREFIX = 'ION_DP';
10const PORT = 41234;
11class Publisher extends events.EventEmitter {
12 constructor(namespace, name, port, commPort) {
13 super();
14 this.namespace = namespace;
15 this.name = name;
16 this.port = port;
17 this.commPort = commPort;
18 this.path = '/';
19 this.running = false;
20 this.interval = 2000;
21 if (name.indexOf(':') >= 0) {
22 throw new Error('name should not contain ":"');
23 }
24 this.id = Math.random().toString(10).substring(2, 8);
25 }
26 start() {
27 return new Promise((resolve, reject) => {
28 if (this.running) {
29 return resolve();
30 }
31 this.running = true;
32 if (!this.interfaces) {
33 this.interfaces = this.getInterfaces();
34 }
35 const client = this.client = dgram.createSocket('udp4');
36 client.on('error', err => {
37 this.emit('error', err);
38 });
39 client.on('listening', () => {
40 client.setBroadcast(true);
41 this.timer = setInterval(() => this.sayHello(), this.interval);
42 this.sayHello();
43 debug('Publisher starting');
44 resolve();
45 });
46 client.bind();
47 });
48 }
49 stop() {
50 if (!this.running) {
51 return;
52 }
53 this.running = false;
54 if (this.timer) {
55 clearInterval(this.timer);
56 this.timer = undefined;
57 }
58 if (this.client) {
59 this.client.close();
60 this.client = undefined;
61 }
62 }
63 buildMessage(ip) {
64 return {
65 t: Date.now(),
66 id: this.id,
67 nspace: this.namespace,
68 name: this.name,
69 host: os.hostname(),
70 ip,
71 port: this.port,
72 commPort: this.commPort,
73 path: this.path,
74 };
75 }
76 getInterfaces() {
77 return prepareInterfaces(os.networkInterfaces());
78 }
79 sayHello() {
80 if (!this.interfaces) {
81 throw new Error('No network interfaces set--was the service started?');
82 }
83 if (!this.client) {
84 throw new Error('Client not initialized--was the service started?');
85 }
86 try {
87 for (const iface of this.interfaces) {
88 const message = this.buildMessage(iface.address);
89 const serialized = PREFIX + JSON.stringify(message);
90 const buf = Buffer.from(serialized);
91 debug(`Broadcasting %O to ${iface.broadcast}`, serialized);
92 this.client.send(buf, 0, buf.length, PORT, iface.broadcast, err => {
93 if (err) {
94 this.emit('error', err);
95 }
96 });
97 }
98 }
99 catch (e) {
100 this.emit('error', e);
101 }
102 }
103}
104exports.Publisher = Publisher;
105function prepareInterfaces(interfaces) {
106 const set = new Set();
107 return Object.keys(interfaces)
108 .map(key => interfaces[key])
109 .reduce((prev, current) => prev.concat(current))
110 .filter(iface => iface.family === 'IPv4')
111 .map(iface => {
112 return {
113 address: iface.address,
114 broadcast: computeBroadcastAddress(iface.address, iface.netmask),
115 };
116 })
117 .filter(iface => {
118 if (!set.has(iface.broadcast)) {
119 set.add(iface.broadcast);
120 return true;
121 }
122 return false;
123 });
124}
125exports.prepareInterfaces = prepareInterfaces;
126function newSilentPublisher(namespace, name, port) {
127 name = `${name}@${port}`;
128 const service = new Publisher(namespace, name, port);
129 service.on('error', () => {
130 // do not log
131 });
132 service.start().catch(() => {
133 // do not log
134 });
135 return service;
136}
137exports.newSilentPublisher = newSilentPublisher;
138function computeBroadcastAddress(address, netmask) {
139 const ip = address + '/' + netmask;
140 const block = new netmask_1.Netmask(ip);
141 return block.broadcast;
142}
143exports.computeBroadcastAddress = computeBroadcastAddress;