OverviewFilesCommitsIssuesDocsReleasesPeers

holepunchto-deps / @holepunchto / keet-core / lib / core / call / proxy.js

1 lines · javascript ref:
const s=require("ready-resource"),e=require("protomux"),t=require("hypercore-id-encoding"),i=require("safety-catch"),o=require("b4a"),n=require("compact-encoding"),r="127.0.0.1",a="keet-webrtc/v1",h={preencode(s,e){n.uint.preencode(s,e.alias)},encode(s,e){n.uint.encode(s,e.alias)},decode:s=>({alias:n.uint.decode(s)})};class l{constructor(s){this.connection=s,this.closed=!1,this._localAlias=0,this._proxiesByRemoteAlias=new Array(256),this._prefixesByLocalAlias=new Array(256),this._onmessage=s=>{if(this.closed)return;const e=this._proxiesByRemoteAlias[s[0]];e&&e._onRawData(s.subarray(2))},s.rawStream.on("message",this._onmessage)}createLocalAlias(){this._localAlias=(this._localAlias+1)%256;const s=o.allocUnsafe(2);return s[0]=this._localAlias,s[1]=225,this._prefixesByLocalAlias[this._localAlias]=s,this._localAlias}send(s,e){if(this.closed)return;const t=o.allocUnsafe(e.byteLength+2);this._prefixesByLocalAlias[s].copy(t,0),e.copy(t,2),this.connection.rawStream.trySend(t)}register(s,e){this._proxiesByRemoteAlias[s]=e}unregister(s){this._proxiesByRemoteAlias[s]=null}close(){this.closed=!0,this.connection.rawStream.removeListener("message",this._onmessage)}}class c{constructor(s,e,o,n,{onopen:h=null,onclose:l=null,onsignal:c=null}={}){this.proxyPort=0,this.closed=!1,this._muxer=e,this._rawMuxer=o,this._socket=n,this._channel=null,this._sendSignal=null,this._localAlias=o.createLocalAlias(),this._remoteAlias=-1,this._rinfo=null,this._sendBuf=[],this._recvBuf=[],this.onopen=h,this.onclose=l,this.onsignal=c,n.bind(0,r),n.on("message",(s,e)=>this._onUdp(s,e)),n.on("error",i),this.proxyPort=n.address().port,this._muxerInfo={protocol:a,id:t.decode(s)},e.pair(this._muxerInfo,()=>this._createChannel()),this._createChannel()}_createChannel(){const s=this._muxer.createChannel({protocol:a,id:this._muxerInfo.id,handshake:h,onopen:({alias:s})=>{for(this._remoteAlias=s,this._rawMuxer.register(s,this);this._sendBuf.length;)this._rawMuxer.send(this._localAlias,this._sendBuf.pop());this.onopen&&this.onopen(this.proxyPort)},onclose:()=>{-1!==this._remoteAlias&&(this._rawMuxer.unregister(this._remoteAlias),this._remoteAlias=-1),this._rinfo=null,this.onclose&&this.onclose()},messages:[{encoding:n.json,onmessage:s=>{this.onsignal&&this.onsignal(s)}}]});s&&(this._channel=s,this._sendSignal=s.messages[0],s.open({alias:this._localAlias}))}_onUdp(s,e){if(!this.closed&&e.host===r){if(!this._rinfo||this._rinfo.port!==e.port)for(this._rinfo=e;this._recvBuf.length;)this._socket.trySend(this._recvBuf.pop(),e.port,r);-1===this._remoteAlias?this._sendBuf.unshift(s):this._rawMuxer.send(this._localAlias,s)}}_onRawData(s){this.closed||(this._rinfo?this._socket.trySend(s,this._rinfo.port,r):this._recvBuf.unshift(s))}signal(s){!this.closed&&this._sendSignal&&this._sendSignal.send(s)}close(){this.closed||(this.closed=!0,this._muxer.unpair(this._muxerInfo),this._channel&&this._channel.close(),this._socket.close().catch(i))}}class _ extends s{constructor(s,e,t){super(),this._server=s,this._callId=e,this._swarm=t,this._entries=new Map,this._open=new Map,this._muxersByConn=new Map,this._onconnection=this._attach.bind(this),t.on("connection",this._onconnection);for(const s of t.connections)this._attach(s)}_attach(s){if(this._muxersByConn.has(s))return;s.on("error",i);const e=new l(s);this._muxersByConn.set(s,e),this._add(s,e),s.on("close",()=>{this._remove(s),e.close(),this._muxersByConn.delete(s)})}_add(s,i){if(this._entries.has(s))return;const o=t.encode(s.remotePublicKey),n=e.from(s),r=this._swarm.dht.udx.createSocket(),a={proxy:null,swarmId:o,port:0};this._entries.set(s,a),a.proxy=new c(this._callId,n,i,r,{onopen:s=>{a.port=s,this._announce(o)},onclose:()=>{a.port=0,this._announce(o)},onsignal:s=>this.emit("signal",{swarmId:o,data:s})})}_current(s){let e=null;for(const t of this._entries.values())t.swarmId===s&&t.port&&(e=t);return e}_announce(s){const e=this._current(s),t=e?e.port:0;t!==(this._open.get(s)||0)&&(t?(this._open.set(s,t),this.emit("peer",{swarmId:s,proxyPort:t})):(this._open.delete(s),this.emit("peer-close",{swarmId:s})))}openPeers(){return[...this._open]}_remove(s){const e=this._entries.get(s);e&&(this._entries.delete(s),e.proxy.close(),this._announce(e.swarmId))}signal(s,e){const t=this._current(s);t&&t.proxy.signal(e)}_close(){this._swarm.removeListener("connection",this._onconnection);for(const s of this._entries.keys())this._remove(s);for(const s of this._muxersByConn.values())s.close();this._muxersByConn.clear(),this._server._calls.delete(this._callId)}}module.exports=class{constructor(){this._calls=new Map}getCallById(s){return this._calls.get(s)}open(s,e){if(this._calls.has(s))return this._calls.get(s);const t=new _(this,s,e);return this._calls.set(s,t),t}close(s){const e=this._calls.get(s);e&&e.close()}};