Skip to content

Commit

Permalink
feat: Implement ping/pong support
Browse files Browse the repository at this point in the history
  • Loading branch information
gnarea committed Jul 26, 2021
1 parent 34128a0 commit 8886e93
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 3 deletions.
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export { MockClient } from './lib/MockClient';
export { MockPeer } from './lib/MockPeer';
export { MockServer } from './lib/MockServer';
export { PingOrPong } from './lib/PingOrPong';
export * from './lib/MockServerAction';
export { CloseFrame } from './lib/CloseFrame';
export { createMockWebSocketStream } from './lib/stream';
37 changes: 37 additions & 0 deletions src/lib/MockPeer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Data as WSData } from 'ws';

import { CloseFrame } from './CloseFrame';
import { MockWebSocket } from './MockWebSocket';
import { PingOrPong } from './PingOrPong';

export abstract class MockPeer {
protected readonly peerWebSocket = new MockWebSocket();
Expand Down Expand Up @@ -55,6 +56,42 @@ export abstract class MockPeer {
return this.peerWebSocket.closeFrame;
}

/**
* Send ping to peer and return ping data.
*
* @param data
*/
public ping(data?: Buffer): Buffer {
const finalData = data ?? Buffer.from(Math.random().toString());
this.peerWebSocket.emit('ping', finalData);
return finalData;
}

/**
* Send pong to peer.
*
* @param data
*/
public pong(data: Buffer): void {
this.peerWebSocket.emit('pong', data);
}

/**
* Return pings sent by peer.
*/
get incomingPings(): readonly PingOrPong[] {
// Return a shallow copy to avoid race conditions if more pings are received
return [...this.peerWebSocket.outgoingPings];
}

/**
* Return pongs sent by peer.
*/
get incomingPongs(): readonly PingOrPong[] {
// Return a shallow copy to avoid race conditions if more pings are received
return [...this.peerWebSocket.outgoingPongs];
}

/**
* Mimic the conversion that `ws` would do on binary frames.
*
Expand Down
36 changes: 33 additions & 3 deletions src/lib/MockWebSocket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,48 @@ import { Duplex } from 'stream';
import { Data as WSData } from 'ws';

import { CloseFrame } from './CloseFrame';
import { PingOrPong } from './PingOrPong';

export class MockWebSocket extends EventEmitter {
// tslint:disable-next-line:readonly-keyword
public binaryType: 'nodebuffer' | 'arraybuffer' = 'nodebuffer';

// tslint:disable-next-line:readonly-array
public readonly outgoingPings: PingOrPong[] = [];
// tslint:disable-next-line:readonly-array
public readonly outgoingPongs: PingOrPong[] = [];

// tslint:disable-next-line:readonly-keyword
protected ownCloseFrame: CloseFrame | null = null;
// tslint:disable-next-line:readonly-array
protected readonly messagesSent: WSData[] = [];
protected readonly ownEvents = new EventEmitter();

public send(data: WSData): void {
this.requireOpenConnection();

this.messagesSent.push(data);
this.ownEvents.emit('messageSent', data);
}

public ping(data?: WSData, mask?: boolean, cb?: (err?: Error) => void): void {
this.requireOpenConnection();

this.outgoingPings.push({ data, cb, mask, date: new Date() });
if (cb) {
cb();
}
}

public pong(data?: WSData, mask?: boolean, cb?: (err?: Error) => void): void {
this.requireOpenConnection();

this.outgoingPongs.push({ data, cb, mask, date: new Date() });
if (cb) {
cb();
}
}

/**
* @internal
*/
Expand All @@ -37,9 +63,7 @@ export class MockWebSocket extends EventEmitter {
}

public close(code?: number, reason?: string): void {
if (this.ownCloseFrame) {
throw new Error('Connection closure was already initiated');
}
this.requireOpenConnection();

// tslint:disable-next-line:no-object-mutation
this.ownCloseFrame = { code, reason };
Expand Down Expand Up @@ -89,6 +113,12 @@ export class MockWebSocket extends EventEmitter {

return duplex;
}

private requireOpenConnection(): void {
if (this.ownCloseFrame) {
throw new Error('Connection is not open (anymore)');
}
}
}

async function waitForEvent<T>(eventName: string, eventEmitter: EventEmitter): Promise<T> {
Expand Down
8 changes: 8 additions & 0 deletions src/lib/PingOrPong.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Data as WSData } from 'ws';

export interface PingOrPong {
readonly data?: WSData;
readonly mask?: boolean;
readonly cb?: (err?: Error) => void;
readonly date: Date;
}

0 comments on commit 8886e93

Please sign in to comment.