Skip to content

Commit

Permalink
fix(client): Use TerminatedCloseEvent class extending an Error fo…
Browse files Browse the repository at this point in the history
…r rejecting promises when terminating

Closes #531
  • Loading branch information
enisdenjo committed Feb 12, 2024
1 parent f76bb54 commit 74b4ceb
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 13 deletions.
20 changes: 16 additions & 4 deletions src/__tests__/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@

import WebSocket from 'ws';
import { EventEmitter } from 'events';
import { createClient, Client, EventListener } from '../client';
import {
createClient,
Client,
EventListener,
TerminatedCloseEvent,
} from '../client';
import {
CloseCode,
MessageType,
Expand Down Expand Up @@ -815,9 +820,16 @@ it('should terminate socket immediately on terminate', async (done) => {
on: {
closed: (event) => {
expect(event).not.toBeInstanceOf(CloseEvent); // because its an artificial close-event-like object
expect((event as any).code).toBe(4499);
expect((event as any).reason).toBe('Terminated');
expect((event as any).wasClean).toBeFalsy();
expect(event).toBeInstanceOf(TerminatedCloseEvent);
expect((event as TerminatedCloseEvent).name).toBe(
'TerminatedCloseEvent',
);
expect((event as TerminatedCloseEvent).message).toBe(
'4499: Terminated',
);
expect((event as TerminatedCloseEvent).code).toBe(4499);
expect((event as TerminatedCloseEvent).reason).toBe('Terminated');
expect((event as TerminatedCloseEvent).wasClean).toBeFalsy();
done();
},
},
Expand Down
31 changes: 22 additions & 9 deletions src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -451,9 +451,9 @@ export interface Client extends Disposable {
/**
* Terminates the WebSocket abruptly and immediately.
*
* A close event `4499: Terminated` is issued to the current WebSocket and an
* artificial `{ code: 4499, reason: 'Terminated', wasClean: false }` close-event-like
* object is immediately emitted without waiting for the one coming from `WebSocket.onclose`.
* A close event `4499: Terminated` is issued to the current WebSocket and a
* syntetic {@link TerminatedCloseEvent} is immediately emitted without waiting for
* the one coming from `WebSocket.onclose`.
*
* Terminating is not considered fatal and a connection retry will occur as expected.
*
Expand Down Expand Up @@ -664,7 +664,7 @@ export function createClient<
clearTimeout(queuedPing);
denied(errOrEvent);

if (isLikeCloseEvent(errOrEvent) && errOrEvent.code === 4499) {
if (errOrEvent instanceof TerminatedCloseEvent) {
socket.close(4499, 'Terminated'); // close event is artificial and emitted manually, see `Client.terminate()` below
socket.onerror = null;
socket.onclose = null;
Expand Down Expand Up @@ -1061,16 +1061,29 @@ export function createClient<
terminate() {
if (connecting) {
// only if there is a connection
emitter.emit('closed', {
code: 4499,
reason: 'Terminated',
wasClean: false,
});
emitter.emit('closed', new TerminatedCloseEvent());
}
},
};
}

/**
* A syntetic close event `4499: Terminated` is issued to the current to immediately
* close the connection without waiting for the one coming from `WebSocket.onclose`.
*
* Terminating is not considered fatal and a connection retry will occur as expected.
*
* Useful in cases where the WebSocket is stuck and not emitting any events;
* can happen on iOS Safari, see: https://github.com/enisdenjo/graphql-ws/discussions/290.
*/
export class TerminatedCloseEvent extends Error {
public name = 'TerminatedCloseEvent';
public message = '4499: Terminated';
public code = 4499;
public reason = 'Terminated';
public wasClean = false;
}

/** Minimal close event interface required by the lib for error and socket close handling. */
interface LikeCloseEvent {
/** Returns the WebSocket connection close code provided by the server. */
Expand Down

0 comments on commit 74b4ceb

Please sign in to comment.