diff --git a/packages/connection/index.js b/packages/connection/index.js index 77f53d2e..3f631860 100644 --- a/packages/connection/index.js +++ b/packages/connection/index.js @@ -252,6 +252,21 @@ class Connection extends EventEmitter { await promise(this.socket, "close", "error", timeout); } + /** + * Forcibly disconnects the socket + * https://xmpp.org/rfcs/rfc6120.html#streams-close + * https://tools.ietf.org/html/rfc7395#section-3.6 + */ + async forceDisconnect(timeout = this.timeout) { + if (!this.socket) return; + + this._status("disconnecting"); + this.socket.destroy(); + + // The 'disconnect' status is set by the socket 'close' listener + await promise(this.socket, "close", "error", timeout); + } + /** * Opens the stream */ diff --git a/packages/stream-management/index.js b/packages/stream-management/index.js index ace127af..0cc7fa8b 100644 --- a/packages/stream-management/index.js +++ b/packages/stream-management/index.js @@ -170,7 +170,7 @@ export default function streamManagement({ clearTimeout(timeoutTimeout); if (sm.timeout) { timeoutTimeout = setTimeout( - () => entity.disconnect().catch(), + () => entity.forceDisconnect().catch(), sm.timeout, ); } diff --git a/packages/tls/lib/Socket.js b/packages/tls/lib/Socket.js index e0bbfa27..9b60506c 100644 --- a/packages/tls/lib/Socket.js +++ b/packages/tls/lib/Socket.js @@ -63,6 +63,10 @@ class Socket extends EventEmitter { this.socket.end(); } + destroy() { + this.socket.destroy(); + } + write(data, fn) { this.socket.write(data, fn); } diff --git a/packages/websocket/lib/Socket.js b/packages/websocket/lib/Socket.js index 3227a11b..d48e5fa4 100644 --- a/packages/websocket/lib/Socket.js +++ b/packages/websocket/lib/Socket.js @@ -75,6 +75,10 @@ export default class Socket extends EventEmitter { this.socket.close(); } + destroy() { + this.socket.close(); + } + write(data, fn) { if (WebSocket === WS) { this.socket.send(data, fn); diff --git a/test/stream-management.js b/test/stream-management.js index 926bbc4d..5d8aacca 100644 --- a/test/stream-management.js +++ b/test/stream-management.js @@ -78,3 +78,25 @@ test("client retry stanzas", async () => { const el = await elP; expect(el.attrs.id).toEqual("ping"); }); + +test("client reconnect automatically", async () => { + await server.enableModules(["smacks"]); + await server.restart(); + + xmpp = client({ credentials, service: domain }); + xmpp.streamManagement.timeout = 10; + xmpp.streamManagement.debounceAckRequest = 1; + debug(xmpp); + + const resumedP = promise(xmpp.streamManagement, "resumed"); + await xmpp.start(); + await xmpp.send( + + + , + ); + xmpp.socket.socket.pause(); + + await resumedP; + expect().pass(); +});