Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replaced ping setInterval integration with webworker #579

Open
wants to merge 4 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
IStompSocket,
messageCallbackType,
StompSocketState,
TickerStrategy,
wsErrorCallbackType,
} from './types.js';
import { Versions } from './versions.js';
Expand Down Expand Up @@ -100,6 +101,16 @@ export class Client {
*/
public heartbeatOutgoing: number = 10000;

/**
* Outgoing heartbeat strategy.
* Can be worker or interval strategy, but will always use interval if the client is used in a non-browser environment.
*
* Interval strategy can be helpful if you discover disconnects after moving the browser in the background while the client is connected.
*
* Defaults to interval strategy.
*/
public heartbeatStrategy: TickerStrategy = TickerStrategy.Interval;

/**
* This switches on a non-standard behavior while sending WebSocket packets.
* It splits larger (text) packets into chunks of [maxWebSocketChunkSize]{@link Client#maxWebSocketChunkSize}.
Expand Down Expand Up @@ -453,6 +464,7 @@ export class Client {
disconnectHeaders: this._disconnectHeaders,
heartbeatIncoming: this.heartbeatIncoming,
heartbeatOutgoing: this.heartbeatOutgoing,
heartbeatStrategy: this.heartbeatStrategy,
splitLargeFrames: this.splitLargeFrames,
maxWebSocketChunkSize: this.maxWebSocketChunkSize,
forceBinaryWSFrames: this.forceBinaryWSFrames,
Expand Down
6 changes: 6 additions & 0 deletions src/stomp-config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { StompHeaders } from './stomp-headers.js';
import {
ActivationState,
TickerStrategy,
closeEventCallbackType,
debugFnType,
frameCallbackType,
Expand Down Expand Up @@ -52,6 +53,11 @@ export class StompConfig {
*/
public heartbeatOutgoing?: number;

/**
* See [Client#heartbeatStrategy]{@link Client#heartbeatStrategy}.
*/
public heartbeatStrategy?: TickerStrategy;

/**
* See [Client#splitLargeFrames]{@link Client#splitLargeFrames}.
*/
Expand Down
13 changes: 8 additions & 5 deletions src/stomp-handler.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { augmentWebsocket } from './augment-websocket.js';
import { BYTE } from './byte.js';
import { Client } from './client.js';
import { FrameImpl } from './frame-impl.js';
Expand All @@ -6,6 +7,7 @@ import { ITransaction } from './i-transaction.js';
import { Parser } from './parser.js';
import { StompHeaders } from './stomp-headers.js';
import { StompSubscription } from './stomp-subscription.js';
import { Ticker } from './ticker.js';
import {
closeEventCallbackType,
debugFnType,
Expand All @@ -19,7 +21,6 @@ import {
wsErrorCallbackType,
} from './types.js';
import { Versions } from './versions.js';
import { augmentWebsocket } from './augment-websocket.js';

/**
* The STOMP protocol handler
Expand Down Expand Up @@ -85,7 +86,7 @@ export class StompHandler {
private _partialData: string;
private _escapeHeaderValues: boolean;
private _counter: number;
private _pinger: any;
private _pinger?: Ticker;
private _ponger: any;
private _lastServerActivityTS: number;

Expand Down Expand Up @@ -289,12 +290,14 @@ export class StompHandler {
if (this.heartbeatOutgoing !== 0 && serverIncoming !== 0) {
const ttl: number = Math.max(this.heartbeatOutgoing, serverIncoming);
this.debug(`send PING every ${ttl}ms`);
this._pinger = setInterval(() => {

this._pinger = new Ticker(ttl, this._client.heartbeatStrategy);
this._pinger.start(() => {
if (this._webSocket.readyState === StompSocketState.OPEN) {
this._webSocket.send(BYTE.LF);
this.debug('>>> PING');
}
}, ttl);
});
}

if (this.heartbeatIncoming !== 0 && serverOutgoing !== 0) {
Expand Down Expand Up @@ -426,7 +429,7 @@ export class StompHandler {
this._connected = false;

if (this._pinger) {
clearInterval(this._pinger);
this._pinger.stop();
this._pinger = undefined;
}
if (this._ponger) {
Expand Down
71 changes: 71 additions & 0 deletions src/ticker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { TickerStrategy } from './types.js';

export class Ticker {
private readonly _workerScript = `
var startTime = Date.now();
setInterval(function() {
self.postMessage(Date.now() - startTime);
}, ${this._interval});
`;

private _worker?: Worker;
private _timer?: NodeJS.Timer;

constructor(
private readonly _interval: number,
private readonly _strategy = TickerStrategy.Interval) {
}

public start(tick: (elapsedTime: number) => void): void {
this.stop();

if (this.shouldUseWorker()) {
this.runWorker(tick);
} else {
this.runInterval(tick);
}
}

public stop(): void {
this.disposeWorker();
this.disposeInterval();
}

private shouldUseWorker(): boolean {
return typeof(Worker) !== 'undefined' && this._strategy === TickerStrategy.Worker
}

private runWorker(tick: (elapsedTime: number) => void): void {
if (!this._worker) {
this._worker = new Worker(
URL.createObjectURL(
new Blob([this._workerScript], { type: 'text/javascript' })
)
);
this._worker.onmessage = (message) => tick(message.data);
}
}

private runInterval(tick: (elapsedTime: number) => void): void {
if (!this._timer) {
const startTime = Date.now();
this._timer = setInterval(() => {
tick(Date.now() - startTime);
}, this._interval);
}
}

private disposeWorker(): void {
if (this._worker) {
this._worker.terminate();
delete this._worker;
}
}

private disposeInterval(): void {
if (this._timer) {
clearInterval(this._timer);
delete this._timer;
}
}
}
9 changes: 9 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,14 @@ export enum ActivationState {
INACTIVE,
}

/**
* Possible ticker strategies
*/
export enum TickerStrategy {
Interval = 'interval',
Worker = 'worker'
}

/**
* @internal
*/
Expand All @@ -167,6 +175,7 @@ export interface IStomptHandlerConfig {
disconnectHeaders: StompHeaders;
heartbeatIncoming: number;
heartbeatOutgoing: number;
heartbeatStrategy: TickerStrategy;
splitLargeFrames: boolean;
maxWebSocketChunkSize: number;
forceBinaryWSFrames: boolean;
Expand Down