hsync 0.17.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli.js +1 -0
- package/connection.js +35 -68
- package/dist/hsync.js +366 -10
- package/dist/hsync.min.js +1 -1
- package/dist/hsync.min.js.LICENSE.txt +2 -0
- package/hsync-web.js +4 -0
- package/hsync.js +3 -0
- package/lib/peers.js +237 -0
- package/lib/rtc-node.js +168 -0
- package/lib/rtc-web.js +172 -0
- package/lib/socket-listen-handler.js +28 -39
- package/lib/socket-map.js +19 -0
- package/lib/socket-relay-handler.js +12 -10
- package/package.json +9 -5
- package/tn.js +34 -0
- package/lib/rpc.js +0 -95
- package/lib/web-net.js +0 -67
package/hsync-web.js
CHANGED
|
@@ -2,11 +2,15 @@ const mqtt = require('precompiled-mqtt');
|
|
|
2
2
|
const buffer = require('buffer');
|
|
3
3
|
const net = require('net-web');
|
|
4
4
|
const { createHsync, setNet, setMqtt } = require('./connection');
|
|
5
|
+
const { setRTC } = require('./lib/peers');
|
|
6
|
+
const rtc = require('./lib/rtc-web');
|
|
5
7
|
const config = require('./config');
|
|
6
8
|
|
|
9
|
+
|
|
7
10
|
// TODO need to make this work with web/service workers
|
|
8
11
|
window.Buffer = buffer.Buffer;
|
|
9
12
|
|
|
13
|
+
setRTC(rtc);
|
|
10
14
|
setNet(net);
|
|
11
15
|
setMqtt(mqtt);
|
|
12
16
|
|
package/hsync.js
CHANGED
|
@@ -2,7 +2,10 @@ const net = require('net');
|
|
|
2
2
|
const mqtt = require('mqtt');
|
|
3
3
|
const { createHsync, setNet, setMqtt } = require('./connection');
|
|
4
4
|
const config = require('./config');
|
|
5
|
+
const { setRTC } = require('./lib/peers');
|
|
6
|
+
const rtc = require('./lib/rtc-node');
|
|
5
7
|
|
|
8
|
+
setRTC(rtc);
|
|
6
9
|
setNet(net);
|
|
7
10
|
setMqtt(mqtt);
|
|
8
11
|
|
package/lib/peers.js
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
const rawr = require('rawr');
|
|
2
|
+
const b64id = require('b64id');
|
|
3
|
+
const debug = require('debug')('hsync:peers');
|
|
4
|
+
const EventEmitter = require('events').EventEmitter;
|
|
5
|
+
const buffer = require('buffer');
|
|
6
|
+
const mqttPacket = require('mqtt-packet-web');
|
|
7
|
+
|
|
8
|
+
globalThis.Buffer = buffer.Buffer;
|
|
9
|
+
|
|
10
|
+
const { handleSocketPacket } = require('./socket-map');
|
|
11
|
+
const fetch = require('./fetch');
|
|
12
|
+
|
|
13
|
+
const peers = {};
|
|
14
|
+
|
|
15
|
+
let rtc;
|
|
16
|
+
|
|
17
|
+
function setRTC(rtcImpl) {
|
|
18
|
+
rtc = rtcImpl;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function createPacket(topic, payload) {
|
|
22
|
+
let payloadStr = payload;
|
|
23
|
+
// console.log('mqttPacket', { topic, payload });
|
|
24
|
+
// if (payload instanceof Uint8Array) {
|
|
25
|
+
// console.log('str payload', new TextDecoder().decode(payload));
|
|
26
|
+
// }
|
|
27
|
+
const packet = mqttPacket.generate({
|
|
28
|
+
qos: 0,
|
|
29
|
+
cmd: 'publish',
|
|
30
|
+
topic,
|
|
31
|
+
payload: payloadStr,
|
|
32
|
+
});
|
|
33
|
+
// console.log('packet', packet);
|
|
34
|
+
return packet;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function parsePacket(packet) {
|
|
38
|
+
const parser = mqttPacket.parser();
|
|
39
|
+
return new Promise((resolve, reject) => {
|
|
40
|
+
parser.on('packet', resolve);
|
|
41
|
+
parser.on('error', reject);
|
|
42
|
+
parser.parse(packet);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function getRPCPeer(options = {}) {
|
|
47
|
+
const { hostName, temporary, timeout = 10000, hsyncClient } = options;
|
|
48
|
+
let peer = peers[hostName];
|
|
49
|
+
if (!peer) {
|
|
50
|
+
debug('CREATING peer', hostName);
|
|
51
|
+
peer = createRPCPeer({hostName, hsyncClient, timeout});
|
|
52
|
+
if (temporary) {
|
|
53
|
+
peer.rpcTemporary = true;
|
|
54
|
+
}
|
|
55
|
+
peers[hostName] = peer;
|
|
56
|
+
}
|
|
57
|
+
return peer;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function createRPCPeer(options = {}) {
|
|
61
|
+
const { hostName, hsyncClient, timeout = 10000, useRTC = true } = options;
|
|
62
|
+
if (!hostName) {
|
|
63
|
+
throw new Error('No hostname specified');
|
|
64
|
+
}
|
|
65
|
+
if (hostName === hsyncClient.myHostName) {
|
|
66
|
+
throw new Error('Peer must be a different host');
|
|
67
|
+
}
|
|
68
|
+
const myAuth = b64id.generateId();
|
|
69
|
+
const transport = new EventEmitter();
|
|
70
|
+
const peer = rawr({transport, methods: Object.assign({}, hsyncClient.peerMethods), timeout, idGenerator: b64id.generateId});
|
|
71
|
+
peer.hostName = hostName;
|
|
72
|
+
peer.rtcEvents = new EventEmitter();
|
|
73
|
+
peer.localMethods = Object.assign({}, hsyncClient.peerMethods);
|
|
74
|
+
peer.sockets = {};
|
|
75
|
+
|
|
76
|
+
peer.localMethods.rtcSignal = (peerInfo, signal) => {
|
|
77
|
+
debug('rtcSignal', signal.type);
|
|
78
|
+
if (signal.type === 'offer' && !peer.rtcCon && !signal.alreadySent) {
|
|
79
|
+
rtc.answerPeer(peer, signal);
|
|
80
|
+
} else if (signal.type === 'answer') {
|
|
81
|
+
peer.handleRtcAnswer(signal);
|
|
82
|
+
}
|
|
83
|
+
return 'rtcSignal ok';
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
peer.rtcEvents.on('packet', async (packet) => {
|
|
87
|
+
try {
|
|
88
|
+
const msg = await parsePacket(packet);
|
|
89
|
+
const [p1, p2, p3] = msg.topic.split('/');
|
|
90
|
+
if (p1 === 'rpc') {
|
|
91
|
+
transport.receiveData(JSON.parse(msg.payload.toString()));
|
|
92
|
+
} else if (p1 === 'socketData'){
|
|
93
|
+
handleSocketPacket(msg);
|
|
94
|
+
} else {
|
|
95
|
+
debug('other topic', msg.topic);
|
|
96
|
+
}
|
|
97
|
+
} catch (e) {
|
|
98
|
+
debug('bad packet', e);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
peer.rtcEvents.on('dcOpen', () => {
|
|
103
|
+
debug('dcOpen');
|
|
104
|
+
peer.packAndSend = (topic, payload) => {
|
|
105
|
+
const packet = createPacket(topic, payload);
|
|
106
|
+
peer.rtcSend(packet);
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
peer.rtcEvents.on('closed', () => {
|
|
111
|
+
peer.dcOpen = false;
|
|
112
|
+
delete peer.packAndSend;
|
|
113
|
+
debug('rtc closed');
|
|
114
|
+
for (s in peer.sockets) {
|
|
115
|
+
try {
|
|
116
|
+
debug('closing socket', s);
|
|
117
|
+
peer.sockets[s].destroy();
|
|
118
|
+
delete peer.sockets[s];
|
|
119
|
+
} catch (e) {
|
|
120
|
+
debug('error closing socket', e);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
peer.rtcEvents.on('disconnected', () => {
|
|
126
|
+
peer.dcOpen = false;
|
|
127
|
+
delete peer.packAndSend;
|
|
128
|
+
debug('rtc disconnected');
|
|
129
|
+
for (s in peer.sockets) {
|
|
130
|
+
try {
|
|
131
|
+
debug('closing socket', s);
|
|
132
|
+
peer.sockets[s].destroy();
|
|
133
|
+
delete peer.sockets[s];
|
|
134
|
+
} catch (e) {
|
|
135
|
+
debug('error closing socket', e);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
peer.connectRTC = async () => {
|
|
141
|
+
debug('connectRTC');
|
|
142
|
+
|
|
143
|
+
return new Promise(async (resolve, reject) => {
|
|
144
|
+
try {
|
|
145
|
+
const offer = await rtc.offerPeer(peer);
|
|
146
|
+
peer.rtcEvents.once('dcOpen', () => {
|
|
147
|
+
debug('dcOpen!');
|
|
148
|
+
resolve(offer);
|
|
149
|
+
});
|
|
150
|
+
} catch (e) {
|
|
151
|
+
reject(e);
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
transport.send = async (msg) => {
|
|
157
|
+
const fullMsg = {
|
|
158
|
+
msg,
|
|
159
|
+
myAuth,
|
|
160
|
+
toHost: hostName,
|
|
161
|
+
fromHost: hsyncClient.webUrl,
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
debug('↑ peer rpc', peer.dcOpen ? 'RTC' : 'REST', `${hostName}/_hs/rpc`, msg.method);
|
|
165
|
+
|
|
166
|
+
if (peer.dcOpen) {
|
|
167
|
+
let payload = msg;
|
|
168
|
+
if (typeof msg === 'object') {
|
|
169
|
+
payload = JSON.stringify(payload);
|
|
170
|
+
}
|
|
171
|
+
const packet = createPacket('rpc', payload);
|
|
172
|
+
peer.rtcSend(packet);
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
try {
|
|
177
|
+
debug('fetching', fullMsg, useRTC);
|
|
178
|
+
const result = await fetch.post(`${hostName}/_hs/rpc`, fullMsg);
|
|
179
|
+
debug('fetch result', result);
|
|
180
|
+
if (msg.id) {
|
|
181
|
+
transport.receiveData({id: msg.id, result, jsonrpc: msg.jsonrpc});
|
|
182
|
+
}
|
|
183
|
+
// if (!peer.rtcCon && useRTC) {
|
|
184
|
+
// debug('STARTING rtc creation');
|
|
185
|
+
// rtc.offerPeer(peer);
|
|
186
|
+
// }
|
|
187
|
+
} catch(e) {
|
|
188
|
+
debug('error sending peer RPC request', e);
|
|
189
|
+
transport.receiveData({id: msg.id, error: e, method: msg.method, jsonrpc: msg.jsonrpc});
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
transport.receiveData = (msg) => {
|
|
195
|
+
if(typeof msg === 'string') {
|
|
196
|
+
msg = JSON.parse(msg);
|
|
197
|
+
}
|
|
198
|
+
debug('↓ peer rpc receivedData', msg, msg.params);
|
|
199
|
+
if (msg.params && Array.isArray(msg.params)) {
|
|
200
|
+
debug('unshifting', msg.params);
|
|
201
|
+
msg.params.unshift(peer);
|
|
202
|
+
}
|
|
203
|
+
transport.emit('rpc', msg);
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
peer.myAuth = myAuth;
|
|
207
|
+
peer.hostName = hostName;
|
|
208
|
+
return peer;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function createServerPeer(hsyncClient, methods) {
|
|
212
|
+
const transport = new EventEmitter();
|
|
213
|
+
transport.send = (msg) => {
|
|
214
|
+
if(typeof msg === 'object') {
|
|
215
|
+
msg = JSON.stringify(msg);
|
|
216
|
+
}
|
|
217
|
+
const topic = `srpc/${hsyncClient.myHostName}`;
|
|
218
|
+
debug('↑ server rpc request', msg);
|
|
219
|
+
hsyncClient.mqConn.publish(topic, Buffer.from(msg));
|
|
220
|
+
};
|
|
221
|
+
transport.receiveData = (msg) => {
|
|
222
|
+
if(msg) {
|
|
223
|
+
msg = JSON.parse(msg);
|
|
224
|
+
}
|
|
225
|
+
debug('↓ server rpc reply', msg.method, msg.id);
|
|
226
|
+
transport.emit('rpc', msg);
|
|
227
|
+
};
|
|
228
|
+
const peer = rawr({transport, methods, timeout: 5000});
|
|
229
|
+
return peer;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
module.exports = {
|
|
233
|
+
createRPCPeer,
|
|
234
|
+
createServerPeer,
|
|
235
|
+
getRPCPeer,
|
|
236
|
+
setRTC,
|
|
237
|
+
};
|
package/lib/rtc-node.js
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
const debug = require('debug')('hsync:rtc-node');
|
|
2
|
+
const debugError = require('debug')('errors');
|
|
3
|
+
let nodeDataChannel;
|
|
4
|
+
try {
|
|
5
|
+
nodeDataChannel = require('node-datachannel');
|
|
6
|
+
} catch (e) {
|
|
7
|
+
debugError(e);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const rtc = {
|
|
11
|
+
PeerConnection: nodeDataChannel?.PeerConnection,
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
const defaultOptions = { iceServers: ['stun:stun.l.google.com:19302'] };
|
|
16
|
+
|
|
17
|
+
const GATHERING_TIMEOUT = 4000;
|
|
18
|
+
|
|
19
|
+
async function offerPeer(peer) {
|
|
20
|
+
|
|
21
|
+
if (!rtc.PeerConnection) {
|
|
22
|
+
throw new Error('node-datachannel not installed');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const con = new rtc.PeerConnection('pc', defaultOptions);
|
|
26
|
+
// window.rtc = rtc;
|
|
27
|
+
|
|
28
|
+
peer.rtcCon = con;
|
|
29
|
+
peer.rtcOfferer = true;
|
|
30
|
+
|
|
31
|
+
let gatheringComplete = false;
|
|
32
|
+
let offerSent = false;
|
|
33
|
+
const start = Date.now();
|
|
34
|
+
|
|
35
|
+
function sendOffer() {
|
|
36
|
+
const desc = con.localDescription();
|
|
37
|
+
peer.methods.rtcSignal({type: desc.type, sdp: desc.sdp});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
con.onGatheringStateChange = (state) => {
|
|
41
|
+
debug('onGatheringStateChange', state);
|
|
42
|
+
if (state === 'complete') {
|
|
43
|
+
debug('icegathering done', Date.now() - start);
|
|
44
|
+
gatheringComplete = true;
|
|
45
|
+
// We only want to provide an answer once all of our candidates have been added to the SDP.
|
|
46
|
+
sendOffer();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
con.onStateChange((state) => {
|
|
51
|
+
debug('offerer onStateChange: ', state);
|
|
52
|
+
if (state === 'connected') {
|
|
53
|
+
peer.connected = true;
|
|
54
|
+
peer.rtcEvents.emit('connected', con);
|
|
55
|
+
} else if (state === 'disconnected') {
|
|
56
|
+
peer.connected = false;
|
|
57
|
+
peer.rtcEvents.emit('disconnected', con);
|
|
58
|
+
peer.rtcCon = null;
|
|
59
|
+
peer.dc = null;
|
|
60
|
+
} else if (state === 'closed') {
|
|
61
|
+
peer.connected = false;
|
|
62
|
+
peer.rtcEvents.emit('closed', con);
|
|
63
|
+
peer.rtcCon = null;
|
|
64
|
+
peer.dc = null;
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
con.onDataChannel((dc) => {
|
|
69
|
+
debug('dc from offerer', dc);
|
|
70
|
+
peer.dc = dc;
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const dc = con.createDataChannel('fromofferer');
|
|
74
|
+
dc.onOpen(() => {
|
|
75
|
+
peer.dc = dc;
|
|
76
|
+
peer.dcOpen = true;
|
|
77
|
+
peer.rtcEvents.emit('dcOpen', dc);
|
|
78
|
+
peer.rtcSend = (packet) => {
|
|
79
|
+
dc.sendMessageBinary(packet);
|
|
80
|
+
};
|
|
81
|
+
// dc.sendMessage("Hello from node from offerer");
|
|
82
|
+
// peer.dc.onStateChange((state) => {
|
|
83
|
+
// debug('dc state change', state);
|
|
84
|
+
// });
|
|
85
|
+
debug('keys', Object.keys(dc));
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
dc.onMessage((msg) => {
|
|
89
|
+
debug('node offerer received msg:', msg.length);
|
|
90
|
+
peer.rtcEvents.emit('packet', msg);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
con.setLocalDescription();
|
|
94
|
+
|
|
95
|
+
setTimeout(() => {
|
|
96
|
+
if (!gatheringComplete) {
|
|
97
|
+
debug('didnt finish gathering');
|
|
98
|
+
sendOffer();
|
|
99
|
+
offerSent = true;
|
|
100
|
+
}
|
|
101
|
+
}, GATHERING_TIMEOUT);
|
|
102
|
+
|
|
103
|
+
peer.handleRtcAnswer = (answer) => {
|
|
104
|
+
debug('node handleRtcAnswer', answer.sdp.length);
|
|
105
|
+
con.setRemoteDescription(answer.sdp, answer.type);
|
|
106
|
+
return 'node handleRtcAnswer ok';
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async function answerPeer(peer, offer) {
|
|
111
|
+
if (!rtc.PeerConnection) {
|
|
112
|
+
throw new Error('node-datachannel not installed');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const con = new rtc.PeerConnection('pc', defaultOptions);
|
|
116
|
+
peer.rtcCon = con;
|
|
117
|
+
|
|
118
|
+
function sendAnswer() {
|
|
119
|
+
const desc = con.localDescription();
|
|
120
|
+
peer.methods.rtcSignal({type: desc.type, sdp: desc.sdp});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
con.onStateChange((state) => {
|
|
124
|
+
debug('answerer onStateChange: ', state);
|
|
125
|
+
if (state === 'connected') {
|
|
126
|
+
peer.connected = true;
|
|
127
|
+
peer.rtcEvents.emit('connected', con);
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
con.onGatheringStateChange((state) => {
|
|
132
|
+
debug('answerer GATHERING STATE: ', state);
|
|
133
|
+
|
|
134
|
+
if (state == 'complete') {
|
|
135
|
+
sendAnswer();
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
con.setRemoteDescription(offer.sdp, offer.type);
|
|
140
|
+
con.setLocalDescription();
|
|
141
|
+
|
|
142
|
+
con.onDataChannel((dc) => {
|
|
143
|
+
debug("node answerer got dataChannel: ", dc.getLabel());
|
|
144
|
+
dc.onMessage((msg) => {
|
|
145
|
+
debug('node answerer Received Msg:', msg.length);
|
|
146
|
+
peer.rtcEvents.emit('packet', msg);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// dc.sendMessage("Hello From node answerer");
|
|
150
|
+
peer.dcOpen = true;
|
|
151
|
+
peer.dc = dc;
|
|
152
|
+
peer.rtcEvents.emit('dcOpen', dc);
|
|
153
|
+
peer.rtcSend = (packet) => {
|
|
154
|
+
dc.sendMessageBinary(packet);
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
// peer.dc.onStateChange((state) => {
|
|
158
|
+
// debug('dc state change', state);
|
|
159
|
+
// });
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
rtc.offerPeer = offerPeer;
|
|
165
|
+
rtc.answerPeer = answerPeer;
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
module.exports = rtc;
|
package/lib/rtc-web.js
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
const debug = require('debug')('hsync:rtc-web');
|
|
2
|
+
const debugError = require('debug')('errors');
|
|
3
|
+
|
|
4
|
+
const rtc = {
|
|
5
|
+
PeerConnection: RTCPeerConnection,
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
const defaultOptions = {
|
|
10
|
+
// Recommended for libdatachannel
|
|
11
|
+
// bundlePolicy: "max-bundle",
|
|
12
|
+
iceServers: [{ 'urls': 'stun:stun.l.google.com:19302' }]
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const GATHERING_TIMEOUT = 4000;
|
|
16
|
+
|
|
17
|
+
async function offerPeer(peer) {
|
|
18
|
+
|
|
19
|
+
const con = new RTCPeerConnection(defaultOptions);
|
|
20
|
+
// window.rtc = rtc;
|
|
21
|
+
|
|
22
|
+
peer.rtcCon = con;
|
|
23
|
+
peer.rtcOfferer = true;
|
|
24
|
+
|
|
25
|
+
let gatheringComplete = false;
|
|
26
|
+
let offerSent = false;
|
|
27
|
+
const start = Date.now();
|
|
28
|
+
|
|
29
|
+
function sendOffer(alreadySent) {
|
|
30
|
+
debug('send offer', alreadySent);
|
|
31
|
+
const desc = con.localDescription;
|
|
32
|
+
peer.methods.rtcSignal({type: desc.type, sdp: desc.sdp, alreadySent});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
con.onicegatheringstatechange = (state) => {
|
|
36
|
+
debug('state change', con.iceGatheringState);
|
|
37
|
+
if (con.iceGatheringState === 'complete') {
|
|
38
|
+
debug('icegathering done', Date.now() - start);
|
|
39
|
+
gatheringComplete = true;
|
|
40
|
+
// We only want to provide an answer once all of our candidates have been added to the SDP.
|
|
41
|
+
sendOffer(offerSent);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
con.onicecandidate = (ice) => {
|
|
46
|
+
debug('ice candidate', ice);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
con.onconnectionstatechange = (event) => {
|
|
50
|
+
debug('connection state', con.connectionState, event);
|
|
51
|
+
if(con.connectionState === 'connected') {
|
|
52
|
+
peer.connected = true;
|
|
53
|
+
peer.rtcEvents.emit('connected', con);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
con.ondatachannel = (event) => {
|
|
58
|
+
debug('dc from answerer', event);
|
|
59
|
+
peer.dc = event.channel;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const dc = con.createDataChannel('from web');
|
|
63
|
+
|
|
64
|
+
peer.dc = dc;
|
|
65
|
+
dc.onmessage = (event) => {
|
|
66
|
+
debug('dc event', event.data);
|
|
67
|
+
peer.rtcEvents.emit('packet', event.data);
|
|
68
|
+
};
|
|
69
|
+
dc.onopen = (event) => {
|
|
70
|
+
peer.dcOpen = true;
|
|
71
|
+
peer.dc = dc;
|
|
72
|
+
peer.rtcEvents.emit('dcOpen', dc);
|
|
73
|
+
peer.rtcSend = (packet) => {
|
|
74
|
+
dc.send(packet);
|
|
75
|
+
};
|
|
76
|
+
// dc.send('yo waddup from the browser');
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const offer = await con.createOffer({offerToReceiveAudio:true, offerToReceiveVideo:true});
|
|
80
|
+
await con.setLocalDescription(offer);
|
|
81
|
+
|
|
82
|
+
setTimeout(() => {
|
|
83
|
+
if (!gatheringComplete) {
|
|
84
|
+
debug('didnt finish gathering');
|
|
85
|
+
sendOffer();
|
|
86
|
+
offerSent = true;
|
|
87
|
+
}
|
|
88
|
+
}, GATHERING_TIMEOUT);
|
|
89
|
+
|
|
90
|
+
peer.handleRtcAnswer = (answer) => {
|
|
91
|
+
debug('node handleRtcAnswer', answer.sdp.length);
|
|
92
|
+
con.setRemoteDescription(answer);
|
|
93
|
+
return 'web handleRtcAnswer ok';
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async function answerPeer(peer, offer) {
|
|
98
|
+
const options = {...defaultOptions, bundlePolicy: "max-bundle"};
|
|
99
|
+
const con = new RTCPeerConnection(options);
|
|
100
|
+
// window.rtc = rtc;
|
|
101
|
+
|
|
102
|
+
peer.rtcCon = con;
|
|
103
|
+
|
|
104
|
+
let gatheringComplete = false;
|
|
105
|
+
const start = Date.now();
|
|
106
|
+
|
|
107
|
+
function sendAnswer() {
|
|
108
|
+
const desc = con.localDescription;
|
|
109
|
+
peer.methods.rtcSignal({type: desc.type, sdp: desc.sdp});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
con.onicegatheringstatechange = (state) => {
|
|
113
|
+
if (con.iceGatheringState === 'complete') {
|
|
114
|
+
gatheringComplete = true;
|
|
115
|
+
debug('answerer icegathering done', Date.now() - start);
|
|
116
|
+
sendAnswer();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
await con.setRemoteDescription(offer);
|
|
120
|
+
|
|
121
|
+
let answer = await con.createAnswer();
|
|
122
|
+
await con.setLocalDescription(answer);
|
|
123
|
+
|
|
124
|
+
con.onicecandidate = (ice) => {
|
|
125
|
+
debug('ice candidate', ice);
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
con.onconnectionstatechange = (event) => {
|
|
129
|
+
debug('connection state', con.connectionState, event);
|
|
130
|
+
if(con.connectionState === 'connected') {
|
|
131
|
+
peer.connected = true;
|
|
132
|
+
peer.rtcEvents.emit('connected', con);
|
|
133
|
+
} else if (con.connectionState === 'disconnected') {
|
|
134
|
+
peer.connected = false;
|
|
135
|
+
peer.rtcEvents.emit('disconnected', con);
|
|
136
|
+
peer.rtcCon = null;
|
|
137
|
+
peer.dc = null;
|
|
138
|
+
} else if (con.connectionState === 'closed') {
|
|
139
|
+
peer.connected = false;
|
|
140
|
+
peer.rtcEvents.emit('closed', con);
|
|
141
|
+
peer.rtcCon = null;
|
|
142
|
+
peer.dc = null;
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
con.ondatachannel = (event) => {
|
|
147
|
+
debug('dc from node', event);
|
|
148
|
+
peer.dcOpen = true;
|
|
149
|
+
peer.dc = event.channel;
|
|
150
|
+
peer.rtcEvents.emit('dcOpen', peer.dc);
|
|
151
|
+
peer.rtcSend = (packet) => {
|
|
152
|
+
peer.dc.send(packet);
|
|
153
|
+
};
|
|
154
|
+
peer.dc.onmessage = (event) => {
|
|
155
|
+
peer.rtcEvents.emit('packet', event.data);
|
|
156
|
+
};
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
setTimeout(() => {
|
|
160
|
+
if (!gatheringComplete) {
|
|
161
|
+
debug('didnt finish gathering');
|
|
162
|
+
sendAnswer();
|
|
163
|
+
}
|
|
164
|
+
}, GATHERING_TIMEOUT);
|
|
165
|
+
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
rtc.offerPeer = offerPeer;
|
|
170
|
+
rtc.answerPeer = answerPeer;
|
|
171
|
+
|
|
172
|
+
module.exports = rtc;
|