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