hsync 0.18.3 → 0.20.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/Readme.md +16 -4
- package/cli.js +12 -1
- package/connection.js +40 -53
- package/dist/hsync.js +15 -15
- package/dist/hsync.min.js +1 -1
- package/hsync-web.js +4 -15
- package/hsync.js +10 -2
- package/lib/fetch.js +11 -16
- package/lib/peers.js +210 -188
- package/lib/rtc-node.js +94 -84
- package/lib/rtc-web.js +72 -65
- package/lib/socket-listeners.js +182 -0
- package/lib/socket-relays.js +123 -0
- package/package.json +7 -6
- package/shell.js +33 -0
- package/lib/socket-listen-handler.js +0 -135
- package/lib/socket-relay-handler.js +0 -105
- package/tn.js +0 -34
package/lib/rtc-node.js
CHANGED
|
@@ -16,95 +16,105 @@ const defaultOptions = { iceServers: ['stun:stun.l.google.com:19302'] };
|
|
|
16
16
|
|
|
17
17
|
const GATHERING_TIMEOUT = 4000;
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const con = new rtc.PeerConnection('pc', defaultOptions);
|
|
26
|
-
// window.rtc = rtc;
|
|
27
|
-
|
|
28
|
-
peer.rtcCon = con;
|
|
29
|
-
peer.rtcOfferer = true;
|
|
19
|
+
function offerPeer(peer) {
|
|
20
|
+
return new Promise(async (resolve, reject) => {
|
|
21
|
+
if (!rtc.PeerConnection) {
|
|
22
|
+
reject('node-datachannel not installed');
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
30
25
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
26
|
+
const con = new rtc.PeerConnection('pc', defaultOptions);
|
|
27
|
+
// window.rtc = rtc;
|
|
28
|
+
|
|
29
|
+
peer.rtcCon = con;
|
|
30
|
+
peer.rtcOfferer = true;
|
|
31
|
+
|
|
32
|
+
let gatheringComplete = false;
|
|
33
|
+
let offerSent = false;
|
|
34
|
+
const start = Date.now();
|
|
35
|
+
|
|
36
|
+
async function sendOffer() {
|
|
37
|
+
const desc = con.localDescription();
|
|
38
|
+
try {
|
|
39
|
+
const resp = await peer.methods.rtcSignal({type: desc.type, sdp: desc.sdp});
|
|
40
|
+
resolve(resp);
|
|
41
|
+
} catch (e) {
|
|
42
|
+
debugError('error sending offer', e);
|
|
43
|
+
reject(e);
|
|
44
|
+
}
|
|
45
|
+
// peer.methods.rtcSignal({type: desc.type, sdp: desc.sdp});
|
|
47
46
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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;
|
|
47
|
+
|
|
48
|
+
con.onGatheringStateChange = (state) => {
|
|
49
|
+
debug('onGatheringStateChange', state);
|
|
50
|
+
if (state === 'complete') {
|
|
51
|
+
debug('icegathering done', Date.now() - start);
|
|
52
|
+
gatheringComplete = true;
|
|
53
|
+
// We only want to provide an answer once all of our candidates have been added to the SDP.
|
|
54
|
+
sendOffer();
|
|
55
|
+
}
|
|
65
56
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
57
|
+
|
|
58
|
+
con.onStateChange((state) => {
|
|
59
|
+
debug('offerer onStateChange: ', state);
|
|
60
|
+
if (state === 'connected') {
|
|
61
|
+
peer.connected = true;
|
|
62
|
+
peer.rtcEvents.emit('connected', con);
|
|
63
|
+
} else if (state === 'disconnected') {
|
|
64
|
+
peer.connected = false;
|
|
65
|
+
peer.rtcEvents.emit('disconnected', con);
|
|
66
|
+
peer.rtcCon = null;
|
|
67
|
+
peer.dc = null;
|
|
68
|
+
} else if (state === 'closed') {
|
|
69
|
+
peer.connected = false;
|
|
70
|
+
peer.rtcEvents.emit('closed', con);
|
|
71
|
+
peer.rtcCon = null;
|
|
72
|
+
peer.dc = null;
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
con.onDataChannel((dc) => {
|
|
77
|
+
debug('offerer onDataChannel', dc);
|
|
78
|
+
peer.dc = dc;
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const dc = con.createDataChannel('fromofferer');
|
|
82
|
+
dc.onOpen(() => {
|
|
83
|
+
peer.dc = dc;
|
|
84
|
+
peer.dcOpen = true;
|
|
85
|
+
peer.rtcSend = (packet) => {
|
|
86
|
+
dc.sendMessageBinary(packet);
|
|
87
|
+
};
|
|
88
|
+
peer.rtcEvents.emit('dcOpen', dc);
|
|
89
|
+
// dc.sendMessage("Hello from node from offerer");
|
|
90
|
+
// peer.dc.onStateChange((state) => {
|
|
91
|
+
// debug('dc state change', state);
|
|
92
|
+
// });
|
|
93
|
+
debug('keys', Object.keys(dc));
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
dc.onMessage((msg) => {
|
|
97
|
+
debug('node offerer received msg:', msg.length);
|
|
98
|
+
peer.rtcEvents.emit('packet', msg);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
con.setLocalDescription();
|
|
102
|
+
|
|
103
|
+
setTimeout(() => {
|
|
104
|
+
if (!gatheringComplete) {
|
|
105
|
+
debug('didnt finish gathering');
|
|
106
|
+
sendOffer();
|
|
107
|
+
offerSent = true;
|
|
108
|
+
}
|
|
109
|
+
}, GATHERING_TIMEOUT);
|
|
110
|
+
|
|
111
|
+
peer.handleRtcAnswer = (answer) => {
|
|
112
|
+
debug('node handleRtcAnswer', answer.sdp.length);
|
|
113
|
+
con.setRemoteDescription(answer.sdp, answer.type);
|
|
114
|
+
return 'node handleRtcAnswer ok';
|
|
100
115
|
}
|
|
101
|
-
}, GATHERING_TIMEOUT);
|
|
102
116
|
|
|
103
|
-
|
|
104
|
-
debug('node handleRtcAnswer', answer.sdp.length);
|
|
105
|
-
con.setRemoteDescription(answer.sdp, answer.type);
|
|
106
|
-
return 'node handleRtcAnswer ok';
|
|
107
|
-
}
|
|
117
|
+
});
|
|
108
118
|
}
|
|
109
119
|
|
|
110
120
|
async function answerPeer(peer, offer) {
|
package/lib/rtc-web.js
CHANGED
|
@@ -15,83 +15,90 @@ const defaultOptions = {
|
|
|
15
15
|
const GATHERING_TIMEOUT = 4000;
|
|
16
16
|
|
|
17
17
|
async function offerPeer(peer) {
|
|
18
|
+
return new Promise(async (resolve, reject) => {
|
|
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
|
+
async function sendOffer(alreadySent) {
|
|
30
|
+
debug('send offer', alreadySent);
|
|
31
|
+
const desc = con.localDescription;
|
|
32
|
+
try {
|
|
33
|
+
const resp = await peer.methods.rtcSignal({type: desc.type, sdp: desc.sdp, alreadySent});
|
|
34
|
+
resolve(resp);
|
|
35
|
+
} catch (e) {
|
|
36
|
+
debugError('error sending offer', e);
|
|
37
|
+
reject(e);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
18
40
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
41
|
+
con.onicegatheringstatechange = (state) => {
|
|
42
|
+
debug('state change', con.iceGatheringState);
|
|
43
|
+
if (con.iceGatheringState === 'complete') {
|
|
44
|
+
debug('icegathering done', Date.now() - start);
|
|
45
|
+
gatheringComplete = true;
|
|
46
|
+
// We only want to provide an answer once all of our candidates have been added to the SDP.
|
|
47
|
+
sendOffer(offerSent);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
28
50
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
peer.methods.rtcSignal({type: desc.type, sdp: desc.sdp, alreadySent});
|
|
33
|
-
}
|
|
51
|
+
con.onicecandidate = (ice) => {
|
|
52
|
+
debug('ice candidate', ice);
|
|
53
|
+
};
|
|
34
54
|
|
|
35
|
-
|
|
36
|
-
debug('state
|
|
37
|
-
if
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
// We only want to provide an answer once all of our candidates have been added to the SDP.
|
|
41
|
-
sendOffer(offerSent);
|
|
55
|
+
con.onconnectionstatechange = (event) => {
|
|
56
|
+
debug('connection state', con.connectionState, event);
|
|
57
|
+
if(con.connectionState === 'connected') {
|
|
58
|
+
peer.connected = true;
|
|
59
|
+
peer.rtcEvents.emit('connected', con);
|
|
42
60
|
}
|
|
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
|
-
};
|
|
61
|
+
};
|
|
56
62
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
63
|
+
con.ondatachannel = (event) => {
|
|
64
|
+
debug('dc from answerer', event);
|
|
65
|
+
peer.dc = event.channel;
|
|
66
|
+
};
|
|
61
67
|
|
|
62
|
-
|
|
68
|
+
const dc = con.createDataChannel('from web');
|
|
63
69
|
|
|
64
|
-
peer.dc = dc;
|
|
65
|
-
dc.onmessage = (event) => {
|
|
66
|
-
debug('dc.onmessage', event.data);
|
|
67
|
-
peer.rtcEvents.emit('packet', event.data);
|
|
68
|
-
};
|
|
69
|
-
dc.onopen = (event) => {
|
|
70
|
-
peer.dcOpen = true;
|
|
71
70
|
peer.dc = dc;
|
|
72
|
-
|
|
73
|
-
dc.
|
|
71
|
+
dc.onmessage = (event) => {
|
|
72
|
+
debug('dc.onmessage', event.data);
|
|
73
|
+
peer.rtcEvents.emit('packet', event.data);
|
|
74
|
+
};
|
|
75
|
+
dc.onopen = (event) => {
|
|
76
|
+
peer.dcOpen = true;
|
|
77
|
+
peer.dc = dc;
|
|
78
|
+
peer.rtcSend = (packet) => {
|
|
79
|
+
dc.send(packet);
|
|
80
|
+
};
|
|
81
|
+
peer.rtcEvents.emit('dcOpen', dc);
|
|
82
|
+
// dc.send('yo waddup from the browser');
|
|
74
83
|
};
|
|
75
|
-
peer.rtcEvents.emit('dcOpen', dc);
|
|
76
|
-
// dc.send('yo waddup from the browser');
|
|
77
|
-
};
|
|
78
84
|
|
|
79
|
-
|
|
80
|
-
|
|
85
|
+
const offer = await con.createOffer({offerToReceiveAudio:true, offerToReceiveVideo:true});
|
|
86
|
+
await con.setLocalDescription(offer);
|
|
81
87
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
88
|
+
setTimeout(() => {
|
|
89
|
+
if (!gatheringComplete) {
|
|
90
|
+
debug('didnt finish gathering');
|
|
91
|
+
sendOffer();
|
|
92
|
+
offerSent = true;
|
|
93
|
+
}
|
|
94
|
+
}, GATHERING_TIMEOUT);
|
|
89
95
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
96
|
+
peer.handleRtcAnswer = (answer) => {
|
|
97
|
+
debug('node handleRtcAnswer', answer.sdp.length);
|
|
98
|
+
con.setRemoteDescription(answer);
|
|
99
|
+
return 'web handleRtcAnswer ok';
|
|
100
|
+
}
|
|
101
|
+
});
|
|
95
102
|
}
|
|
96
103
|
|
|
97
104
|
async function answerPeer(peer, offer) {
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
const b64id = require('b64id');
|
|
2
|
+
const debug = require('debug')('hsync:listener');
|
|
3
|
+
const debugError = require('debug')('hsync:error');
|
|
4
|
+
|
|
5
|
+
const { sockets } = require('./socket-map');
|
|
6
|
+
|
|
7
|
+
let net;
|
|
8
|
+
|
|
9
|
+
function setNet(netImpl) {
|
|
10
|
+
net = netImpl;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
debugError.color = 1;
|
|
14
|
+
|
|
15
|
+
function initListeners(hsyncClient) {
|
|
16
|
+
const socketListeners = {};
|
|
17
|
+
|
|
18
|
+
function getSocketListeners() {
|
|
19
|
+
const hKeys = Object.keys(socketListeners);
|
|
20
|
+
debug('getSocketListeners', hKeys);
|
|
21
|
+
let retVal = hKeys.map((hk) => {
|
|
22
|
+
const l = socketListeners[hk];
|
|
23
|
+
return {
|
|
24
|
+
port: l.port,
|
|
25
|
+
targetHost: l.targetHost,
|
|
26
|
+
targetPort: l.targetPort,
|
|
27
|
+
};
|
|
28
|
+
});
|
|
29
|
+
return retVal;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function addSocketListener(options = {}) {
|
|
33
|
+
const { port, targetPort, targetHost } = options;
|
|
34
|
+
debug('creating handler', port, targetHost);
|
|
35
|
+
|
|
36
|
+
const rpcPeer = hsyncClient.getRPCPeer({ hostName: targetHost });
|
|
37
|
+
|
|
38
|
+
const socketServer = net.createServer(async (socket) => {
|
|
39
|
+
|
|
40
|
+
if (!rpcPeer.rtcCon) {
|
|
41
|
+
try {
|
|
42
|
+
debug('initiating connectRTC from socket listener');
|
|
43
|
+
await rpcPeer.connectRTC();
|
|
44
|
+
} catch (e) {
|
|
45
|
+
debug('error connecting to rtc', e);
|
|
46
|
+
socket.end();
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
rpcPeer.notifications.oncloseListenerSocket((remotePeer, { socketId }) => {
|
|
52
|
+
debug('closeListenerSocket', socketId, !!sockets[socketId]);
|
|
53
|
+
if (sockets[socketId]) {
|
|
54
|
+
sockets[socketId].end();
|
|
55
|
+
delete sockets[socketId];
|
|
56
|
+
return 'closeListenerSocket ok';
|
|
57
|
+
}
|
|
58
|
+
return `closeListenerSocket no matching socket for ${socketId}`;
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
socket.socketId = b64id.generateId();
|
|
62
|
+
sockets[socket.socketId] = socket;
|
|
63
|
+
rpcPeer.sockets[socket.socketId] = socket;
|
|
64
|
+
socket.listenerSocket = true;
|
|
65
|
+
debug('connected to local listener', port, socket.socketId);
|
|
66
|
+
socket.peerConnected = false;
|
|
67
|
+
// const pubTopic = `msg/${hostName}/${hsyncClient.myHostName}/socketData/${socket.socketId}`;
|
|
68
|
+
// const closeTopic = `msg/${hostName}/${hsyncClient.myHostName}/socketClose/${socket.socketId}`;
|
|
69
|
+
const dataQueue = [];
|
|
70
|
+
|
|
71
|
+
async function sendData(data) {
|
|
72
|
+
// TODO queue data if not connected
|
|
73
|
+
if (rpcPeer.packAndSend) {
|
|
74
|
+
debug('sending data via rtc', targetHost, socket.socketId, data.length);
|
|
75
|
+
rpcPeer.packAndSend(`socketData/${socket.socketId}`, data);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
// debug('sending data via rpc', targetHost, data.length);
|
|
79
|
+
// // hsyncClient.mqConn.publish(pubTopic, data);
|
|
80
|
+
// const result = await rpcPeer.methods.receiveListenerData({
|
|
81
|
+
// socketId: socket.socketId,
|
|
82
|
+
// data: Buffer.from(data).toString('base64'),
|
|
83
|
+
// });
|
|
84
|
+
// debug('sendData from Listener', result);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
socket.on('data', async (data) => {
|
|
88
|
+
debug('socket data', data ? data.length : '');
|
|
89
|
+
// if (!socket.peerConnected) {
|
|
90
|
+
// dataQueue.push(data);
|
|
91
|
+
// return;
|
|
92
|
+
// }
|
|
93
|
+
sendData(data);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
socket.on('close', (a, b, c) => {
|
|
97
|
+
debug('listener socket closed', port, socket.socketId, a, b, c);
|
|
98
|
+
if (sockets[socket.socketId]) {
|
|
99
|
+
delete sockets[socket.socketId];
|
|
100
|
+
try {
|
|
101
|
+
rpcPeer.notifiers.closeRelaySocket({
|
|
102
|
+
socketId: socket.socketId,
|
|
103
|
+
});
|
|
104
|
+
} catch (e) {
|
|
105
|
+
debug('error closing relay socket', e);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
socket.on('error', (error) => {
|
|
111
|
+
debug('socket error', targetHost, socket.socketId, error);
|
|
112
|
+
if (sockets[socket.socketId]) {
|
|
113
|
+
delete sockets[socket.socketId];
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
try {
|
|
118
|
+
debug('connecting remotely', socket.socketId, targetPort, rpcPeer.hostName, targetHost);
|
|
119
|
+
const result = await rpcPeer.methods.connectSocket({
|
|
120
|
+
socketId: socket.socketId,
|
|
121
|
+
port: targetPort,
|
|
122
|
+
hostName: rpcPeer.hostName,
|
|
123
|
+
});
|
|
124
|
+
debug('connect result', result);
|
|
125
|
+
socket.peerConnected = true;
|
|
126
|
+
dataQueue.forEach(sendData);
|
|
127
|
+
} catch (e) {
|
|
128
|
+
debugError('cant connect remotely', targetHost, targetPort, e);
|
|
129
|
+
if (sockets[socket.socketId]) {
|
|
130
|
+
delete sockets[socket.socketId];
|
|
131
|
+
}
|
|
132
|
+
socket.end();
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
socketServer.listen(port);
|
|
137
|
+
|
|
138
|
+
function end() {
|
|
139
|
+
const sockKeys = Object.keys(sockets);
|
|
140
|
+
sockKeys.forEach((sk) => {
|
|
141
|
+
try {
|
|
142
|
+
if (sockets[sk].listenerSocket) {
|
|
143
|
+
sockets[sk].end();
|
|
144
|
+
delete sockets[sk];
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
catch(e) {
|
|
148
|
+
debug('error closing socket', e);
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const listener = {
|
|
154
|
+
socketServer,
|
|
155
|
+
sockets,
|
|
156
|
+
end,
|
|
157
|
+
targetHost,
|
|
158
|
+
targetPort,
|
|
159
|
+
port,
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
socketListeners['p' + port] = listener;
|
|
163
|
+
return listener;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
hsyncClient.socketListeners = socketListeners;
|
|
167
|
+
hsyncClient.addSocketListener = addSocketListener;
|
|
168
|
+
// hsyncClient.receiveRelayData = receiveRelayData;
|
|
169
|
+
hsyncClient.getSocketListeners = getSocketListeners;
|
|
170
|
+
// hsyncClient.closeListenerSocket = closeListenerSocket;
|
|
171
|
+
|
|
172
|
+
return {
|
|
173
|
+
addSocketListener,
|
|
174
|
+
// receiveRelayData,
|
|
175
|
+
getSocketListeners,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
module.exports = {
|
|
180
|
+
initListeners,
|
|
181
|
+
setNet,
|
|
182
|
+
};
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
const debug = require('debug')('hsync:relay');
|
|
2
|
+
const debugError = require('debug')('hsync:error');
|
|
3
|
+
const { sockets } = require('./socket-map');
|
|
4
|
+
|
|
5
|
+
debugError.color = 1;
|
|
6
|
+
|
|
7
|
+
let net;
|
|
8
|
+
|
|
9
|
+
function setNet(netImpl) {
|
|
10
|
+
net = netImpl;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function initRelays(hsyncClient) {
|
|
14
|
+
const cachedRelays = {};
|
|
15
|
+
|
|
16
|
+
function getSocketRelays() {
|
|
17
|
+
const hKeys = Object.keys(cachedRelays);
|
|
18
|
+
debug('getSocketListeners', hKeys);
|
|
19
|
+
let retVal = hKeys.map((hk) => {
|
|
20
|
+
const l = cachedRelays[hk];
|
|
21
|
+
return {
|
|
22
|
+
port: l.port,
|
|
23
|
+
targetHost: l.targetHost,
|
|
24
|
+
targetPort: l.targetPort,
|
|
25
|
+
whitelist: l.whitelist || '',
|
|
26
|
+
blacklist: l.blacklist || '',
|
|
27
|
+
};
|
|
28
|
+
});
|
|
29
|
+
return retVal;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function connectSocket(remotePeer, { port, socketId }) {
|
|
33
|
+
|
|
34
|
+
remotePeer.notifications.oncloseRelaySocket((remotePeer, { socketId }) => {
|
|
35
|
+
debug('closeRelaySocket', socketId);
|
|
36
|
+
if (sockets[socketId]) {
|
|
37
|
+
sockets[socketId].end();
|
|
38
|
+
delete sockets[socketId];
|
|
39
|
+
return 'closeRelaySocket ok';
|
|
40
|
+
}
|
|
41
|
+
return `closeRelaySocket no matching socket for ${socketId}`;
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const relay = cachedRelays['p' + port];
|
|
45
|
+
debug('connect relay', port, socketId, remotePeer.hostName);
|
|
46
|
+
if (!relay) {
|
|
47
|
+
throw new Error('no relay found for port: ' + port);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// TODO: check white and black lists on remotePeer
|
|
51
|
+
|
|
52
|
+
// const relayDataTopic = `msg/${hostName}/${hsyncClient.myHostName}/relayData/${socketId}`;
|
|
53
|
+
return new Promise((resolve, reject) => {
|
|
54
|
+
const socket = new net.Socket();
|
|
55
|
+
socket.socketId = socketId;
|
|
56
|
+
sockets[socketId] = socket;
|
|
57
|
+
socket.connect(relay.targetPort, relay.targetHost, () => {
|
|
58
|
+
debug(`CONNECTED TO LOCAL SERVER`, socket.socketId, socket.hostName, port);
|
|
59
|
+
resolve({socketId, targetHost: relay.targetHost, targetPort: relay.targetPort});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
socket.on('data', async (data) => {
|
|
63
|
+
debug(`data in ${socket.socketId}`, relay.targetPort, relay.targetHost, data.length);
|
|
64
|
+
// TODO: queue data if remotePeer is not ready
|
|
65
|
+
if (remotePeer.packAndSend) {
|
|
66
|
+
debug('sending relay data via rtc', socket.socketId, data.length);
|
|
67
|
+
remotePeer.packAndSend(`socketData/${socket.socketId}`, Buffer.from(data));
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
socket.on('close', async () => {
|
|
72
|
+
debug(`LOCAL CONNECTION CLOSED`, socket.socketId);
|
|
73
|
+
if (sockets[socket.socketId]) {
|
|
74
|
+
try {
|
|
75
|
+
await remotePeer.notifiers.closeListenerSocket({socketId});
|
|
76
|
+
} catch (e) {
|
|
77
|
+
debug('error closing socket', e);
|
|
78
|
+
}
|
|
79
|
+
delete sockets[socket.socketId];
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
socket.on('error', (e) => {
|
|
84
|
+
debugError(`LOCAL CONNECTION ERROR`, socket.socketId, e);
|
|
85
|
+
delete sockets[socket.socketId];
|
|
86
|
+
reject(e);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function addSocketRelay({whitelist, blacklist, port, targetPort, targetHost = 'localhost'}) {
|
|
94
|
+
targetPort = targetPort || port;
|
|
95
|
+
debug('creating relay', whitelist, blacklist, port, targetPort, targetHost);
|
|
96
|
+
const newRelay = {
|
|
97
|
+
whitelist,
|
|
98
|
+
blacklist,
|
|
99
|
+
port,
|
|
100
|
+
targetPort,
|
|
101
|
+
targetHost,
|
|
102
|
+
};
|
|
103
|
+
cachedRelays['p' + port] = newRelay;
|
|
104
|
+
return newRelay;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
hsyncClient.cachedRelays = cachedRelays;
|
|
108
|
+
hsyncClient.addSocketRelay = addSocketRelay;
|
|
109
|
+
hsyncClient.getSocketRelays = getSocketRelays;
|
|
110
|
+
hsyncClient.connectSocket = connectSocket;
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
// receiveListenerData,
|
|
114
|
+
getSocketRelays,
|
|
115
|
+
connectSocket,
|
|
116
|
+
addSocketRelay,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
module.exports = {
|
|
121
|
+
initRelays,
|
|
122
|
+
setNet,
|
|
123
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hsync",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.0",
|
|
4
4
|
"description": "client for hsync-server",
|
|
5
5
|
"main": "hsync.js",
|
|
6
6
|
"scripts": {
|
|
@@ -24,7 +24,8 @@
|
|
|
24
24
|
"./hsync": "./hsync-web"
|
|
25
25
|
},
|
|
26
26
|
"bin": {
|
|
27
|
-
"hsync": "./cli.js"
|
|
27
|
+
"hsync": "./cli.js",
|
|
28
|
+
"sh-hsync": "./shell.js"
|
|
28
29
|
},
|
|
29
30
|
"author": "Luis Montes",
|
|
30
31
|
"license": "ISC",
|
|
@@ -38,14 +39,14 @@
|
|
|
38
39
|
"mqtt-packet-web": "^8.2.0",
|
|
39
40
|
"net-web": "^0.2.0",
|
|
40
41
|
"precompiled-mqtt": "^4.3.14-beta",
|
|
41
|
-
"rawr": "^0.15.1"
|
|
42
|
-
"webpack": "^5.88.2",
|
|
43
|
-
"webpack-cli": "^4.10.0"
|
|
42
|
+
"rawr": "^0.15.1"
|
|
44
43
|
},
|
|
45
44
|
"optionalDependencies": {
|
|
46
45
|
"node-datachannel": "^0.4.3"
|
|
47
46
|
},
|
|
48
47
|
"devDependencies": {
|
|
49
|
-
"nodemon": "^3.0.1"
|
|
48
|
+
"nodemon": "^3.0.1",
|
|
49
|
+
"webpack": "^5.88.2",
|
|
50
|
+
"webpack-cli": "^4.10.0"
|
|
50
51
|
}
|
|
51
52
|
}
|