websock.ts 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. import * as message from "./message.js";
  2. import * as rendezvous from "./rendezvous.js";
  3. type Keys = "message" | "open" | "close" | "error";
  4. export default class Websock {
  5. _websocket: WebSocket;
  6. _eventHandlers: { [key in Keys]: Function };
  7. _buf: (rendezvous.RendezvousMessage | message.Message)[];
  8. _status: any;
  9. _latency: number;
  10. _secretKey: [Uint8Array, number, number] | undefined;
  11. _uri: string;
  12. _isRendezvous: boolean;
  13. constructor(uri: string, isRendezvous: boolean = true) {
  14. this._eventHandlers = {
  15. message: (_: any) => {},
  16. open: () => {},
  17. close: () => {},
  18. error: () => {},
  19. };
  20. this._uri = uri;
  21. this._status = "";
  22. this._buf = [];
  23. this._websocket = new WebSocket(uri);
  24. this._websocket.onmessage = this._recv_message.bind(this);
  25. this._websocket.binaryType = "arraybuffer";
  26. this._latency = new Date().getTime();
  27. this._isRendezvous = isRendezvous;
  28. }
  29. latency(): number {
  30. return this._latency;
  31. }
  32. setSecretKey(key: Uint8Array) {
  33. this._secretKey = [key, 0, 0];
  34. }
  35. sendMessage(json: message.DeepPartial<message.Message>) {
  36. let data = message.Message.encode(
  37. message.Message.fromPartial(json)
  38. ).finish();
  39. // let k = this._secretKey;
  40. // if (k) {
  41. // k[1] += 1;
  42. // data = globals.encrypt(data, k[1], k[0]);
  43. // }
  44. this._websocket.send(data);
  45. }
  46. sendRendezvous(data: rendezvous.DeepPartial<rendezvous.RendezvousMessage>) {
  47. this._websocket.send(
  48. rendezvous.RendezvousMessage.encode(
  49. rendezvous.RendezvousMessage.fromPartial(data)
  50. ).finish()
  51. );
  52. }
  53. parseMessage(data: Uint8Array) {
  54. return message.Message.decode(data);
  55. }
  56. parseRendezvous(data: Uint8Array) {
  57. return rendezvous.RendezvousMessage.decode(data);
  58. }
  59. // Event Handlers
  60. off(evt: Keys) {
  61. this._eventHandlers[evt] = () => {};
  62. }
  63. on(evt: Keys, handler: Function) {
  64. this._eventHandlers[evt] = handler;
  65. }
  66. async open(timeout: number = 12000): Promise<Websock> {
  67. return new Promise((resolve, reject) => {
  68. setTimeout(() => {
  69. if (this._status != "open") {
  70. reject(this._status || "Timeout");
  71. }
  72. }, timeout);
  73. this._websocket.onopen = () => {
  74. this._latency = new Date().getTime() - this._latency;
  75. this._status = "open";
  76. console.debug(">> WebSock.onopen");
  77. if (this._websocket?.protocol) {
  78. console.info(
  79. "Server choose sub-protocol: " + this._websocket.protocol
  80. );
  81. }
  82. this._eventHandlers.open();
  83. console.info("WebSock.onopen");
  84. resolve(this);
  85. };
  86. this._websocket.onclose = (e) => {
  87. if (this._status == "open") {
  88. // e.code 1000 means that the connection was closed normally.
  89. //
  90. }
  91. this._status = e;
  92. console.error("WebSock.onclose: ");
  93. console.error(e);
  94. this._eventHandlers.close(e);
  95. reject("Reset by the peer");
  96. };
  97. this._websocket.onerror = (e: any) => {
  98. if (!this._status) {
  99. reject("Failed to connect to " + (this._isRendezvous ? "rendezvous" : "relay") + " server");
  100. return;
  101. }
  102. this._status = e;
  103. console.error("WebSock.onerror: ")
  104. console.error(e);
  105. this._eventHandlers.error(e);
  106. };
  107. });
  108. }
  109. async next(
  110. timeout = 12000
  111. ): Promise<rendezvous.RendezvousMessage | message.Message> {
  112. const func = (
  113. resolve: (value: rendezvous.RendezvousMessage | message.Message) => void,
  114. reject: (reason: any) => void,
  115. tm0: number
  116. ) => {
  117. // console.log('next')
  118. if (this._buf.length) {
  119. resolve(this._buf[0]);
  120. this._buf.splice(0, 1);
  121. } else {
  122. if (this._status != "open") {
  123. reject(this._status);
  124. return;
  125. }
  126. if (new Date().getTime() > tm0 + timeout) {
  127. reject("Timeout");
  128. } else {
  129. setTimeout(() => func(resolve, reject, tm0), 1);
  130. }
  131. }
  132. };
  133. return new Promise((resolve, reject) => {
  134. func(resolve, reject, new Date().getTime());
  135. });
  136. }
  137. close() {
  138. this._status = "";
  139. if (this._websocket) {
  140. if (
  141. this._websocket.readyState === WebSocket.OPEN ||
  142. this._websocket.readyState === WebSocket.CONNECTING
  143. ) {
  144. console.info("Closing WebSocket connection");
  145. this._websocket.close();
  146. }
  147. this._websocket.onmessage = () => {};
  148. }
  149. }
  150. _recv_message(e: any) {
  151. if (e.data instanceof window.ArrayBuffer) {
  152. let bytes = new Uint8Array(e.data);
  153. // const k = this._secretKey;
  154. // if (k) {
  155. // k[2] += 1;
  156. // bytes = globals.decrypt(bytes, k[2], k[0]);
  157. // }
  158. this._buf.push(
  159. this._isRendezvous
  160. ? this.parseRendezvous(bytes)
  161. : this.parseMessage(bytes)
  162. );
  163. }
  164. this._eventHandlers.message(e.data);
  165. }
  166. }