diff --git a/Antunnel/build/debug/main.js b/Antunnel/build/debug/main.js index cd49372..d99596e 100644 --- a/Antunnel/build/debug/main.js +++ b/Antunnel/build/debug/main.js @@ -1 +1,492 @@ -(function(){var e,n,t,s,i;(t=class e{constructor(){this.header={sid:0,cid:0,type:0,size:0},this.data=void 0}as_raw(){var n,t,s;return s=21+this.header.size,(n=new Uint8Array(s)).set(e.MAGIC_START,0),n[4]=this.header.type,t=e.bytes_of(this.header.cid),n.set(t,5),t=e.bytes_of(this.header.sid),n.set(t,9),t=e.bytes_of(this.header.size),n.set(t,13),this.data&&n.set(this.data,17),n.set(e.MAGIC_END,this.header.size+17),n.buffer}}).decode=function(e){return new Promise((function(n,s){var i;return i=new t,t.int_from(t.MAGIC_START,0)!==t.int_from(e,0)?s("Unmatch message begin magic number"):(i.header.type=e[4],i.header.cid=t.int_from(e,5),i.header.sid=t.int_from(e,9),i.header.size=t.int_from(e,13),i.data=e.slice(17,17+i.header.size),t.int_from(t.MAGIC_END,0)!==t.int_from(e,17+i.header.size)?s("Unmatch message end magic number"):n(i))}))},t.bytes_of=function(e){var n;return(n=new Uint8Array(4))[0]=255&e,e>>=8,n[1]=255&e,e>>=8,n[2]=255&e,e>>=8,n[3]=255&e,n},t.int_from=function(e,n){return e[n]|e[n+1]<<8|e[n+2]<<16|e[n+3]<<24},t.OK=0,t.ERROR=1,t.DATA=6,t.CLOSE=5,t.SUBSCRIBE=2,t.UNSUBSCRIBE=3,t.CTRL=7,t.MAGIC_END=[65,78,84,68],t.MAGIC_START=[68,84,78,65],s=class{constructor(e){this.channel=e,this.id=void 0,this.channel_id=void 0,this.onmessage=void 0,this.onerror=void 0,this.onopen=void 0,this.onclose=void 0,this.tunnel=void 0,this.is_opened=!1}send(e,n){if(this.tunnel){if(this.is_opened)return this.tunnel.send(this.genmsg(e,n));this.onerror&&this.onerror("Channel is not opened yet")}else this.onerror&&this.onerror("Tunnel is not opened")}genmsg(e,n){var s;return(s=new t).header.sid=this.id,s.header.cid=this.channel_id,s.header.type=e,s.header.size=n?n.length:0,s.data=n,s}close(e){if(this.is_opened=!1,this.tunnel)return this.tunnel.unsubscribe(this,e)}},e=class{constructor(e){this.uri=e,this.socket=void 0,this.pending={},this.subscribers={},this.onclose=void 0}ready(){return new Promise((e,n)=>this.uri?void 0!==this.socket?e():(console.log("Connect to "+this.uri),this.socket=new WebSocket(this.uri),this.socket.binaryType="arraybuffer",this.socket.onmessage=e=>this.process(e),this.socket.onclose=e=>{var n,t,s,i;for(n in this.socket=void 0,t=this.pending)(i=t[n]).tunnel=void 0,i.onclose&&i.onclose();for(n in s=this.subscribers)(i=s[n]).tunnel=void 0,i.is_opened=!1,i.onclose&&i.onclose();if(this.pending={},this.subscribe={},this.onclose())return this.onclose()},this.socket.onerror=e=>{var n,t,s,i,r;for(n in t=this.pending)(r=t[n]).onerror&&r.onerror(e.toString());for(n in i=[],s=this.subscribers)(r=s[n]).onerror&&i.push(r.onerror(e.toString()));return i},this.socket.onopen=n=>e()):n())}process(e){return t.decode(new Uint8Array(e.data)).then(e=>{var n,s;switch(n=(e,n)=>{var t;if(!(t=this.pending[e.header.sid]))return(t=this.subscribers[e.header.sid])&&t[n]?t[n](e):void 0;t[n]&&t[n](e)},e.header.type){case t.OK:if(!(s=this.pending[e.header.sid]))return n(e,"onmessage");if(delete this.pending[e.header.sid],s.id=t.int_from(e.data,0),s.channel_id=e.header.cid,this.subscribers[s.id]=s,s.is_opened=!0,s.onopen)return s.onopen();break;case t.DATA:return n(e,"onmessage");case t.ERROR:return n(e,"onerror");case t.UNSUBSCRIBE:if(!(s=this.subscribers[e.header.sid]))return;return s.close(!0);default:return console.error(`Message of type ${e.header.type} is unsupported`,e)}}).catch(e=>{var n,t,s,i,r;for(n in t=this.pending)(r=t[n]).onerror&&r.onerror(e);for(n in i=[],s=this.subscribers)(r=s[n]).onerror&&i.push(r.onerror(e));return i})}subscribe(e){return this.ready().then(()=>{for(e.tunnel=this,e.id=Math.floor(1e5*Math.random())+1;this.subscribers[e.id]||this.pending[e.id];)e.id=Math.floor(1e5*Math.random())+1;return this.pending[e.id]=e,this.send(e.genmsg(t.SUBSCRIBE,(new TextEncoder).encode(e.channel)))}).catch((function(n){if(e.onerror)return e.onerror(n.toString())}))}unsubscribe(e,n){return this.ready().then(()=>{if(this.subscribers[e.id])return n||this.send(e.genmsg(t.UNSUBSCRIBE,void 0)),e.onclose&&e.onclose(),delete this.subscribers[e.id],e.tunnel=void 0,e.is_opened=!1}).catch((function(n){if(e.onerror)return e.onerror(n.toString())}))}send(e){return this.socket.send(e.as_raw())}close(){if(console.log("Close connection to "+this.uri),this.socket&&this.socket.close(),this.onclose())return this.onclose()}},(i=this).Antunnel={tunnel:void 0,init:function(n){return new Promise((function(t,s){return i.Antunnel.tunnel?t(i.Antunnel.tunnel):(i.Antunnel.tunnel=new e(n),i.Antunnel.tunnel.onclose=function(){return i.Antunnel.tunnel=void 0},i.Antunnel.tunnel.ready().then((function(){return t(i.Antunnel.tunnel)})).catch((function(e){return s(e)})))}))},Subscriber:s,Msg:t},n=class extends OS.application.BaseService{constructor(e){super("AntunnelService",e),this.text=__("Tunnel"),this.iconclass="fa fa-close",this.is_connect=!1,this.nodes=[{text:__("Connect"),id:1},{text:__("Disconnect"),id:2},{text:__("Enter uri"),id:3},{text:__("Exit"),id:4}],this.onchildselect=e=>this.action(e)}init(){return this.systemsetting.system.tunnel_uri&&this.start(),this.watch(1500,()=>{var e;if(e=!1,void 0!==Antunnel.tunnel&&(e=!0),e!==this.is_connect)return this.is_connect=e,this.iconclass="fa fa-circle",this.is_connect||(this.iconclass="fa fa-close"),this.update()})}action(e){var n;switch(n=()=>this._gui.openDialog("PromptDialog",{title:__("Tunnel uri"),label:__("Please enter tunnel uri"),value:"wss://localhost/tunnel"}).then(e=>{if(e&&""!==e)return this.systemsetting.system.tunnel_uri=e,this.start()}),e.data.item.data.id){case 1:if(this.is_connect)return;return this.systemsetting.system.tunnel_uri?this.start():n();case 2:if(Antunnel.tunnel)return Antunnel.tunnel.close();break;case 3:return Antunnel.tunnel&&Antunnel.tunnel.close(),n();case 4:return Antunnel.tunnel&&Antunnel.tunnel.close(),this.quit()}}start(){if(this.systemsetting.system.tunnel_uri&&!Antunnel.tunnel)return Antunnel.init(this.systemsetting.system.tunnel_uri).then(e=>this.notify(__("Tunnel now connected to the server at: {0}",this.systemsetting.system.tunnel_uri))).catch(e=>(Antunnel.tunnel&&Antunnel.tunnel.close(),this.error(__("Unable to connect to the tunnel: {0}",e.toString()),e)))}awake(){}},this.OS.register("AntunnelService",n)}).call(this); \ No newline at end of file +(function() { + var AntunnelApi, AntunnelService, Msg, Subscriber, W; + + Msg = class Msg { + constructor() { + this.header = { + sid: 0, + cid: 0, + type: 0, + size: 0 + }; + this.data = void 0; + } + + as_raw() { + var arr, bytes, length; + length = 21 + this.header.size; + arr = new Uint8Array(length); + arr.set(Msg.MAGIC_START, 0); + arr[4] = this.header.type; + bytes = Msg.bytes_of(this.header.cid); + arr.set(bytes, 5); + bytes = Msg.bytes_of(this.header.sid); + arr.set(bytes, 9); + bytes = Msg.bytes_of(this.header.size); + arr.set(bytes, 13); + if (this.data) { + arr.set(this.data, 17); + } + arr.set(Msg.MAGIC_END, this.header.size + 17); + return arr.buffer; + } + + }; + + Msg.decode = function(raw) { + return new Promise(function(resolve, reject) { + var msg; + msg = new Msg(); + if (Msg.int_from(Msg.MAGIC_START, 0) !== Msg.int_from(raw, 0)) { + return reject("Unmatch message begin magic number"); + } + msg.header.type = raw[4]; + msg.header.cid = Msg.int_from(raw, 5); + msg.header.sid = Msg.int_from(raw, 9); + msg.header.size = Msg.int_from(raw, 13); + msg.data = raw.slice(17, 17 + msg.header.size); + if (Msg.int_from(Msg.MAGIC_END, 0) !== Msg.int_from(raw, 17 + msg.header.size)) { + return reject("Unmatch message end magic number"); + } + return resolve(msg); + }); + }; + + Msg.bytes_of = function(x) { + var bytes; + bytes = new Uint8Array(4); + bytes[0] = x & 255; + x = x >> 8; + bytes[1] = x & 255; + x = x >> 8; + bytes[2] = x & 255; + x = x >> 8; + bytes[3] = x & 255; + return bytes; + }; + + Msg.int_from = function(bytes, offset) { + return bytes[offset] | (bytes[offset + 1] << 8) | (bytes[offset + 2] << 16) | (bytes[offset + 3] << 24); + }; + + Msg.OK = 0; + + Msg.ERROR = 1; + + Msg.DATA = 6; + + Msg.CLOSE = 5; + + Msg.SUBSCRIBE = 2; + + Msg.UNSUBSCRIBE = 3; + + Msg.CTRL = 7; + + Msg.MAGIC_END = [0x41, 0x4e, 0x54, 0x44]; + + Msg.MAGIC_START = [0x44, 0x54, 0x4e, 0x41]; + + Subscriber = class Subscriber { + constructor(channel) { + this.channel = channel; + this.id = void 0; + this.channel_id = void 0; + this.onmessage = void 0; + this.onerror = void 0; + this.onopen = void 0; + this.onclose = void 0; + this.tunnel = void 0; + this.is_opened = false; + } + + send(type, arr) { + if (!this.tunnel) { + if (this.onerror) { + this.onerror("Tunnel is not opened"); + } + return; + } + if (!this.is_opened) { + if (this.onerror) { + this.onerror("Channel is not opened yet"); + } + return; + } + return this.tunnel.send(this.genmsg(type, arr)); + } + + genmsg(type, data) { + var msg; + msg = new Msg(); + msg.header.sid = this.id; + msg.header.cid = this.channel_id; + msg.header.type = type; + msg.header.size = data ? data.length : 0; + msg.data = data; + return msg; + } + + close(b) { + this.is_opened = false; + if (!this.tunnel) { + return; + } + return this.tunnel.unsubscribe(this, b); + } + + }; + + AntunnelApi = class AntunnelApi { + constructor(uri1) { + this.uri = uri1; + this.socket = void 0; + this.pending = {}; + this.subscribers = {}; + this.onclose = void 0; + } + + ready() { + return new Promise((resolve, reject) => { + if (!this.uri) { + return reject(); + } + if (this.socket !== void 0) { + return resolve(); + } + // connect to the socket + console.log(`Connect to ${this.uri}`); + this.socket = new WebSocket(this.uri); + this.socket.binaryType = 'arraybuffer'; + this.socket.onmessage = (evt) => { + return this.process(evt); + }; + this.socket.onclose = (evt) => { + var k, ref, ref1, v; + this.socket = void 0; + ref = this.pending; + for (k in ref) { + v = ref[k]; + v.tunnel = void 0; + if (v.onclose) { + v.onclose(); + } + } + ref1 = this.subscribers; + for (k in ref1) { + v = ref1[k]; + v.tunnel = void 0; + v.is_opened = false; + if (v.onclose) { + v.onclose(); + } + } + this.pending = {}; + this.subscribe = {}; + if (this.onclose()) { + return this.onclose(); + } + }; + this.socket.onerror = (evt) => { + var k, ref, ref1, results, v; + ref = this.pending; + for (k in ref) { + v = ref[k]; + if (v.onerror) { + v.onerror(evt.toString()); + } + } + ref1 = this.subscribers; + results = []; + for (k in ref1) { + v = ref1[k]; + if (v.onerror) { + results.push(v.onerror(evt.toString())); + } + } + return results; + }; + return this.socket.onopen = (e) => { + return resolve(); + }; + }); + } + + process(evt) { + return Msg.decode(new Uint8Array(evt.data)).then((msg) => { + var relay_msg, sub; + // find the correct subscriber of the data + relay_msg = (m, a) => { + var sub; + sub = this.pending[m.header.sid]; + if (sub) { + if (sub[a]) { + sub[a](m); + } + return; + } + sub = this.subscribers[m.header.sid]; + if (sub) { + if (sub[a]) { + return sub[a](m); + } + } + }; + switch (msg.header.type) { + case Msg.OK: + // first look for the pending + sub = this.pending[msg.header.sid]; + if (sub) { + delete this.pending[msg.header.sid]; + sub.id = Msg.int_from(msg.data, 0); + sub.channel_id = msg.header.cid; + this.subscribers[sub.id] = sub; + sub.is_opened = true; + if (sub.onopen) { + return sub.onopen(); + } + } else { + return relay_msg(msg, "onmessage"); + } + break; + case Msg.DATA: + return relay_msg(msg, "onmessage"); + case Msg.ERROR: + return relay_msg(msg, "onerror"); + case Msg.UNSUBSCRIBE: + sub = this.subscribers[msg.header.sid]; + if (!sub) { + return; + } + return sub.close(true); + default: + return console.error(`Message of type ${msg.header.type} is unsupported`, msg); + } + }).catch((e) => { + var k, ref, ref1, results, v; + ref = this.pending; + for (k in ref) { + v = ref[k]; + if (v.onerror) { + v.onerror(e); + } + } + ref1 = this.subscribers; + results = []; + for (k in ref1) { + v = ref1[k]; + if (v.onerror) { + results.push(v.onerror(e)); + } + } + return results; + }); + } + + subscribe(sub) { + return this.ready().then(() => { + // insert it to pending list + sub.tunnel = this; + sub.id = Math.floor(Math.random() * 100000) + 1; + while (this.subscribers[sub.id] || this.pending[sub.id]) { + sub.id = Math.floor(Math.random() * 100000) + 1; + } + this.pending[sub.id] = sub; + // send request to connect to a channel + return this.send(sub.genmsg(Msg.SUBSCRIBE, (new TextEncoder()).encode(sub.channel))); + }).catch(function(e) { + if (sub.onerror) { + return sub.onerror(e.toString()); + } + }); + } + + unsubscribe(sub, b) { + return this.ready().then(() => { + if (!this.subscribers[sub.id]) { + return; + } + if (!b) { + // insert it to pending list + // send request to connect to a channel + this.send(sub.genmsg(Msg.UNSUBSCRIBE, void 0)); + } + if (sub.onclose) { + sub.onclose(); + } + delete this.subscribers[sub.id]; + sub.tunnel = void 0; + return sub.is_opened = false; + }).catch(function(e) { + if (sub.onerror) { + return sub.onerror(e.toString()); + } + }); + } + + send(msg) { + // return unless @subscribers[msg.header.sid] + return this.socket.send(msg.as_raw()); + } + + close() { + console.log(`Close connection to ${this.uri}`); + if (this.socket) { + this.socket.close(); + } + if (this.onclose()) { + return this.onclose(); + } + } + + }; + + W = this; + + W.Antunnel = { + tunnel: void 0, + init: (function(url) { + return new Promise(function(resolve, reject) { + if (W.Antunnel.tunnel) { + return resolve(W.Antunnel.tunnel); + } + W.Antunnel.tunnel = new AntunnelApi(url); + W.Antunnel.tunnel.onclose = function() { + return W.Antunnel.tunnel = void 0; + }; + return W.Antunnel.tunnel.ready().then(function() { + return resolve(W.Antunnel.tunnel); + }).catch(function(e) { + return reject(e); + }); + }); + }), + Subscriber: Subscriber, + Msg: Msg + }; + + AntunnelService = class AntunnelService extends OS.application.BaseService { + constructor(args) { + super("AntunnelService", args); + this.text = __("Tunnel"); + this.iconclass = "fa fa-close"; + this.is_connect = false; + this.nodes = [ + { + text: __("Connect"), + id: 1 + }, + { + text: __("Disconnect"), + id: 2 + }, + { + text: __("Enter uri"), + id: 3 + }, + { + text: __("Exit"), + id: 4 + } + ]; + this.onchildselect = (e) => { + return this.action(e); + }; + } + + init() { + if (this.systemsetting.system.tunnel_uri) { + this.start(); + } + this.watch(1500, () => { + var new_status; + new_status = false; + if (Antunnel.tunnel !== void 0) { + new_status = true; + } + if (new_status === this.is_connect) { + return; + } + this.is_connect = new_status; + this.iconclass = "fa fa-circle"; + if (!this.is_connect) { + this.iconclass = "fa fa-close"; + } + return this.update(); + }); + return OS.onexit("cleanupAntunnel", () => { + if (Antunnel.tunnel) { + Antunnel.tunnel.close(); + } + return this.quit(); + }); + } + + action(e) { + var ask; + ask = () => { + return this._gui.openDialog("PromptDialog", { + title: __("Tunnel uri"), + label: __("Please enter tunnel uri"), + value: "wss://localhost/tunnel" + }).then((uri) => { + if (!(uri && uri !== "")) { + return; + } + this.systemsetting.system.tunnel_uri = uri; + return this.start(); + }); + }; + switch (e.data.item.data.id) { + case 1: + if (this.is_connect) { + return; + } + if (this.systemsetting.system.tunnel_uri) { + return this.start(); + } else { + return ask(); + } + break; + case 2: + if (Antunnel.tunnel) { + return Antunnel.tunnel.close(); + } + break; + case 3: + if (Antunnel.tunnel) { + Antunnel.tunnel.close(); + } + return ask(); + case 4: + if (Antunnel.tunnel) { + Antunnel.tunnel.close(); + } + return this.quit(); + } + } + + start() { + if (!this.systemsetting.system.tunnel_uri) { + return; + } + if (Antunnel.tunnel) { + return; + } + return Antunnel.init(this.systemsetting.system.tunnel_uri).then((t) => { + return this.notify(__("Tunnel now connected to the server at: {0}", this.systemsetting.system.tunnel_uri)); + }).catch((e) => { + if (Antunnel.tunnel) { + Antunnel.tunnel.close(); + } + return this.error(__("Unable to connect to the tunnel: {0}", e.toString()), e); + }); + } + + awake() {} + + }; + + this.OS.register("AntunnelService", AntunnelService); + +}).call(this); diff --git a/Antunnel/build/release/Antunnel.zip b/Antunnel/build/release/Antunnel.zip index 5cabf04..c11af26 100644 Binary files a/Antunnel/build/release/Antunnel.zip and b/Antunnel/build/release/Antunnel.zip differ diff --git a/Antunnel/coffees/AntunnelService.coffee b/Antunnel/coffees/AntunnelService.coffee index e5b0185..d826782 100644 --- a/Antunnel/coffees/AntunnelService.coffee +++ b/Antunnel/coffees/AntunnelService.coffee @@ -23,6 +23,9 @@ class AntunnelService extends OS.application.BaseService @iconclass = "fa fa-circle" @iconclass = "fa fa-close" unless @is_connect @update() + OS.onexit "cleanupAntunnel", () => + Antunnel.tunnel.close() if Antunnel.tunnel + @quit() action: (e) ->