Skip to content

Commit

Permalink
feat(client): Add connectionAckWaitTimeout option to client
Browse files Browse the repository at this point in the history
  • Loading branch information
D34THWINGS committed Sep 7, 2021
1 parent 8a0da71 commit 47b9830
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 0 deletions.
7 changes: 7 additions & 0 deletions docs/enums/common.CloseCode.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
### Enumeration members

- [BadRequest](common.CloseCode.md#badrequest)
- [ConnectionAcknowledgementTimeout](common.CloseCode.md#connectionacknowledgementtimeout)
- [ConnectionInitialisationTimeout](common.CloseCode.md#connectioninitialisationtimeout)
- [Forbidden](common.CloseCode.md#forbidden)
- [InternalServerError](common.CloseCode.md#internalservererror)
Expand All @@ -27,6 +28,12 @@

___

### ConnectionAcknowledgementTimeout

**ConnectionAcknowledgementTimeout** = `4418`

___

### ConnectionInitialisationTimeout

**ConnectionInitialisationTimeout** = `4408`
Expand Down
19 changes: 19 additions & 0 deletions docs/interfaces/client.ClientOptions.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Configuration used for the GraphQL over WebSocket client.

### Properties

- [connectionAckWaitTimeout](client.ClientOptions.md#connectionackwaittimeout)
- [connectionParams](client.ClientOptions.md#connectionparams)
- [disablePong](client.ClientOptions.md#disablepong)
- [jsonMessageReplacer](client.ClientOptions.md#jsonmessagereplacer)
Expand All @@ -31,6 +32,24 @@ Configuration used for the GraphQL over WebSocket client.

## Properties

### connectionAckWaitTimeout

`Optional` **connectionAckWaitTimeout**: `number`

The amount of time for which the client will wait
for `ConnectionAck` message.

Set the value to `Infinity`, `''`, `0`, `null` or `undefined` to skip waiting.

If the wait timeout has passed and the server
has not responded with `ConnectionAck` message,
the client will terminate the socket by
dispatching a close event `4418: Connection acknowledgement timeout`

**`default`** 0

___

### connectionParams

`Optional` **connectionParams**: `Record`<`string`, `unknown`\> \| () => `undefined` \| `Record`<`string`, `unknown`\> \| `Promise`<`undefined` \| `Record`<`string`, `unknown`\>\>
Expand Down
34 changes: 34 additions & 0 deletions src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,20 @@ export interface ClientOptions {
* @default 0
*/
keepAlive?: number;
/**
* The amount of time for which the client will wait
* for `ConnectionAck` message.
*
* Set the value to `Infinity`, `''`, `0`, `null` or `undefined` to skip waiting.
*
* If the wait timeout has passed and the server
* has not responded with `ConnectionAck` message,
* the client will terminate the socket by
* dispatching a close event `4418: Connection acknowledgement timeout`
*
* @default 0
*/
connectionAckWaitTimeout?: number;
/**
* Disable sending the `PongMessage` automatically.
*
Expand Down Expand Up @@ -423,6 +437,7 @@ export function createClient(options: ClientOptions): Client {
lazyCloseTimeout = 0,
keepAlive = 0,
disablePong,
connectionAckWaitTimeout = 0,
retryAttempts = 5,
retryWait = async function randomisedExponentialBackoff(retries) {
let retryDelay = 1000; // start with 1s delay
Expand Down Expand Up @@ -575,6 +590,21 @@ export function createClient(options: ClientOptions): Client {
}
}

let connectionAckTimeout: ReturnType<typeof setTimeout>;
function startConnectionAckTimeout() {
if (
isFinite(connectionAckWaitTimeout) &&
connectionAckWaitTimeout > 0
) {
connectionAckTimeout = setTimeout(() => {
socket.close(
CloseCode.ConnectionAcknowledgementTimeout,
'Connection acknowledgement timeout',
);
}, connectionAckWaitTimeout);
}
}

socket.onerror = (err) => {
// we let the onclose reject the promise for correct retry handling
emitter.emit('error', err);
Expand All @@ -583,6 +613,7 @@ export function createClient(options: ClientOptions): Client {
socket.onclose = (event) => {
connecting = undefined;
clearTimeout(queuedPing);
clearTimeout(connectionAckTimeout);
emitter.emit('closed', event);
denied(event);
};
Expand All @@ -608,6 +639,7 @@ export function createClient(options: ClientOptions): Client {
replacer,
),
);
startConnectionAckTimeout();
enqueuePing(); // enqueue ping (noop if disabled)
} catch (err) {
socket.close(
Expand Down Expand Up @@ -651,6 +683,7 @@ export function createClient(options: ClientOptions): Client {
throw new Error(
`First message cannot be of type ${message.type}`,
);
clearTimeout(connectionAckTimeout);
acknowledged = true;
emitter.emit('connected', socket, message.payload); // connected = socket opened + acknowledged
retrying = false; // future lazy connects are not retries
Expand Down Expand Up @@ -723,6 +756,7 @@ export function createClient(options: ClientOptions): Client {
// CloseCode.Forbidden, might grant access out after retry
CloseCode.SubprotocolNotAcceptable,
// CloseCode.ConnectionInitialisationTimeout, might not time out after retry
// CloseCode.ConnectionAcknowledgementTimeout, might not time out after retry
CloseCode.SubscriberAlreadyExists,
CloseCode.TooManyInitialisationRequests,
].includes(errOrCloseEvent.code))
Expand Down
1 change: 1 addition & 0 deletions src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export enum CloseCode {
Forbidden = 4403,
SubprotocolNotAcceptable = 4406,
ConnectionInitialisationTimeout = 4408,
ConnectionAcknowledgementTimeout = 4418,
/** Subscriber distinction is very important */
SubscriberAlreadyExists = 4409,
TooManyInitialisationRequests = 4429,
Expand Down

0 comments on commit 47b9830

Please sign in to comment.