'Error: WebSocket is not open' when closing two connected ws clients simultaneously
See original GitHub issueHi 👋 first of all, thanks for providing this awesome package - I am having great time using it. Unfortunately I ran into an issue, that I believe is not on my side (however I am happy to be proven wrong, or pointed to a valid solution).
Describe the bug
When closing connections of two clients that are connected and notified of each other state via a common channel (e.g. a Subject), an Error: WebSocket is not open: readyState 2 (CLOSING)
is thrown. Happens only when clients are closed immediately after another (e.g. via a script).
Full stack trace:
Error: WebSocket is not open: readyState 2 (CLOSING)
at WebSocket.send (/*path to the project*//node_modules/@marblejs/websockets/node_modules/ws/lib/websocket.js:322:19)
at WebSocket.sendResponse (/*path to the project*//node_modules/@marblejs/websockets/dist/response/websocket.response.handler.js:7:12)
at SafeSubscriber.input$.pipe.subscribe.type [as _next] (/*path to the project*//node_modules/@marblejs/websockets/dist/server/websocket.server.listener.js:64:42)
at SafeSubscriber.__tryOrUnsub (/*path to the project*//node_modules/rxjs/src/internal/Subscriber.ts:265:10)
at SafeSubscriber.next (/*path to the project*//node_modules/rxjs/src/internal/Subscriber.ts:207:14)
at Subscriber._next (/*path to the project*//node_modules/rxjs/src/internal/Subscriber.ts:139:22)
at Subscriber.next (/*path to the project*//node_modules/rxjs/src/internal/Subscriber.ts:99:12)
at TakeUntilSubscriber.Subscriber._next (/*path to the project*//node_modules/rxjs/src/internal/Subscriber.ts:139:22)
at TakeUntilSubscriber.Subscriber.next (/*path to the project*//node_modules/rxjs/src/internal/Subscriber.ts:99:12)
at CatchSubscriber.Subscriber._next (/*path to the project*//node_modules/rxjs/src/internal/Subscriber.ts:139:22)
To Reproduce
- Link two clients using a common channel.
- Using the
finalize
operator notify each user about another user being disconnected - Open the connections
- Close the connections directly one after another
(5.) First client that happens to be disconnected causes a message to be pushed to the second client, that also disconnected in the meantime.
(6.) Message is being pushed, even though the websocket is in
CLOSING
state
Minimal epic causing the error:
class UserPoolPubSub {
private readonly pubSub = new Subject<Message>();
public join(id: string): Observable<Message> {
this.publish(id, 'Hello there!');
return this.pubSub.pipe(filter(({ publisher }) => publisher !== id));
}
public publish(publisher: string, message: string): void {
this.pubSub.next({ publisher, message });
}
}
const pool = new UserPoolPubSub();
const join$: WsEffect = (event$, { client: { id } }) => {
const leave$ = event$.pipe(matchEvent('LEAVE'));
return event$.pipe(
matchEvent('JOIN'),
exhaustMap(() =>
pool.join(id).pipe(
map(({ message }) => ({ type: 'RECEIVED', payload: message })),
takeUntil(leave$),
),
),
finalize(() => pool.publish(id, 'I was disconnected, sorry')),
);
};
Minimal client interaction:
function run() {
// after opening the ws connections and exchanging messages
clientA.close();
clientB.close();
}
I published a runnable reproduction here: https://github.com/GrzegorzKazana/marblejs-ws-issue-repro. It can be run by
npm i
npm run dev
# in separate terminal
npm run client
Expected behavior Server should not try to push the event to user that is already closing.
Desktop (please complete the following information):
OS: MacOS 10.14.6
@marblejs/core: 3.4.8
@marblejs/websockets: 3.4.8
Node version: 12.18.0, also happens on 14.15.1
Additional context
I run into this issue when doing integration tests for my app, that uses @marblejs/websocket
for its backend. When the test runner was halting on a failed test, it caused closing all connections (2 in my case) at the same time - this in turned was causing crash of my backend service.
I hope I did not miss any obvious error on my side, but I will be happy to be corrected or pointed to a mistake.
Thanks in advance!
Issue Analytics
- State:
- Created 3 years ago
- Reactions:1
- Comments:8 (5 by maintainers)
Top GitHub Comments
Version
3.4.9
has been released 🚀That’s a really accurate remark - I completely overlooked the fact that
sendResponse
is exposed to the user (via theclient
). Guess we can not change the public api too much then - so the option 3. does in fact seem to be the optimal way of going about it 🚀