session.ts 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. export interface Command {
  2. command: string
  3. task_id: string
  4. spec: any
  5. }
  6. export interface ClientEvent {
  7. event: string,
  8. task_id: string,
  9. data: any
  10. }
  11. /*
  12. * 会话
  13. * 向外暴露的事件:on_session_create、on_session_close、on_server_message
  14. * 提供的函数:start_session、send_message、close_session
  15. * */
  16. export interface Session {
  17. on_session_create(callback: () => void): void;
  18. on_session_close(callback: () => void): void;
  19. on_server_message(callback: (msg: Command) => void): void;
  20. start_session(debug: boolean): void;
  21. send_message(msg: ClientEvent, onprogress?: (loaded: number, total: number) => void): void;
  22. close_session(): void;
  23. closed(): boolean;
  24. }
  25. export class WebSocketSession implements Session {
  26. ws: WebSocket;
  27. debug: boolean;
  28. private _closed: boolean;
  29. private _on_session_create: (this: WebSocket, ev: Event) => any = () => {
  30. };
  31. private _on_session_close: (this: WebSocket, ev: CloseEvent) => any = () => {
  32. };
  33. private _on_server_message: (msg: Command) => any = () => {
  34. };
  35. constructor(public ws_api: string, app_name: string = 'index') {
  36. this.ws = null;
  37. this.debug = false;
  38. this._closed = false;
  39. let url = new URL(ws_api);
  40. if (url.protocol !== 'wss:' && url.protocol !== 'ws:') {
  41. let protocol = url.protocol || window.location.protocol;
  42. url.protocol = protocol.replace('https', 'wss').replace('http', 'ws');
  43. }
  44. url.search = "?app=" + app_name;
  45. this.ws_api = url.href;
  46. }
  47. on_session_create(callback: () => any): void {
  48. this._on_session_create = callback;
  49. };
  50. on_session_close(callback: () => any): void {
  51. this._on_session_close = callback;
  52. }
  53. on_server_message(callback: (msg: Command) => any): void {
  54. this._on_server_message = callback;
  55. }
  56. start_session(debug: boolean = false): void {
  57. this.debug = debug;
  58. this.ws = new WebSocket(this.ws_api);
  59. this.ws.onopen = this._on_session_create;
  60. this.ws.onclose = this._on_session_close;
  61. let that = this;
  62. this.ws.onmessage = function (evt) {
  63. let msg: Command = JSON.parse(evt.data);
  64. if (debug) console.info('>>>', msg);
  65. that._on_server_message(msg);
  66. };
  67. }
  68. start_onprogress(onprogress?: (loaded: number, total: number) => void): void {
  69. let total = this.ws.bufferedAmount;
  70. let onprogressID = setInterval(() => {
  71. let loaded = total - this.ws.bufferedAmount;
  72. onprogress(loaded, total);
  73. if (this.ws.bufferedAmount == 0)
  74. clearInterval(onprogressID);
  75. }, 200);
  76. }
  77. send_message(msg: ClientEvent, onprogress?: (loaded: number, total: number) => void): void {
  78. if (this.closed())
  79. return alert("与服务器连接已断开,请刷新页面重新操作");
  80. if (this.ws === null)
  81. return console.error('WebSocketWebIOSession.ws is null when invoke WebSocketWebIOSession.send_message. ' +
  82. 'Please call WebSocketWebIOSession.start_session first');
  83. this.ws.send(JSON.stringify(msg));
  84. if (onprogress)
  85. this.start_onprogress(onprogress);
  86. if (this.debug) console.info('<<<', msg);
  87. }
  88. close_session(): void {
  89. this._closed = true;
  90. this._on_session_close.call(this.ws, null);
  91. try {
  92. this.ws.close()
  93. } catch (e) {
  94. }
  95. }
  96. closed(): boolean {
  97. return this._closed || this.ws.readyState === WebSocket.CLOSED || this.ws.readyState === WebSocket.CLOSING;
  98. }
  99. }
  100. export class HttpSession implements Session {
  101. interval_pull_id: number = null;
  102. webio_session_id: string;
  103. debug = false;
  104. private _closed = false;
  105. private _on_session_create: () => void = () => {
  106. };
  107. private _on_session_close: () => void = () => {
  108. };
  109. private _on_server_message: (msg: Command) => void = () => {
  110. };
  111. constructor(public api_url: string, app_name = 'index', public pull_interval_ms = 1000) {
  112. let url = new URL(api_url, window.location.href);
  113. url.search = "?app=" + app_name;
  114. this.api_url = url.href;
  115. }
  116. on_session_create(callback: () => void): void {
  117. this._on_session_create = callback;
  118. }
  119. on_session_close(callback: () => void): void {
  120. this._on_session_close = callback;
  121. }
  122. on_server_message(callback: (msg: Command) => void): void {
  123. this._on_server_message = callback;
  124. }
  125. start_session(debug: boolean = false): void {
  126. this.debug = debug;
  127. let that = this;
  128. function pull() {
  129. $.ajax({
  130. type: "GET",
  131. url: that.api_url,
  132. contentType: "application/json; charset=utf-8",
  133. dataType: "json",
  134. headers: {"webio-session-id": that.webio_session_id},
  135. success: function (data: Command[], textStatus: string, jqXHR: JQuery.jqXHR) {
  136. that._on_request_success(data, textStatus, jqXHR);
  137. that._on_session_create();
  138. },
  139. error: function () {
  140. console.error('Http pulling failed');
  141. }
  142. })
  143. }
  144. pull();
  145. this.interval_pull_id = setInterval(pull, this.pull_interval_ms);
  146. }
  147. private _on_request_success(data: Command[], textStatus: string, jqXHR: JQuery.jqXHR) {
  148. let sid = jqXHR.getResponseHeader('webio-session-id');
  149. if (sid) this.webio_session_id = sid;
  150. for (let msg of data) {
  151. if (this.debug) console.info('>>>', msg);
  152. this._on_server_message(msg);
  153. }
  154. };
  155. send_message(msg: ClientEvent, onprogress?: (loaded: number, total: number) => void): void {
  156. if (this.closed())
  157. return alert("与服务器连接已断开,请刷新页面重新操作");
  158. if (this.debug) console.info('<<<', msg);
  159. $.ajax({
  160. type: "POST",
  161. url: this.api_url,
  162. data: JSON.stringify(msg),
  163. contentType: "application/json; charset=utf-8",
  164. dataType: "json",
  165. headers: {"webio-session-id": this.webio_session_id},
  166. success: this._on_request_success.bind(this),
  167. xhr: function () {
  168. let xhr = new window.XMLHttpRequest();
  169. // Upload progress
  170. xhr.upload.addEventListener("progress", function (evt) {
  171. if (evt.lengthComputable && onprogress) {
  172. onprogress(evt.loaded, evt.total);
  173. }
  174. }, false);
  175. return xhr;
  176. },
  177. error: function () { // todo
  178. console.error('Http push event failed, event data: %s', msg);
  179. }
  180. });
  181. }
  182. close_session(): void {
  183. this._closed = true;
  184. this._on_session_close();
  185. clearInterval(this.interval_pull_id);
  186. }
  187. closed(): boolean {
  188. return this._closed;
  189. }
  190. }
  191. /*
  192. * Check given `backend_addr` is a http backend
  193. * Usage:
  194. * // `http_backend` is a boolean to present whether or not a http_backend the given `backend_addr` is
  195. * is_http_backend('http://localhost:8080/io').then(function(http_backend){ });
  196. * */
  197. export function is_http_backend(backend_addr: string) {
  198. let url = new URL(backend_addr);
  199. let protocol = url.protocol || window.location.protocol;
  200. url.protocol = protocol.replace('wss', 'https').replace('ws', 'http');
  201. backend_addr = url.href;
  202. return new Promise(function (resolve, reject) {
  203. $.get(backend_addr, {test: 1}, undefined, 'html').done(function (data: string) {
  204. resolve(data === 'ok');
  205. }).fail(function (e: JQuery.jqXHR) {
  206. resolve(false);
  207. });
  208. });
  209. }