1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | const Debug = require("debug");
|
4 | const dgram = require("dgram");
|
5 | const events = require("events");
|
6 | const netmask_1 = require("netmask");
|
7 | const os = require("os");
|
8 | const debug = Debug('ionic:discover:publisher');
|
9 | const PREFIX = 'ION_DP';
|
10 | const PORT = 41234;
|
11 | class 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 | }
|
104 | exports.Publisher = Publisher;
|
105 | function 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 | }
|
125 | exports.prepareInterfaces = prepareInterfaces;
|
126 | function newSilentPublisher(namespace, name, port) {
|
127 | name = `${name}@${port}`;
|
128 | const service = new Publisher(namespace, name, port);
|
129 | service.on('error', () => {
|
130 |
|
131 | });
|
132 | service.start().catch(() => {
|
133 |
|
134 | });
|
135 | return service;
|
136 | }
|
137 | exports.newSilentPublisher = newSilentPublisher;
|
138 | function computeBroadcastAddress(address, netmask) {
|
139 | const ip = address + '/' + netmask;
|
140 | const block = new netmask_1.Netmask(ip);
|
141 | return block.broadcast;
|
142 | }
|
143 | exports.computeBroadcastAddress = computeBroadcastAddress;
|