provider-websocket.ts 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. import { WebSocket as _WebSocket } from "./ws.js"; /*-browser*/
  2. import { SocketProvider } from "./provider-socket.js";
  3. import type { JsonRpcApiProviderOptions} from "./provider-jsonrpc.js";
  4. import type { Networkish } from "./network.js";
  5. /**
  6. * A generic interface to a Websocket-like object.
  7. */
  8. export interface WebSocketLike {
  9. onopen: null | ((...args: Array<any>) => any);
  10. onmessage: null | ((...args: Array<any>) => any);
  11. onerror: null | ((...args: Array<any>) => any);
  12. readyState: number;
  13. send(payload: any): void;
  14. close(code?: number, reason?: string): void;
  15. }
  16. /**
  17. * A function which can be used to re-create a WebSocket connection
  18. * on disconnect.
  19. */
  20. export type WebSocketCreator = () => WebSocketLike;
  21. /**
  22. * A JSON-RPC provider which is backed by a WebSocket.
  23. *
  24. * WebSockets are often preferred because they retain a live connection
  25. * to a server, which permits more instant access to events.
  26. *
  27. * However, this incurs higher server infrasturture costs, so additional
  28. * resources may be required to host your own WebSocket nodes and many
  29. * third-party services charge additional fees for WebSocket endpoints.
  30. */
  31. export class WebSocketProvider extends SocketProvider {
  32. #connect: null | WebSocketCreator;
  33. #websocket: null | WebSocketLike;
  34. get websocket(): WebSocketLike {
  35. if (this.#websocket == null) { throw new Error("websocket closed"); }
  36. return this.#websocket;
  37. }
  38. constructor(url: string | WebSocketLike | WebSocketCreator, network?: Networkish, options?: JsonRpcApiProviderOptions) {
  39. super(network, options);
  40. if (typeof(url) === "string") {
  41. this.#connect = () => { return new _WebSocket(url); };
  42. this.#websocket = this.#connect();
  43. } else if (typeof(url) === "function") {
  44. this.#connect = url;
  45. this.#websocket = url();
  46. } else {
  47. this.#connect = null;
  48. this.#websocket = url;
  49. }
  50. this.websocket.onopen = async () => {
  51. try {
  52. await this._start()
  53. this.resume();
  54. } catch (error) {
  55. console.log("failed to start WebsocketProvider", error);
  56. // @TODO: now what? Attempt reconnect?
  57. }
  58. };
  59. this.websocket.onmessage = (message: { data: string }) => {
  60. this._processMessage(message.data);
  61. };
  62. /*
  63. this.websocket.onclose = (event) => {
  64. // @TODO: What event.code should we reconnect on?
  65. const reconnect = false;
  66. if (reconnect) {
  67. this.pause(true);
  68. if (this.#connect) {
  69. this.#websocket = this.#connect();
  70. this.#websocket.onopen = ...
  71. // @TODO: this requires the super class to rebroadcast; move it there
  72. }
  73. this._reconnect();
  74. }
  75. };
  76. */
  77. }
  78. async _write(message: string): Promise<void> {
  79. this.websocket.send(message);
  80. }
  81. async destroy(): Promise<void> {
  82. if (this.#websocket != null) {
  83. this.#websocket.close();
  84. this.#websocket = null;
  85. }
  86. super.destroy();
  87. }
  88. }