Skip to content

Commit

Permalink
Allow raw WebSocket to be passed into WebSocketProvider (ethers-io#2562
Browse files Browse the repository at this point in the history
  • Loading branch information
ricmoo authored and Woodpile37 committed Jan 14, 2024
1 parent 67e146f commit 481cc52
Showing 1 changed file with 36 additions and 11 deletions.
47 changes: 36 additions & 11 deletions packages/providers/src.ts/websocket-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,16 @@ export type Subscription = {
processFunc: (payload: any) => void;
};

export interface WebSocketLike {
onopen: (...args: Array<any>) => any;
onmessage: (...args: Array<any>) => any;
onerror: (...args: Array<any>) => any;

readyState: number;

send(payload: any): void;
close(code?: number, reason?: string): void;
}

// For more info about the Real-time Event API see:
// https://geth.ethereum.org/docs/rpc/pubsub
Expand All @@ -56,34 +66,45 @@ export class WebSocketProvider extends JsonRpcProvider {

_wsReady: boolean;

constructor(url: string, network?: Networkish) {
constructor(url: string | WebSocketLike, network?: Networkish) {

// This will be added in the future; please open an issue to expedite
if (network === "any") {
logger.throwError("WebSocketProvider does not support 'any' network yet", Logger.errors.UNSUPPORTED_OPERATION, {
operation: "network:any"
});
}

super(url, network);
if (typeof(url) === "string") {
super(url, network);
} else {
super("_websocket", network);
}

this._pollingInterval = -1;

this._wsReady = false;

defineReadOnly(this, "_websocket", new WebSocket(this.connection.url));
if (typeof(url) === "string") {
defineReadOnly(this, "_websocket", new WebSocket(this.connection.url));
} else {
defineReadOnly(this, "_websocket", url);
}

defineReadOnly(this, "_requests", { });
defineReadOnly(this, "_subs", { });
defineReadOnly(this, "_subIds", { });
defineReadOnly(this, "_detectNetwork", super.detectNetwork());

// Stall sending requests until the socket is open...
this._websocket.onopen = () => {
this.websocket.onopen = () => {
this._wsReady = true;
Object.keys(this._requests).forEach((id) => {
this._websocket.send(this._requests[id].payload);
this.websocket.send(this._requests[id].payload);
});
};

this._websocket.onmessage = (messageEvent: { data: string }) => {
this.websocket.onmessage = (messageEvent: { data: string }) => {
const data = messageEvent.data;
const result = JSON.parse(data);
if (result.id != null) {
Expand Down Expand Up @@ -144,6 +165,10 @@ export class WebSocketProvider extends JsonRpcProvider {
if (fauxPoll.unref) { fauxPoll.unref(); }
}

// Cannot narrow the type of _websocket, as that is not backwards compatible
// so we add a getter and let the WebSocket be a public API.
get websocket(): WebSocketLike { return this._websocket; }

detectNetwork(): Promise<Network> {
return this._detectNetwork;
}
Expand Down Expand Up @@ -200,7 +225,7 @@ export class WebSocketProvider extends JsonRpcProvider {

this._requests[String(rid)] = { callback, payload };

if (this._wsReady) { this._websocket.send(payload); }
if (this._wsReady) { this.websocket.send(payload); }
});
}

Expand Down Expand Up @@ -306,20 +331,20 @@ export class WebSocketProvider extends JsonRpcProvider {

async destroy(): Promise<void> {
// Wait until we have connected before trying to disconnect
if (this._websocket.readyState === WebSocket.CONNECTING) {
if (this.websocket.readyState === WebSocket.CONNECTING) {
await (new Promise((resolve) => {
this._websocket.onopen = function() {
this.websocket.onopen = function() {
resolve(true);
};

this._websocket.onerror = function() {
this.websocket.onerror = function() {
resolve(false);
};
}));
}

// Hangup
// See: https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Status_codes
this._websocket.close(1000);
this.websocket.close(1000);
}
}

0 comments on commit 481cc52

Please sign in to comment.